Fixed incorrect usage of result (now object rather than scalar), thanks Michal Zygmun...
[pwlib.git] / src / ptclib / httpsvc.cxx
blob031cf7e7d1a75ac26e10e2202692c38047af3219
1 /*
2 * httpsvc.cxx
4 * Classes for service applications using HTTP as the user interface.
6 * Portable Windows Library
8 * Copyright (c) 1993-2002 Equivalence Pty. Ltd.
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
18 * under the License.
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
26 * $Log$
27 * Revision 1.95 2004/04/03 08:22:20 csoutheren
28 * Remove pseudo-RTTI and replaced with real RTTI
30 * Revision 1.94 2004/04/03 06:54:24 rjongbloed
31 * Many and various changes to support new Visual C++ 2003
33 * Revision 1.93 2004/03/23 04:41:05 csoutheren
34 * Fixed compile problem on Linux
36 * Revision 1.92 2004/03/23 03:40:57 csoutheren
37 * Change service process default to be more useful in some environments
39 * Revision 1.91 2004/01/17 17:44:54 csoutheren
40 * Changed to use PString::MakeEmpty
42 * Revision 1.90 2003/09/17 09:02:13 csoutheren
43 * Removed memory leak detection code
45 * Revision 1.89 2003/02/19 07:23:45 robertj
46 * Changes to allow for single threaded HTTP service processes.
48 * Revision 1.88 2002/11/06 22:47:25 robertj
49 * Fixed header comment (copyright etc)
51 * Revision 1.87 2002/10/10 04:43:44 robertj
52 * VxWorks port, thanks Martijn Roest
54 * Revision 1.86 2002/08/14 00:43:40 robertj
55 * Added ability to have fixed maximum length PStringStream's so does not do
56 * unwanted malloc()'s while outputing data.
58 * Revision 1.85 2002/08/13 05:39:17 robertj
59 * Fixed GNU compatibility
61 * Revision 1.84 2002/08/13 01:57:15 robertj
62 * Fixed "last dump object" position in Memory Dump macro.
64 * Revision 1.83 2002/08/13 01:30:27 robertj
65 * Added UpTime macro for time service has been running.
66 * Added IfQuery macro blcok to add chunks of HTML depending on the value
67 * of query parameters in the URL.
68 * Added memory statistics dump and memory object dump macros to help in
69 * leak finding.
71 * Revision 1.82 2002/07/30 08:37:34 robertj
72 * Removed peer host as bad DNS makes it useless due to huge timeout.
74 * Revision 1.81 2002/07/30 04:51:26 craigs
75 * Added MonitorInfo macro
77 * Revision 1.80 2002/07/30 03:16:57 craigs
78 * Added StartTime macro
80 * Revision 1.79 2002/07/17 09:18:00 robertj
81 * made detection of gif file more intelligent for debug version.
83 * Revision 1.78 2002/07/17 08:03:45 robertj
84 * Allowed for adjustable copyright holder.
85 * Allowed for not having gif file for product name in default header.
87 * Revision 1.77 2001/10/10 08:06:49 robertj
88 * Fixed problem with not shutting down threads when closing listener.
90 * Revision 1.76 2001/09/11 02:37:41 robertj
91 * Fixed thread name for HTTP service connection handler.
93 * Revision 1.75 2001/08/28 06:44:45 craigs
94 * Added ability to override PHTTPServer creation
96 * Revision 1.74 2001/06/30 06:59:06 yurik
97 * Jac Goudsmit from Be submit these changes 6/28. Implemented by Yuri Kiryanov
99 * Revision 1.73 2001/06/27 04:14:48 robertj
100 * Added logging for listener thread open/close.
102 * Revision 1.72 2001/06/23 00:32:15 robertj
103 * Added parameter to be able to set REUSEADDR on listener socket.
105 * Revision 1.71 2001/05/07 23:27:06 robertj
106 * Added SO_LINGER setting to HTTP sockets to help with clearing up sockets
107 * when the application exits, which prevents new run of app as "port in use".
109 * Revision 1.70 2001/03/26 04:55:26 robertj
110 * Made sure OnConfigChanged() is called from OnStart() function.
112 * Revision 1.69 2001/03/21 06:29:31 robertj
113 * Fixed bug in calling OnConfigChanged after service macros are loaded,
114 * should be before so state can be changed before the macros translated.
116 * Revision 1.68 2001/03/19 02:41:53 robertj
117 * Made sure HTTP listener thread is shut down in OnStop().
119 * Revision 1.67 2001/03/16 03:33:21 robertj
120 * Fixed HTML signature code due to changes in encryption code.
122 * Revision 1.66 2001/03/04 02:24:44 robertj
123 * Removed default OnControl() from http service as cannot get port number.
125 * Revision 1.65 2001/02/21 04:33:46 robertj
126 * Fixed GNU warning.
128 * Revision 1.64 2001/02/20 02:32:41 robertj
129 * Added PServiceMacro version that can do substitutions on blocks of HTML.
131 * Revision 1.63 2001/02/15 01:12:15 robertj
132 * Moved some often repeated HTTP service code into PHTTPServiceProcess.
134 * Revision 1.62 2001/02/14 06:52:26 robertj
135 * Fixed GNU compatibility with last change to PServiceMacro.
137 * Revision 1.61 2001/02/14 02:30:59 robertj
138 * Moved HTTP Service Macro facility to public API so can be used by apps.
139 * Added ability to specify the service macro keyword, defaults to "macro".
140 * Added machine macro to get the OS version and hardware.
142 * Revision 1.60 2001/01/15 06:17:56 robertj
143 * Set HTTP resource members to private to assure are not modified by
144 * dscendents in non-threadsafe manner.
146 * Revision 1.59 2001/01/08 22:53:34 craigs
147 * Changed OnPOST to allow subtle usage of embedded commands
149 * Revision 1.58 2000/12/14 08:09:41 robertj
150 * Fixed missing immediate expiry date on string and file service HTTP resourcer.
152 * Revision 1.57 2000/12/11 13:15:17 robertj
153 * Added macro to include signed or unsigned chunks of HTML.
154 * Added flag to globally ignore HTML signatures (useful for develeopment).
156 * Revision 1.56 2000/10/23 09:17:26 robertj
157 * Fixed bug un Linux version where HTML macros didn't work correctly.
159 * Revision 1.55 2000/08/04 12:48:25 robertj
160 * Added mechanism by which a service can get at new HTTP connections, eg to add SSL.
162 * Revision 1.54 2000/05/02 02:58:49 robertj
163 * Fixed MSVC warning about unused parameters.
165 * Revision 1.53 2000/05/02 02:01:18 craigs
166 * Changed stricmp and added implementation of PServiceMacro::Translate
168 * Revision 1.52 2000/05/02 01:50:37 robertj
169 * Rewrite of PServiceMacro so does not use malloc (indirectly).
171 * Revision 1.51 2000/01/27 00:35:52 robertj
172 * Fixed benign warning about uninitialised variables in MSVC optimised compile.
174 * Revision 1.50 1999/08/07 06:50:52 robertj
175 * Removed silly (and incorrect) warning.
177 * Revision 1.49 1999/04/24 05:16:26 robertj
178 * Fixed incorrect date in copyright notice.
180 * Revision 1.48 1998/11/30 05:37:46 robertj
181 * New directory structure
183 * Revision 1.47 1998/11/24 23:05:14 robertj
184 * Fixed extra *** in demo message
186 * Revision 1.46 1998/11/16 07:23:15 robertj
187 * More PPC GNU compatibility.
189 * Revision 1.45 1998/11/16 06:50:40 robertj
190 * Fixed PPC GNU compiler compatibility.
192 * Revision 1.44 1998/10/31 12:49:25 robertj
193 * Added read/write mutex to the HTTP space variable to avoid thread crashes.
195 * Revision 1.43 1998/10/29 11:58:52 robertj
196 * Added ability to configure the HTTP threads stack size.
198 * Revision 1.42 1998/10/29 11:31:57 robertj
199 * Fixed default URL to have lower case and spaceless product name.
200 * Increased HTTP stack size.
202 * Revision 1.41 1998/10/15 01:53:35 robertj
203 * GNU compatibility.
205 * Revision 1.40 1998/10/13 14:06:24 robertj
206 * Complete rewrite of memory leak detection code.
208 * Revision 1.39 1998/09/23 06:22:15 robertj
209 * Added open source copyright license.
211 * Revision 1.38 1998/09/18 01:47:23 robertj
212 * Fixed bug that made files with signature on first line fail on unix systems.
214 * Revision 1.37 1998/08/20 06:01:02 robertj
215 * Improved internationalisation, registrationpage override.
217 * Revision 1.36 1998/04/21 02:43:40 robertj
218 * Fixed conditional around wrong way for requiring signature on HTML files.
220 * Revision 1.35 1998/04/01 01:55:41 robertj
221 * Fixed bug for automatically including GIF file in HTTP name space.
223 * Revision 1.34 1998/03/23 03:21:40 robertj
224 * Fixed missing invalid case in register page.
226 * Revision 1.33 1998/03/20 03:18:15 robertj
227 * Added special classes for specific sepahores, PMutex and PSyncPoint.
229 * Revision 1.32 1998/03/17 10:14:39 robertj
230 * Rewrite of registration page to allow for HTML file override.
232 * Revision 1.31 1998/03/09 07:17:48 robertj
233 * Added IP peer/local number macros.
234 * Set GetPageGraphic reference to GIF file to be at lop level directory.
236 * Revision 1.30 1998/02/16 00:14:09 robertj
237 * Added ProductName and BuildDate macros.
238 * Major rewrite of application info passed in PHTTPServiceProcess constructor.
240 * Revision 1.29 1998/02/03 06:22:45 robertj
241 * Allowed PHTTPServiceString to be overridden by html file after ';'.
243 * Revision 1.28 1998/01/26 02:49:19 robertj
244 * GNU support.
246 * Revision 1.27 1998/01/26 02:12:14 robertj
247 * GNU warnings.
249 * Revision 1.26 1998/01/26 00:45:44 robertj
250 * Added option flags to ProcessMacros to automatically load from file etc.
251 * Assured that all service HTTP resources are overidable with file, using ; URL field.
252 * Added a number of extra #equival macros.
253 * Added "Pty. Ltd." to company name.
255 * Revision 1.25 1997/11/10 12:40:05 robertj
256 * Changed SustituteEquivalSequence so can override standard macros.
258 * Revision 1.24 1997/11/04 06:02:46 robertj
259 * Allowed help gif file name to overridable in PServiceHTML, so can be in subdirectory.
261 * Revision 1.23 1997/10/30 10:21:26 robertj
262 * Added ability to customise regisration text by application.
264 * Revision 1.22 1997/08/28 14:19:40 robertj
265 * Fixed bug where HTTP directory was not processed for macros.
267 * Revision 1.20 1997/08/20 08:59:58 craigs
268 * Changed macro handling to commonise #equival sequence
270 * Revision 1.19 1997/07/26 11:38:22 robertj
271 * Support for overridable pages in HTTP service applications.
273 * Revision 1.18 1997/07/08 13:11:44 robertj
274 * Added standard header and copyright macros to service HTML.
276 * Revision 1.17 1997/06/16 13:20:15 robertj
277 * Fixed bug where PHTTPThread crashes on exit.
279 * Revision 1.16 1997/05/16 12:07:21 robertj
280 * Added operating system and version to hidden fields on registration form.
282 * Revision 1.15 1997/03/02 03:40:59 robertj
283 * Added error logging to standard HTTP Service HTTP Server.
285 * Revision 1.14 1997/02/05 11:54:54 robertj
286 * Added support for order form page overridiing.
288 * Revision 1.13 1997/01/28 11:45:19 robertj
291 * Revision 1.13 1997/01/27 10:22:37 robertj
292 * Numerous changes to support OEM versions of products.
294 * Revision 1.12 1997/01/03 06:33:23 robertj
295 * Removed slash from operating system version string, so says Windows NT rather than Windows/NT
297 * Revision 1.11 1996/11/16 10:50:26 robertj
298 * ??
300 * Revision 1.10 1996/11/04 03:58:23 robertj
301 * Changed to accept separate copyright and manufacturer strings.
303 * Revision 1.8 1996/10/08 13:08:29 robertj
304 * Changed standard graphic to use PHTML class.
306 * Revision 1.7 1996/09/14 13:09:33 robertj
307 * Major upgrade:
308 * rearranged sockets to help support IPX.
309 * added indirect channel class and moved all protocols to descend from it,
310 * separating the protocol from the low level byte transport.
312 * Revision 1.6 1996/08/25 09:39:00 robertj
313 * Prevented registration if no user etc entered.
315 * Revision 1.5 1996/08/19 13:39:55 robertj
316 * Fixed race condition in system restart logic.
318 * Revision 1.4 1996/08/08 13:36:39 robertj
319 * Fixed Registation page so no longer has static link, ie can be DLLed.
321 * Revision 1.3 1996/07/15 10:36:48 robertj
322 * Added registration info to bottom of order form so can be faxed to us.
324 * Revision 1.2 1996/06/28 13:21:30 robertj
325 * Fixed nesting problem in tables.
326 * Fixed PConfig page always restarting.
328 * Revision 1.1 1996/06/13 13:33:34 robertj
329 * Initial revision
333 #ifdef __GNUC__
334 #pragma implementation "httpsvc.h"
335 #endif
337 #include <ptlib.h>
338 #include <ptclib/httpsvc.h>
339 #include <ptlib/sockets.h>
342 PSORTED_LIST(PServiceMacros_base, PServiceMacro);
344 class PServiceMacros_list : public PServiceMacros_base
346 public:
347 PServiceMacros_list();
351 #define new PNEW
354 #define HOME_PAGE "http://www.equival.com"
355 #define EMAIL "equival@equival.com.au"
356 #define EQUIVALENCE "Equivalence Pty. Ltd."
359 static const PTime ImmediateExpiryTime(0, 0, 0, 1, 1, 1980);
362 ///////////////////////////////////////////////////////////////////////////////
364 PHTTPServiceProcess::PHTTPServiceProcess(const Info & inf)
365 : PServiceProcess(inf.manufacturerName, inf.productName,
366 inf.majorVersion, inf.minorVersion, inf.buildStatus, inf.buildNumber),
367 macroKeyword("macro"),
368 productKey(inf.productKey),
369 securedKeys(inf.securedKeyCount, inf.securedKeys),
370 signatureKey(inf.signatureKey),
371 compilationDate(inf.compilationDate),
372 manufacturersHomePage(inf.manufHomePage != NULL ? inf.manufHomePage : HOME_PAGE),
373 manufacturersEmail(inf.email != NULL ? inf.email : EMAIL),
374 productNameHTML(inf.productHTML != NULL ? inf.productHTML : inf.productName),
375 gifHTML(inf.gifHTML),
376 copyrightHolder(inf.copyrightHolder != NULL ? inf.copyrightHolder : inf.manufacturerName),
377 copyrightHomePage(inf.copyrightHomePage != NULL ? inf.copyrightHomePage : (const char *)manufacturersHomePage),
378 copyrightEmail(inf.copyrightEmail != NULL ? inf.copyrightEmail : (const char *)manufacturersEmail)
380 ignoreSignatures = FALSE;
382 if (inf.gifFilename != NULL) {
383 PDirectory exeDir = GetFile().GetDirectory();
384 #if defined(_WIN32) && defined(_DEBUG)
385 // Special check to aid in using DevStudio for debugging.
386 if (exeDir.Find("\\Debug\\") != P_MAX_INDEX)
387 exeDir = exeDir.GetParent();
388 #endif
389 httpNameSpace.AddResource(new PServiceHTTPFile(inf.gifFilename, exeDir+inf.gifFilename));
390 if (gifHTML.IsEmpty()) {
391 gifHTML = psprintf("<img src=\"/%s\" alt=\"%s!\"", inf.gifFilename, inf.productName);
392 if (inf.gifWidth != 0 && inf.gifHeight != 0)
393 gifHTML += psprintf(" width=%i height=%i", inf.gifWidth, inf.gifHeight);
394 gifHTML += " align=absmiddle>";
398 restartThread = NULL;
399 httpListeningSocket = NULL;
400 httpThreads.DisallowDeleteObjects();
404 PHTTPServiceProcess::~PHTTPServiceProcess()
406 ShutdownListener();
410 PHTTPServiceProcess & PHTTPServiceProcess::Current()
412 PHTTPServiceProcess & process = (PHTTPServiceProcess &)PProcess::Current();
413 PAssert(PIsDescendant(&process, PHTTPServiceProcess), "Not a HTTP service!");
414 return process;
418 BOOL PHTTPServiceProcess::OnStart()
420 if (!Initialise("Started"))
421 return FALSE;
423 OnConfigChanged();
424 return TRUE;
428 void PHTTPServiceProcess::OnStop()
430 ShutdownListener();
431 PSYSTEMLOG(Warning, GetName() << " stopped.");
432 PServiceProcess::OnStop();
436 BOOL PHTTPServiceProcess::OnPause()
438 OnConfigChanged();
439 return TRUE;
443 void PHTTPServiceProcess::OnContinue()
445 if (Initialise("Restarted"))
446 return;
448 OnStop();
449 Terminate();
453 #ifdef _WIN32
454 const char * PHTTPServiceProcess::GetServiceDependencies() const
456 return "EventLog\0Tcpip\0";
458 #endif
461 BOOL PHTTPServiceProcess::ListenForHTTP(WORD port,
462 PSocket::Reusability reuse,
463 PINDEX stackSize)
465 if (httpListeningSocket != NULL &&
466 httpListeningSocket->GetPort() == port &&
467 httpListeningSocket->IsOpen())
468 return TRUE;
470 return ListenForHTTP(new PTCPSocket(port), reuse, stackSize);
474 BOOL PHTTPServiceProcess::ListenForHTTP(PSocket * listener,
475 PSocket::Reusability reuse,
476 PINDEX stackSize)
478 if (httpListeningSocket != NULL)
479 ShutdownListener();
481 httpListeningSocket = PAssertNULL(listener);
482 if (!httpListeningSocket->Listen(5, 0, reuse)) {
483 PSYSTEMLOG(Debug, "HTTPSVC\tListen on port " << httpListeningSocket->GetPort()
484 << " failed: " << httpListeningSocket->GetErrorText());
485 return FALSE;
488 if (stackSize > 1000)
489 new PHTTPServiceThread(stackSize, *this);
491 return TRUE;
495 void PHTTPServiceProcess::ShutdownListener()
497 if (httpListeningSocket == NULL)
498 return;
500 if (!httpListeningSocket->IsOpen())
501 return;
503 PSYSTEMLOG(Debug, "HTTPSVC\tClosing listener socket on port "
504 << httpListeningSocket->GetPort());
506 httpListeningSocket->Close();
508 httpThreadsMutex.Wait();
509 for (PINDEX i = 0; i < httpThreads.GetSize(); i++)
510 httpThreads[i].Close();
512 while (httpThreads.GetSize() > 0) {
513 httpThreadsMutex.Signal();
514 Sleep(1);
515 httpThreadsMutex.Wait();
518 httpThreadsMutex.Signal();
520 delete httpListeningSocket;
521 httpListeningSocket = NULL;
525 PString PHTTPServiceProcess::GetCopyrightText()
527 PHTML html(PHTML::InBody);
528 html << "Copyright &copy;"
529 << compilationDate.AsString("yyyy") << " by "
530 << PHTML::HotLink(copyrightHomePage)
531 << copyrightHolder
532 << PHTML::HotLink()
533 << ", "
534 << PHTML::HotLink("mailto:" + copyrightEmail)
535 << copyrightEmail
536 << PHTML::HotLink();
537 return html;
541 PString PHTTPServiceProcess::GetPageGraphic()
543 PFile header;
544 if (header.Open("header.html", PFile::ReadOnly))
545 return header.ReadString(header.GetLength());
547 PHTML html(PHTML::InBody);
548 html << PHTML::TableStart()
549 << PHTML::TableRow()
550 << PHTML::TableData();
552 if (gifHTML.IsEmpty())
553 html << PHTML::Heading(1) << productNameHTML << "&nbsp;" << PHTML::Heading(1);
554 else
555 html << gifHTML;
557 html << PHTML::TableData()
558 << GetOSClass() << ' ' << GetOSName()
559 << " Version " << GetVersion(TRUE) << PHTML::BreakLine()
560 << ' ' << GetCompilationDate().AsString("d MMMM yyyy")
561 << PHTML::BreakLine()
562 << "By "
563 << PHTML::HotLink(manufacturersHomePage) << GetManufacturer() << PHTML::HotLink()
564 << ", "
565 << PHTML::HotLink("mailto:" + manufacturersEmail) << manufacturersEmail << PHTML::HotLink()
566 << PHTML::TableEnd()
567 << PHTML::HRule();
569 return html;
573 void PHTTPServiceProcess::GetPageHeader(PHTML & html)
575 GetPageHeader(html, GetName());
579 void PHTTPServiceProcess::GetPageHeader(PHTML & html, const PString & title)
581 html << PHTML::Title(title)
582 << PHTML::Body()
583 << GetPageGraphic();
587 PTCPSocket * PHTTPServiceProcess::AcceptHTTP()
589 if (httpListeningSocket == NULL)
590 return NULL;
592 if (!httpListeningSocket->IsOpen())
593 return NULL;
595 // get a socket when a client connects
596 PTCPSocket * socket = new PTCPSocket;
597 if (socket->Accept(*httpListeningSocket))
598 return socket;
600 if (socket->GetErrorCode() != PChannel::Interrupted)
601 PSYSTEMLOG(Error, "Accept failed for HTTP: " << socket->GetErrorText());
603 if (httpListeningSocket != NULL && httpListeningSocket->IsOpen())
604 return socket;
606 delete socket;
607 return NULL;
611 BOOL PHTTPServiceProcess::ProcessHTTP(PTCPSocket & socket)
613 if (!socket.IsOpen())
614 return TRUE;
616 PHTTPServer * server = CreateHTTPServer(socket);
617 if (server == NULL) {
618 PSYSTEMLOG(Error, "HTTP server creation/open failed.");
619 return TRUE;
622 // process requests
623 while (server->ProcessCommand())
626 // always close after the response has been sent
627 delete server;
629 // if a restart was requested, then do it, but only if we are not shutting down
630 if (httpListeningSocket->IsOpen())
631 CompleteRestartSystem();
633 return TRUE;
637 void PHTTPServiceProcess::BeginRestartSystem()
639 if (restartThread == NULL) {
640 restartThread = PThread::Current();
641 OnConfigChanged();
646 void PHTTPServiceProcess::CompleteRestartSystem()
648 if (restartThread == NULL)
649 return;
651 if (restartThread != PThread::Current())
652 return;
654 httpNameSpace.StartWrite();
656 if (Initialise("Restart\tInitialisation"))
657 restartThread = NULL;
659 httpNameSpace.EndWrite();
661 if (restartThread != NULL)
662 Terminate();
666 void PHTTPServiceProcess::AddRegisteredText(PHTML &)
671 void PHTTPServiceProcess::AddUnregisteredText(PHTML &)
676 BOOL PHTTPServiceProcess::SubstituteEquivalSequence(PHTTPRequest &, const PString &, PString &)
678 return FALSE;
682 PHTTPServer * PHTTPServiceProcess::CreateHTTPServer(PTCPSocket & socket)
684 #ifdef SO_LINGER
685 const linger ling = { 1, 5 };
686 socket.SetOption(SO_LINGER, &ling, sizeof(ling));
687 #endif
689 PHTTPServer * server = OnCreateHTTPServer(httpNameSpace);
691 if (server->Open(socket))
692 return server;
694 delete server;
695 return NULL;
699 PHTTPServer * PHTTPServiceProcess::OnCreateHTTPServer(const PHTTPSpace & httpNameSpace)
701 return new PHTTPServer(httpNameSpace);
705 //////////////////////////////////////////////////////////////
707 PHTTPServiceThread::PHTTPServiceThread(PINDEX stackSize,
708 PHTTPServiceProcess & app)
709 : PThread(stackSize, AutoDeleteThread, NormalPriority, "HTTP Service:%x"),
710 process(app)
712 process.httpThreadsMutex.Wait();
713 process.httpThreads.Append(this);
714 process.httpThreadsMutex.Signal();
716 myStackSize = stackSize;
717 socket = NULL;
718 Resume();
722 PHTTPServiceThread::~PHTTPServiceThread()
724 process.httpThreadsMutex.Wait();
725 process.httpThreads.Remove(this);
726 process.httpThreadsMutex.Signal();
727 delete socket;
731 void PHTTPServiceThread::Close()
733 if (socket != NULL)
734 socket->Close();
738 void PHTTPServiceThread::Main()
740 PTCPSocket * socket = process.AcceptHTTP();
741 if (socket != NULL) {
742 new PHTTPServiceThread(myStackSize, process);
743 process.ProcessHTTP(*socket);
748 //////////////////////////////////////////////////////////////
750 PConfigPage::PConfigPage(PHTTPServiceProcess & app,
751 const PString & title,
752 const PString & section,
753 const PHTTPAuthority & auth)
754 : PHTTPConfig(title, section, auth),
755 process(app)
760 PConfigPage::PConfigPage(PHTTPServiceProcess & app,
761 const PString & section,
762 const PHTTPAuthority & auth)
763 : PHTTPConfig(section.ToLower() + ".html", section, auth),
764 process(app)
769 void PConfigPage::OnLoadedText(PHTTPRequest & request, PString & text)
771 PServiceHTML::ProcessMacros(request, text,
772 GetURL().AsString(PURL::PathOnly).Mid(1),
773 PServiceHTML::LoadFromFile);
774 PHTTPConfig::OnLoadedText(request, text);
775 PServiceHTML::ProcessMacros(request, text, "", PServiceHTML::NoOptions);
779 BOOL PConfigPage::OnPOST(PHTTPServer & server,
780 const PURL & url,
781 const PMIMEInfo & info,
782 const PStringToString & data,
783 const PHTTPConnectionInfo & connectInfo)
785 PHTTPConfig::OnPOST(server, url, info, data, connectInfo);
786 return FALSE; // Make sure we break any persistent connections
790 BOOL PConfigPage::Post(PHTTPRequest & request,
791 const PStringToString & data,
792 PHTML & reply)
794 PSYSTEMLOG(Debug3, "Post to " << request.url << '\n' << data);
795 BOOL retval = PHTTPConfig::Post(request, data, reply);
797 if (request.code == PHTTP::RequestOK)
798 process.BeginRestartSystem();
800 PServiceHTML::ProcessMacros(request, reply,
801 GetURL().AsString(PURL::PathOnly).Mid(1),
802 PServiceHTML::LoadFromFile);
803 OnLoadedText(request, reply);
805 return retval;
809 BOOL PConfigPage::GetExpirationDate(PTime & when)
811 // Well and truly before now....
812 when = ImmediateExpiryTime;
813 return TRUE;
817 //////////////////////////////////////////////////////////////
819 PConfigSectionsPage::PConfigSectionsPage(PHTTPServiceProcess & app,
820 const PURL & url,
821 const PHTTPAuthority & auth,
822 const PString & prefix,
823 const PString & valueName,
824 const PURL & editSection,
825 const PURL & newSection,
826 const PString & newTitle,
827 PHTML & heading)
828 : PHTTPConfigSectionList(url, auth, prefix, valueName,
829 editSection, newSection, newTitle, heading),
830 process(app)
835 void PConfigSectionsPage::OnLoadedText(PHTTPRequest & request, PString & text)
837 PServiceHTML::ProcessMacros(request, text,
838 GetURL().AsString(PURL::PathOnly).Mid(1),
839 PServiceHTML::LoadFromFile);
840 PHTTPConfigSectionList::OnLoadedText(request, text);
844 BOOL PConfigSectionsPage::OnPOST(PHTTPServer & server,
845 const PURL & url,
846 const PMIMEInfo & info,
847 const PStringToString & data,
848 const PHTTPConnectionInfo & connectInfo)
850 PHTTPConfigSectionList::OnPOST(server, url, info, data, connectInfo);
851 return FALSE; // Make sure we break any persistent connections
855 BOOL PConfigSectionsPage::Post(PHTTPRequest & request,
856 const PStringToString & data,
857 PHTML & reply)
859 BOOL retval = PHTTPConfigSectionList::Post(request, data, reply);
860 if (request.code == PHTTP::RequestOK)
861 process.BeginRestartSystem();
862 return retval;
866 BOOL PConfigSectionsPage::GetExpirationDate(PTime & when)
868 // Well and truly before now....
869 when = ImmediateExpiryTime;
870 return TRUE;
874 //////////////////////////////////////////////////////////////
876 PRegisterPage::PRegisterPage(PHTTPServiceProcess & app,
877 const PHTTPAuthority & auth)
878 : PConfigPage(app, "register.html", "Secured Options", auth),
879 process(app)
884 PString PRegisterPage::LoadText(PHTTPRequest & request)
886 if (fields.GetSize() > 0)
887 return PConfigPage::LoadText(request);
889 PString mailURL = "mailto:" + process.GetEMailAddress();
890 PString orderURL = mailURL;
891 PString tempURL = mailURL;
892 if (process.GetHomePage() == HOME_PAGE) {
893 orderURL = "https://home.equival.com.au/purchase.html";
894 tempURL = "http://www.equival.com/" + process.GetName().ToLower() + "/register.html";
895 tempURL.Replace(" ", "", TRUE);
898 PServiceHTML regPage(process.GetName() & "Registration", NULL);
899 regPage << "<!--#registration start Permanent-->"
900 "Your registration key is permanent.<p>"
901 "Do not change your registration details or your key will not "
902 "operate correctly.<p>"
903 "If you need to "
904 << PHTML::HotLink(orderURL)
905 << "upgrade"
906 << PHTML::HotLink()
907 << " or "
908 << PHTML::HotLink(mailURL)
909 << "change"
910 << PHTML::HotLink()
911 << " your registration, then you may enter the new values sent "
912 << " to you from "
913 << process.GetManufacturer()
914 << " into the fields "
915 "below, and then press the Accept button.<p>"
916 << PHTML::HRule()
917 << "<!--#registration end Permanent-->"
918 "<!--#registration start Temporary-->"
919 "Your registration key is temporary and will expire on "
920 "<!--#registration ExpiryDate-->.<p>"
921 "Do not change your registration details or your key will not "
922 "operate correctly.<p>"
923 "You may "
924 << PHTML::HotLink(orderURL)
925 << "order a permanent key"
926 << PHTML::HotLink()
927 << " and enter the new values sent to you from "
928 << process.GetManufacturer()
929 << " into the fields below, and then press the Accept button.<p>"
930 << PHTML::HRule()
931 << "<!--#registration end Temporary-->"
932 "<!--#registration start Expired-->"
933 "Your temporary registration key has expired.<p>"
934 "You may "
935 << PHTML::HotLink(orderURL)
936 << "order a permanent key"
937 << PHTML::HotLink()
938 << " and enter the new values sent to you from "
939 << process.GetManufacturer()
940 << " into the fields below, and then press the Accept button.<P>"
941 << PHTML::HRule()
942 << "<!--#registration end Expired-->";
944 PSecureConfig securedConf(process.GetProductKey(), process.GetSecuredKeys());
945 PString prefix;
946 if (securedConf.GetValidation() != PSecureConfig::IsValid)
947 prefix = securedConf.GetPendingPrefix();
949 AddFields(prefix);
951 Add(new PHTTPStringField("Validation", 40));
952 BuildHTML(regPage, InsertIntoHTML);
954 regPage << "<!--#registration start Invalid-->"
955 "You have entered the values sent to you from "
956 << process.GetManufacturer()
957 << " incorrectly. Please enter them again. Note, "
958 << PHTML::Emphasis() << PHTML::Strong() << "all" << PHTML::Strong() << PHTML::Emphasis()
959 << "the fields must be entered "
960 << PHTML::Emphasis() << PHTML::Strong() << "exactly" << PHTML::Strong() << PHTML::Emphasis()
961 << " as they appear in the e-mail from "
962 << process.GetManufacturer()
963 << ". We strongly recommend using copy and paste of all the fields, and then "
964 "press the Accept button."
965 "<!--#registration end Invalid-->"
966 "<!--#registration start Default-->"
967 "You may "
968 << PHTML::HotLink(orderURL)
969 << "order a permanent key"
970 << PHTML::HotLink()
971 << " or "
972 << PHTML::HotLink(tempURL)
973 << "obtain a temporary key"
974 << PHTML::HotLink()
975 << " and enter the values sent to you from "
976 << process.GetManufacturer()
977 << " into the fields above, and then press the Accept button.<p>"
978 "<!--#registration end Default-->"
979 << PHTML::HRule()
980 << PHTML::Heading(3) << "Disclaimer" << PHTML::Heading(3)
981 << PHTML::Paragraph() << PHTML::Bold()
982 << "The information and code herein is provided \"as is\" "
983 "without warranty of any kind, either expressed or implied, "
984 "including but not limited to the implied warrenties of "
985 "merchantability and fitness for a particular purpose. In "
986 "no event shall " << process.GetManufacturer() << " be liable "
987 "for any damages whatsoever including direct, indirect, "
988 "incidental, consequential, loss of business profits or special "
989 "damages, even if " << process.GetManufacturer() << " has been "
990 "advised of the possibility of such damages."
991 << PHTML::Bold() << PHTML::Paragraph()
992 << process.GetCopyrightText()
993 << PHTML::Body();
995 SetString(regPage);
996 return PConfigPage::LoadText(request);
1000 static BOOL FindSpliceBlock(const PRegularExpression & regex,
1001 const PString & text,
1002 PINDEX & pos,
1003 PINDEX & len,
1004 PINDEX & start,
1005 PINDEX & finish)
1007 if (!text.FindRegEx(regex, pos, len, 0))
1008 return FALSE;
1010 PINDEX endpos, endlen;
1011 static PRegularExpression EndBlock("<?!--#registration[ \t\n]*end[ \t\n]*[a-z]*[ \t\n]*-->?",
1012 PRegularExpression::Extended|PRegularExpression::IgnoreCase);
1013 if (text.FindRegEx(EndBlock, endpos, endlen, pos)) {
1014 start = pos+len;
1015 finish = endpos-1;
1016 len = endpos - pos + endlen;
1019 return TRUE;
1024 void PRegisterPage::OnLoadedText(PHTTPRequest & request, PString & text)
1026 PString block;
1027 PINDEX pos, len, start = 0, finish = 0;
1028 PSecureConfig securedConf(process.GetProductKey(), process.GetSecuredKeys());
1029 PTime expiry = securedConf.GetTime(securedConf.GetExpiryDateKey());
1031 static PRegularExpression Default("<?!--#registration[ \t\n]*start[ \t\n]*Default[ \t\n]*-->?",
1032 PRegularExpression::Extended|PRegularExpression::IgnoreCase);
1033 static PRegularExpression Permanent("<?!--#registration[ \t\n]*start[ \t\n]*Permanent[ \t\n]*-->?",
1034 PRegularExpression::Extended|PRegularExpression::IgnoreCase);
1035 static PRegularExpression Temporary("<?!--#registration[ \t\n]*start[ \t\n]*Temporary[ \t\n]*-->?",
1036 PRegularExpression::Extended|PRegularExpression::IgnoreCase);
1037 static PRegularExpression Expired("<?!--#registration[ \t\n]*start[ \t\n]*Expired[ \t\n]*-->?",
1038 PRegularExpression::Extended|PRegularExpression::IgnoreCase);
1039 static PRegularExpression Invalid("<?!--#registration[ \t\n]*start[ \t\n]*Invalid[ \t\n]*-->?",
1040 PRegularExpression::Extended|PRegularExpression::IgnoreCase);
1041 static PRegularExpression Pending("name[ \t\n]*=[ \t\n]*\"" +
1042 securedConf.GetPendingPrefix() +
1043 "[^\"]+\"",
1044 PRegularExpression::Extended|PRegularExpression::IgnoreCase);
1046 PServiceHTML::ProcessMacros(request, text,
1047 GetURL().AsString(PURL::PathOnly).Mid(1),
1048 PServiceHTML::LoadFromFile);
1050 switch (securedConf.GetValidation()) {
1051 case PSecureConfig::Defaults :
1052 while (FindSpliceBlock(Default, text, pos, len, start, finish))
1053 text.Splice(text(start, finish), pos, len);
1054 while (FindSpliceBlock(Permanent, text, pos, len, start, finish))
1055 text.Delete(pos, len);
1056 while (FindSpliceBlock(Temporary, text, pos, len, start, finish))
1057 text.Delete(pos, len);
1058 while (FindSpliceBlock(Expired, text, pos, len, start, finish))
1059 text.Delete(pos, len);
1060 while (FindSpliceBlock(Invalid, text, pos, len, start, finish))
1061 text.Delete(pos, len);
1062 break;
1064 case PSecureConfig::Invalid :
1065 case PSecureConfig::Pending :
1066 while (FindSpliceBlock(Default, text, pos, len, start, finish))
1067 text.Delete(pos, len);
1068 while (FindSpliceBlock(Permanent, text, pos, len, start, finish))
1069 text.Delete(pos, len);
1070 while (FindSpliceBlock(Temporary, text, pos, len, start, finish))
1071 text.Delete(pos, len);
1072 while (FindSpliceBlock(Expired, text, pos, len, start, finish))
1073 text.Delete(pos, len);
1074 while (FindSpliceBlock(Invalid, text, pos, len, start, finish))
1075 text.Splice(text(start, finish), pos, len);
1076 break;
1078 case PSecureConfig::Expired :
1079 while (FindSpliceBlock(Default, text, pos, len, start, finish))
1080 text.Delete(pos, len);
1081 while (FindSpliceBlock(Permanent, text, pos, len, start, finish))
1082 text.Delete(pos, len);
1083 while (FindSpliceBlock(Temporary, text, pos, len, start, finish))
1084 text.Delete(pos, len);
1085 while (FindSpliceBlock(Expired, text, pos, len, start, finish))
1086 text.Splice(text(start, finish), pos, len);
1087 while (FindSpliceBlock(Invalid, text, pos, len, start, finish))
1088 text.Delete(pos, len);
1089 break;
1091 case PSecureConfig::IsValid :
1092 while (text.FindRegEx(Pending, pos, len)) {
1093 static PINDEX pendingLength = securedConf.GetPendingPrefix().GetLength();
1094 text.Delete(text.Find('"', pos)+1, pendingLength);
1095 start = pos + len - pendingLength;
1097 if (expiry.GetYear() < 2011) {
1098 while (FindSpliceBlock(Default, text, pos, len, start, finish))
1099 text.Delete(pos, len);
1100 while (FindSpliceBlock(Permanent, text, pos, len, start, finish))
1101 text.Delete(pos, len);
1102 while (FindSpliceBlock(Temporary, text, pos, len, start, finish))
1103 text.Splice(text(start, finish), pos, len);
1104 while (FindSpliceBlock(Expired, text, pos, len, start, finish))
1105 text.Delete(pos, len);
1106 while (FindSpliceBlock(Invalid, text, pos, len, start, finish))
1107 text.Delete(pos, len);
1109 else {
1110 while (FindSpliceBlock(Default, text, pos, len, start, finish))
1111 text.Delete(pos, len);
1112 while (FindSpliceBlock(Permanent, text, pos, len, start, finish))
1113 text.Splice(text(start, finish), pos, len);
1114 while (FindSpliceBlock(Temporary, text, pos, len, start, finish))
1115 text.Delete(pos, len);
1116 while (FindSpliceBlock(Expired, text, pos, len, start, finish))
1117 text.Delete(pos, len);
1118 while (FindSpliceBlock(Invalid, text, pos, len, start, finish))
1119 text.Delete(pos, len);
1123 static PRegularExpression ExpiryDate("<?!--#registration[ \t\n]*ExpiryDate[ \t\n]*-->?",
1124 PRegularExpression::Extended|PRegularExpression::IgnoreCase);
1125 while (text.FindRegEx(ExpiryDate, pos, len, 0))
1126 text.Splice(expiry.AsString(PTime::LongDate), pos, len);
1128 PHTTPConfig::OnLoadedText(request, text);
1129 PServiceHTML::ProcessMacros(request, text, "", PServiceHTML::NoOptions);
1133 BOOL PRegisterPage::Post(PHTTPRequest & request,
1134 const PStringToString & data,
1135 PHTML & reply)
1137 if (fields.GetSize() == 0)
1138 LoadText(request);
1140 BOOL retval = PHTTPConfig::Post(request, data, reply);
1141 if (request.code != PHTTP::RequestOK)
1142 return FALSE;
1144 PSecureConfig sconf(process.GetProductKey(), process.GetSecuredKeys());
1145 switch (sconf.GetValidation()) {
1146 case PSecureConfig::Defaults :
1147 sconf.ResetPending();
1148 break;
1150 case PSecureConfig::IsValid :
1151 break;
1153 case PSecureConfig::Pending :
1154 sconf.ValidatePending();
1155 break;
1157 default :
1158 sconf.ResetPending();
1161 RemoveAllFields();
1162 LoadText(request);
1163 OnLoadedText(request, reply);
1165 return retval;
1169 ///////////////////////////////////////////////////////////////////
1171 static void DigestSecuredKeys(PHTTPServiceProcess & process,
1172 PString & reginfo,
1173 PHTML * html)
1175 const PStringArray & securedKeys = process.GetSecuredKeys();
1176 PSecureConfig sconf(process.GetProductKey(), securedKeys);
1178 PString prefix;
1179 if (sconf.GetValidation() != PSecureConfig::IsValid)
1180 prefix = sconf.GetPendingPrefix();
1182 PMessageDigest5 digestor;
1184 PStringStream info;
1185 info << '"' << process.GetName() << "\" ===";
1187 PINDEX i;
1188 for (i = 0; i < securedKeys.GetSize(); i++) {
1189 PString val = sconf.GetString(prefix + securedKeys[i]).Trim();
1190 info << " \"" << val << '"';
1191 if (html != NULL)
1192 *html << PHTML::HiddenField(securedKeys[i], val);
1193 digestor.Process(val);
1196 PString digest = digestor.Complete();
1197 if (html != NULL)
1198 *html << PHTML::HiddenField("digest", digest);
1200 info.Replace("===", digest);
1201 reginfo = info;
1205 ///////////////////////////////////////////////////////////////////
1207 PServiceHTML::PServiceHTML(const char * title, const char * help, const char * helpGif)
1209 PHTTPServiceProcess::Current().GetPageHeader(*this, title);
1211 ostream & this_stream = *this;
1212 this_stream << PHTML::Heading(1) << title;
1214 if (help != NULL)
1215 this_stream << "&nbsp;"
1216 << PHTML::HotLink(help)
1217 << PHTML::Image(helpGif, "Help", 48, 23, "align=absmiddle")
1218 << PHTML::HotLink();
1220 this_stream << PHTML::Heading(1) << PHTML::Paragraph();
1224 PString PServiceHTML::ExtractSignature(PString & out)
1226 return ExtractSignature(*this, out);
1230 PString PServiceHTML::ExtractSignature(const PString & html,
1231 PString & out,
1232 const char * keyword)
1234 out = html;
1236 PRegularExpression SignatureRegEx("<?!--" + PString(keyword) + "[ \t\r\n]+"
1237 "signature[ \t\r\n]+(-?[^-])+-->?",
1238 PRegularExpression::Extended|PRegularExpression::IgnoreCase);
1240 PINDEX pos, len;
1241 if (out.FindRegEx(SignatureRegEx, pos, len)) {
1242 PString tag = out.Mid(pos, len);
1243 out.Delete(pos, len);
1244 return tag(tag.Find("signature")+10, tag.FindLast('-')-2).Trim();
1247 return PString::Empty();
1251 PString PServiceHTML::CalculateSignature()
1253 return CalculateSignature(*this);
1257 PString PServiceHTML::CalculateSignature(const PString & out)
1259 return CalculateSignature(out, PHTTPServiceProcess::Current().GetSignatureKey());
1263 PString PServiceHTML::CalculateSignature(const PString & out,
1264 const PTEACypher::Key & sig)
1266 // calculate the MD5 digest of the HTML data
1267 PMessageDigest5 digestor;
1269 PINDEX p1 = 0;
1270 PINDEX p2;
1271 while ((p2 = out.FindOneOf("\r\n", p1)) != P_MAX_INDEX) {
1272 if (p2 > p1)
1273 digestor.Process(out(p1, p2-1));
1274 digestor.Process("\r\n", 2);
1275 p1 = p2 + 1;
1276 if (out[p2] == '\r' && out[p1] == '\n') // CR LF pair
1277 p1++;
1279 digestor.Process(out(p1, P_MAX_INDEX));
1281 PMessageDigest5::Code md5;
1282 digestor.Complete(md5);
1284 // encode it
1285 PTEACypher cypher(sig);
1286 BYTE buf[sizeof(md5)+7];
1287 memcpy(buf, &md5, sizeof(md5));
1288 memset(&buf[sizeof(md5)], 0, sizeof(buf)-sizeof(md5));
1289 return cypher.Encode(buf, sizeof(buf));
1293 BOOL PServiceHTML::CheckSignature()
1295 return CheckSignature(*this);
1299 BOOL PServiceHTML::CheckSignature(const PString & html)
1301 if (PHTTPServiceProcess::Current().ShouldIgnoreSignatures())
1302 return TRUE;
1304 // extract the signature from the file
1305 PString out;
1306 PString signature = ExtractSignature(html, out);
1308 // calculate the signature on the data
1309 PString checkSignature = CalculateSignature(out);
1311 // return TRUE or FALSE
1312 return checkSignature == signature;
1316 static BOOL FindBrackets(const PString & args, PINDEX & open, PINDEX & close)
1318 open = args.FindOneOf("[{(", close);
1319 if (open == P_MAX_INDEX)
1320 return FALSE;
1322 switch (args[open]) {
1323 case '[' :
1324 close = args.Find(']', open+1);
1325 break;
1326 case '{' :
1327 close = args.Find('}', open+1);
1328 break;
1329 case '(' :
1330 close = args.Find(')', open+1);
1331 break;
1333 return close != P_MAX_INDEX;
1337 static BOOL ExtractVariables(const PString & args,
1338 PString & variable,
1339 PString & value)
1341 PINDEX open;
1342 PINDEX close = 0;
1343 if (FindBrackets(args, open, close))
1344 variable = args(open+1, close-1);
1345 else {
1346 variable = args.Trim();
1347 close = P_MAX_INDEX-1;
1349 if (variable.IsEmpty())
1350 return FALSE;
1352 if (FindBrackets(args, open, close))
1353 value = args(open+1, close-1);
1355 return TRUE;
1359 ///////////////////////////////////////////////////////////////////////////////
1361 PServiceMacro * PServiceMacro::list;
1364 PServiceMacro::PServiceMacro(const char * name, BOOL isBlock)
1366 macroName = name;
1367 isMacroBlock = isBlock;
1368 link = list;
1369 list = this;
1373 PServiceMacro::PServiceMacro(const PCaselessString & name, BOOL isBlock)
1375 macroName = name;
1376 isMacroBlock = isBlock;
1380 PObject::Comparison PServiceMacro::Compare(const PObject & obj) const
1382 PAssert(PIsDescendant(&obj, PServiceMacro), PInvalidCast);
1383 const PServiceMacro & other = (const PServiceMacro &)obj;
1385 if (isMacroBlock != other.isMacroBlock)
1386 return isMacroBlock ? GreaterThan : LessThan;
1388 int cmp = strcasecmp(macroName, other.macroName);
1389 if (cmp < 0)
1390 return LessThan;
1391 if (cmp > 0)
1392 return GreaterThan;
1393 return EqualTo;
1397 PString PServiceMacro::Translate(PHTTPRequest &, const PString &, const PString &) const
1399 return PString::Empty();
1404 PServiceMacros_list::PServiceMacros_list()
1406 DisallowDeleteObjects();
1407 PServiceMacro * macro = PServiceMacro::list;
1408 while (macro != NULL) {
1409 Append(macro);
1410 macro = macro->link;
1415 PCREATE_SERVICE_MACRO(Header,request,P_EMPTY)
1417 PString hdr = PHTTPServiceProcess::Current().GetPageGraphic();
1418 PServiceHTML::ProcessMacros(request, hdr, "header.html",
1419 PServiceHTML::LoadFromFile|PServiceHTML::NoURLOverride);
1420 return hdr;
1424 PCREATE_SERVICE_MACRO(Copyright,P_EMPTY,P_EMPTY)
1426 return PHTTPServiceProcess::Current().GetCopyrightText();
1430 PCREATE_SERVICE_MACRO(ProductName,P_EMPTY,P_EMPTY)
1432 return PHTTPServiceProcess::Current().GetProductName();
1436 PCREATE_SERVICE_MACRO(Manufacturer,P_EMPTY,P_EMPTY)
1438 return PHTTPServiceProcess::Current().GetManufacturer();
1442 PCREATE_SERVICE_MACRO(Version,P_EMPTY,P_EMPTY)
1444 return PHTTPServiceProcess::Current().GetVersion(TRUE);
1448 PCREATE_SERVICE_MACRO(BuildDate,P_EMPTY,args)
1450 const PTime & date = PHTTPServiceProcess::Current().GetCompilationDate();
1451 if (args.IsEmpty())
1452 return date.AsString("d MMMM yyyy");
1454 return date.AsString(args);
1458 PCREATE_SERVICE_MACRO(OS,P_EMPTY,P_EMPTY)
1460 return PHTTPServiceProcess::Current().GetOSClass() &
1461 PHTTPServiceProcess::Current().GetOSName();
1465 PCREATE_SERVICE_MACRO(Machine,P_EMPTY,P_EMPTY)
1467 return PHTTPServiceProcess::Current().GetOSVersion() + '-' +
1468 PHTTPServiceProcess::Current().GetOSHardware();
1472 PCREATE_SERVICE_MACRO(LongDateTime,P_EMPTY,P_EMPTY)
1474 return PTime().AsString(PTime::LongDateTime);
1478 PCREATE_SERVICE_MACRO(LongDate,P_EMPTY,P_EMPTY)
1480 return PTime().AsString(PTime::LongDate);
1484 PCREATE_SERVICE_MACRO(LongTime,P_EMPTY,P_EMPTY)
1486 return PTime().AsString(PTime::LongTime);
1490 PCREATE_SERVICE_MACRO(MediumDateTime,P_EMPTY,P_EMPTY)
1492 return PTime().AsString(PTime::MediumDateTime);
1496 PCREATE_SERVICE_MACRO(MediumDate,P_EMPTY,P_EMPTY)
1498 return PTime().AsString(PTime::MediumDate);
1502 PCREATE_SERVICE_MACRO(ShortDateTime,P_EMPTY,P_EMPTY)
1504 return PTime().AsString(PTime::ShortDateTime);
1508 PCREATE_SERVICE_MACRO(ShortDate,P_EMPTY,P_EMPTY)
1510 return PTime().AsString(PTime::ShortDate);
1514 PCREATE_SERVICE_MACRO(ShortTime,P_EMPTY,P_EMPTY)
1516 return PTime().AsString(PTime::ShortTime);
1520 PCREATE_SERVICE_MACRO(Time,P_EMPTY,args)
1522 PTime now;
1523 if (args.IsEmpty())
1524 return now.AsString();
1526 return now.AsString(args);
1530 PCREATE_SERVICE_MACRO(StartTime,P_EMPTY,P_EMPTY)
1532 return PProcess::Current().GetStartTime().AsString(PTime::MediumDateTime);
1536 PCREATE_SERVICE_MACRO(UpTime,P_EMPTY,P_EMPTY)
1538 PTimeInterval upTime = PTime() - PProcess::Current().GetStartTime();
1539 return upTime.AsString(0, PTimeInterval::IncludeDays);
1543 PCREATE_SERVICE_MACRO(LocalHost,request,P_EMPTY)
1545 if (request.localAddr != 0)
1546 return PIPSocket::GetHostName(request.localAddr);
1547 else
1548 return PIPSocket::GetHostName();
1552 PCREATE_SERVICE_MACRO(LocalIP,request,P_EMPTY)
1554 if (request.localAddr != 0)
1555 return request.localAddr;
1556 else
1557 return "127.0.0.1";
1561 PCREATE_SERVICE_MACRO(LocalPort,request,P_EMPTY)
1563 if (request.localPort != 0)
1564 return psprintf("%u", request.localPort);
1565 else
1566 return "80";
1570 PCREATE_SERVICE_MACRO(PeerHost,request,P_EMPTY)
1572 if (request.origin != 0)
1573 return PIPSocket::GetHostName(request.origin);
1574 else
1575 return "N/A";
1579 PCREATE_SERVICE_MACRO(PeerIP,request,P_EMPTY)
1581 if (request.origin != 0)
1582 return request.origin;
1583 else
1584 return "N/A";
1587 PCREATE_SERVICE_MACRO(MonitorInfo,request,P_EMPTY)
1589 const PTime & compilationDate = PHTTPServiceProcess::Current().GetCompilationDate();
1591 PString peerAddr = "N/A";
1592 if (request.origin != 0)
1593 peerAddr = request.origin.AsString();
1595 PString localAddr = "127.0.0.1";
1596 if (request.localAddr != 0)
1597 localAddr = request.localAddr.AsString();
1599 WORD localPort = 80;
1600 if (request.localPort != 0)
1601 localPort = request.localPort;
1603 PString timeFormat = "yyyyMMdd hhmmss z";
1605 PTime now;
1606 PTimeInterval upTime = now - PProcess::Current().GetStartTime();
1608 PStringStream monitorText;
1609 monitorText << "Program: " << PHTTPServiceProcess::Current().GetProductName() << "\n"
1610 << "Version: " << PHTTPServiceProcess::Current().GetVersion(TRUE) << "\n"
1611 << "Manufacturer: " << PHTTPServiceProcess::Current().GetManufacturer() << "\n"
1612 << "OS: " << PHTTPServiceProcess::Current().GetOSClass() << " " << PHTTPServiceProcess::Current().GetOSName() << "\n"
1613 << "OS Version: " << PHTTPServiceProcess::Current().GetOSVersion() << "\n"
1614 << "Hardware: " << PHTTPServiceProcess::Current().GetOSHardware() << "\n"
1615 << "Compilation date: " << compilationDate.AsString(timeFormat, PTime::GMT) << "\n"
1616 << "Start Date: " << PProcess::Current().GetStartTime().AsString(timeFormat, PTime::GMT) << "\n"
1617 << "Current Date: " << now.AsString(timeFormat, PTime::GMT) << "\n"
1618 << "Up time: " << upTime << "\n"
1619 << "Peer Addr: " << peerAddr << "\n"
1620 << "Local Host: " << PIPSocket::GetHostName() << "\n"
1621 << "Local Addr: " << localAddr << "\n"
1622 << "Local Port: " << localPort << "\n"
1625 return monitorText;
1629 PCREATE_SERVICE_MACRO(RegInfo,P_EMPTY,P_EMPTY)
1631 PString subs;
1632 DigestSecuredKeys(PHTTPServiceProcess::Current(), subs, NULL);
1633 return subs;
1637 static PString GetRegInfo(const char * info)
1639 PHTTPServiceProcess & process = PHTTPServiceProcess::Current();
1640 PSecureConfig sconf(process.GetProductKey(), process.GetSecuredKeys());
1641 PString pending = sconf.GetPendingPrefix();
1642 return sconf.GetString(info, sconf.GetString(pending+info));
1645 PCREATE_SERVICE_MACRO(RegUser,P_EMPTY,P_EMPTY)
1647 return GetRegInfo("Name");
1651 PCREATE_SERVICE_MACRO(RegCompany,P_EMPTY,P_EMPTY)
1653 return GetRegInfo("Company");
1657 PCREATE_SERVICE_MACRO(RegEmail,P_EMPTY,P_EMPTY)
1659 return GetRegInfo("EMail");
1663 PCREATE_SERVICE_MACRO(Registration,P_EMPTY,args)
1665 PHTTPServiceProcess & process = PHTTPServiceProcess::Current();
1666 PSecureConfig sconf(process.GetProductKey(), process.GetSecuredKeys());
1667 PString pending = sconf.GetPendingPrefix();
1669 PString regNow = "Register Now!";
1670 PString viewReg = "View Registration";
1671 PString demoCopy = "Unregistered Demonstration Copy";
1672 PINDEX open;
1673 PINDEX close = 0;
1674 if (FindBrackets(args, open, close)) {
1675 regNow = args(open+1, close-1);
1676 if (FindBrackets(args, open, close)) {
1677 viewReg = args(open+1, close-1);
1678 if (FindBrackets(args, open, close))
1679 demoCopy = args(open+1, close-1);
1683 PHTML out(PHTML::InBody);
1684 out << "<font size=5>"
1685 << sconf.GetString("Name", sconf.GetString(pending+"Name", "*** "+demoCopy+" ***"))
1686 << PHTML::BreakLine()
1687 << "<font size=4>"
1688 << sconf.GetString("Company", sconf.GetString(pending+"Company"))
1689 << PHTML::BreakLine()
1690 << PHTML::BreakLine()
1691 << "<font size=3>";
1693 if (sconf.GetString("Name").IsEmpty())
1694 process.AddUnregisteredText(out);
1695 else
1696 process.AddRegisteredText(out);
1698 out << PHTML::HotLink("/register.html")
1699 << (sconf.GetString("Name").IsEmpty() ? regNow : viewReg)
1700 << PHTML::HotLink();
1701 return out;
1705 PCREATE_SERVICE_MACRO(InputsFromQuery,request,P_EMPTY)
1707 PStringToString vars = request.url.GetQueryVars();
1708 PStringStream subs;
1709 for (PINDEX i = 0; i < vars.GetSize(); i++)
1710 subs << "<INPUT TYPE=hidden NAME=\"" << vars.GetKeyAt(i)
1711 << "\" VALUE=\"" << vars.GetDataAt(i) << "\">\r\n";
1712 return subs;
1716 PCREATE_SERVICE_MACRO(Query,request,args)
1718 if (args.IsEmpty())
1719 return request.url.GetQuery();
1721 PString variable, value;
1722 if (ExtractVariables(args, variable, value)) {
1723 value = request.url.GetQueryVars()(variable, value);
1724 if (!value)
1725 return value;
1727 return PString::Empty();
1731 PCREATE_SERVICE_MACRO(Get,request,args)
1733 PString variable, value;
1734 if (ExtractVariables(args, variable, value)) {
1735 PString section = request.url.GetQueryVars()("section");
1736 PINDEX slash = variable.FindLast('\\');
1737 if (slash != P_MAX_INDEX) {
1738 section += variable.Left(slash);
1739 variable = variable.Mid(slash+1);
1741 if (!section && !variable) {
1742 PConfig config(section);
1743 return config.GetString(variable, value);
1746 return PString::Empty();
1750 PCREATE_SERVICE_MACRO(URL,request,P_EMPTY)
1752 return request.url.AsString();
1756 PCREATE_SERVICE_MACRO(Include,P_EMPTY,args)
1758 PString text;
1760 if (!args) {
1761 PFile file;
1762 if (file.Open(args, PFile::ReadOnly))
1763 text = file.ReadString(file.GetLength());
1766 return text;
1770 PCREATE_SERVICE_MACRO(SignedInclude,P_EMPTY,args)
1772 PString text;
1774 if (!args) {
1775 PFile file;
1776 if (file.Open(args, PFile::ReadOnly)) {
1777 text = file.ReadString(file.GetLength());
1778 if (!PServiceHTML::CheckSignature(text)) {
1779 PHTTPServiceProcess & process = PHTTPServiceProcess::Current();
1780 PHTML html("Invalid OEM Signature");
1781 html << "The HTML file \""
1782 << args
1783 << "\" contains an invalid signature for \""
1784 << process.GetName()
1785 << "\" by \""
1786 << process.GetManufacturer()
1787 << '"'
1788 << PHTML::Body();
1789 text = html;
1794 return text;
1797 PCREATE_SERVICE_MACRO_BLOCK(IfQuery,request,args,block)
1799 PStringToString vars = request.url.GetQueryVars();
1801 PINDEX space = args.FindOneOf(" \t\r\n");
1802 PString var = args.Left(space);
1803 PString value = args.Mid(space).LeftTrim();
1805 BOOL ok;
1806 if (value.IsEmpty())
1807 ok = vars.Contains(var);
1808 else {
1809 PString operation;
1810 space = value.FindOneOf(" \t\r\n");
1811 if (space != P_MAX_INDEX) {
1812 operation = value.Left(space);
1813 value = value.Mid(space).LeftTrim();
1816 PString query = vars(var);
1817 if (operation == "!=")
1818 ok = query != value;
1819 else if (operation == "<")
1820 ok = query < value;
1821 else if (operation == ">")
1822 ok = query > value;
1823 else if (operation == "<=")
1824 ok = query <= value;
1825 else if (operation == ">=")
1826 ok = query >= value;
1827 else if (operation == "*=")
1828 ok = (query *= value);
1829 else
1830 ok = query == value;
1833 return ok ? block : PString::Empty();
1837 PCREATE_SERVICE_MACRO_BLOCK(IfInURL,request,args,block)
1839 if (request.url.AsString().Find(args) != P_MAX_INDEX)
1840 return block;
1842 return PString::Empty();
1846 PCREATE_SERVICE_MACRO_BLOCK(IfNotInURL,request,args,block)
1848 if (request.url.AsString().Find(args) == P_MAX_INDEX)
1849 return block;
1851 return PString::Empty();
1855 static void SplitCmdAndArgs(const PString & text, PINDEX pos, PCaselessString & cmd, PString & args)
1857 static const char whitespace[] = " \t\r\n";
1858 PString macro = text(text.FindOneOf(whitespace, pos)+1, text.Find("--", pos+3)-1).Trim();
1859 PINDEX endCmd = macro.FindOneOf(whitespace);
1860 if (endCmd == P_MAX_INDEX) {
1861 cmd = macro;
1862 args.MakeEmpty();
1864 else {
1865 cmd = macro.Left(endCmd);
1866 args = macro.Mid(endCmd+1).LeftTrim();
1871 BOOL PServiceHTML::ProcessMacros(PHTTPRequest & request,
1872 PString & text,
1873 const PString & defaultFile,
1874 unsigned options)
1876 PINDEX alreadyLoadedPrefixLength = 0;
1878 PString filename = defaultFile;
1879 if ((options&LoadFromFile) != 0) {
1880 if ((options&NoURLOverride) == 0) {
1881 filename = request.url.GetParameters();
1882 if (filename.IsEmpty())
1883 filename = defaultFile;
1886 if (!filename) {
1887 PString alreadyLoaded = "<!--#loadedfrom " + filename + "-->\r\n";
1888 alreadyLoadedPrefixLength = alreadyLoaded.GetLength();
1890 if (text.Find(alreadyLoaded) != 0) {
1891 PFile file;
1892 if (file.Open(filename, PFile::ReadOnly)) {
1893 text = alreadyLoaded + file.ReadString(file.GetLength());
1894 if ((options&NoSignatureForFile) == 0)
1895 options |= NeedSignature;
1901 if ((options&NeedSignature) != 0) {
1902 if (!CheckSignature(text.Mid(alreadyLoadedPrefixLength))) {
1903 PHTTPServiceProcess & process = PHTTPServiceProcess::Current();
1904 PHTML html("Invalid OEM Signature");
1905 html << "The HTML file \""
1906 << filename
1907 << "\" contains an invalid signature for \""
1908 << process.GetName()
1909 << "\" by \""
1910 << process.GetManufacturer()
1911 << '"'
1912 << PHTML::Body();
1913 text = html;
1914 return FALSE;
1918 static PServiceMacros_list ServiceMacros;
1920 PHTTPServiceProcess & process = PHTTPServiceProcess::Current();
1922 PRegularExpression StartBlockRegEx("<?!--#(equival|" + process.GetMacroKeyword() + ")"
1923 "start[ \t\r\n]+(-?[^-])+-->?",
1924 PRegularExpression::Extended|PRegularExpression::IgnoreCase);
1926 PRegularExpression MacroRegEx("<?!--#(equival|" + process.GetMacroKeyword() + ")[ \t\r\n]+(-?[^-])+-->?",
1927 PRegularExpression::Extended|PRegularExpression::IgnoreCase);
1929 BOOL substitedMacro;
1930 do {
1931 substitedMacro = FALSE;
1933 PINDEX pos = 0;
1934 PINDEX len;
1935 while (text.FindRegEx(StartBlockRegEx, pos, len, pos)) {
1936 PString substitution;
1938 PCaselessString cmd;
1939 PString args;
1940 SplitCmdAndArgs(text, pos, cmd, args);
1941 PINDEX idx = ServiceMacros.GetValuesIndex(PServiceMacro(cmd, TRUE));
1942 if (idx != P_MAX_INDEX) {
1943 PRegularExpression EndBlockRegEx("<?!--#(equival|" + process.GetMacroKeyword() + ")"
1944 "end[ \t\r\n]+" + cmd + "(-?[^-])*-->?",
1945 PRegularExpression::Extended|PRegularExpression::IgnoreCase);
1946 PINDEX endpos, endlen;
1947 if (text.FindRegEx(EndBlockRegEx, endpos, endlen, pos+len)) {
1948 PINDEX startpos = pos+len;
1949 len = endpos-pos + endlen;
1950 substitution = ServiceMacros[idx].Translate(request, args, text(startpos, endpos-1));
1951 substitedMacro = TRUE;
1955 text.Splice(substitution, pos, len);
1958 pos = 0;
1959 while (text.FindRegEx(MacroRegEx, pos, len, pos)) {
1960 PCaselessString cmd;
1961 PString args;
1962 SplitCmdAndArgs(text, pos, cmd, args);
1964 PString substitution;
1965 if (!process.SubstituteEquivalSequence(request, cmd & args, substitution)) {
1966 PINDEX idx = ServiceMacros.GetValuesIndex(PServiceMacro(cmd, FALSE));
1967 if (idx != P_MAX_INDEX) {
1968 substitution = ServiceMacros[idx].Translate(request, args, PString::Empty());
1969 substitedMacro = TRUE;
1973 text.Splice(substitution, pos, len);
1975 } while (substitedMacro);
1977 return TRUE;
1981 ///////////////////////////////////////////////////////////////////
1983 static void ServiceOnLoadedText(PString & text)
1985 PHTTPServiceProcess & process = PHTTPServiceProcess::Current();
1987 PString manuf = "<!--Standard_" + process.GetManufacturer() + "_Header-->";
1988 if (text.Find(manuf) != P_MAX_INDEX)
1989 text.Replace(manuf, process.GetPageGraphic(), TRUE);
1991 static const char equiv[] = "<!--Standard_Equivalence_Header-->";
1992 if (text.Find(equiv) != P_MAX_INDEX)
1993 text.Replace(equiv, process.GetPageGraphic(), TRUE);
1995 static const char copy[] = "<!--Standard_Copyright_Header-->";
1996 if (text.Find(copy) != P_MAX_INDEX)
1997 text.Replace(copy, process.GetCopyrightText(), TRUE);
2001 PString PServiceHTTPString::LoadText(PHTTPRequest & request)
2003 PString text = PHTTPString::LoadText(request);
2004 ServiceOnLoadedText(text);
2005 PServiceHTML::ProcessMacros(request, text, "", PServiceHTML::LoadFromFile);
2007 return text;
2010 BOOL PServiceHTTPString::GetExpirationDate(PTime & when)
2012 // Well and truly before now....
2013 when = ImmediateExpiryTime;
2014 return TRUE;
2018 void PServiceHTTPFile::OnLoadedText(PHTTPRequest & request, PString & text)
2020 ServiceOnLoadedText(text);
2021 PServiceHTML::ProcessMacros(request, text, GetURL().AsString(PURL::PathOnly),
2022 needSignature ? PServiceHTML::NeedSignature : PServiceHTML::NoOptions);
2025 BOOL PServiceHTTPFile::GetExpirationDate(PTime & when)
2027 // Well and truly before now....
2028 when = ImmediateExpiryTime;
2029 return TRUE;
2033 void PServiceHTTPDirectory::OnLoadedText(PHTTPRequest & request, PString & text)
2035 ServiceOnLoadedText(text);
2036 PServiceHTML::ProcessMacros(request, text, GetURL().AsString(PURL::PathOnly),
2037 needSignature ? PServiceHTML::NeedSignature : PServiceHTML::NoOptions);
2041 BOOL PServiceHTTPDirectory::GetExpirationDate(PTime & when)
2043 // Well and truly before now....
2044 when = ImmediateExpiryTime;
2045 return TRUE;
2049 ///////////////////////////////////////////////////////////////////