Added a parameter to semaphore constructor to avoid ambiguity
[pwlib.git] / src / ptclib / httpsvc.cxx
blobbc43bd77fd61979c944b843361d4819cd0b1d7b3
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.91 2004/01/17 17:44:54 csoutheren
28 * Changed to use PString::MakeEmpty
30 * Revision 1.90 2003/09/17 09:02:13 csoutheren
31 * Removed memory leak detection code
33 * Revision 1.89 2003/02/19 07:23:45 robertj
34 * Changes to allow for single threaded HTTP service processes.
36 * Revision 1.88 2002/11/06 22:47:25 robertj
37 * Fixed header comment (copyright etc)
39 * Revision 1.87 2002/10/10 04:43:44 robertj
40 * VxWorks port, thanks Martijn Roest
42 * Revision 1.86 2002/08/14 00:43:40 robertj
43 * Added ability to have fixed maximum length PStringStream's so does not do
44 * unwanted malloc()'s while outputing data.
46 * Revision 1.85 2002/08/13 05:39:17 robertj
47 * Fixed GNU compatibility
49 * Revision 1.84 2002/08/13 01:57:15 robertj
50 * Fixed "last dump object" position in Memory Dump macro.
52 * Revision 1.83 2002/08/13 01:30:27 robertj
53 * Added UpTime macro for time service has been running.
54 * Added IfQuery macro blcok to add chunks of HTML depending on the value
55 * of query parameters in the URL.
56 * Added memory statistics dump and memory object dump macros to help in
57 * leak finding.
59 * Revision 1.82 2002/07/30 08:37:34 robertj
60 * Removed peer host as bad DNS makes it useless due to huge timeout.
62 * Revision 1.81 2002/07/30 04:51:26 craigs
63 * Added MonitorInfo macro
65 * Revision 1.80 2002/07/30 03:16:57 craigs
66 * Added StartTime macro
68 * Revision 1.79 2002/07/17 09:18:00 robertj
69 * made detection of gif file more intelligent for debug version.
71 * Revision 1.78 2002/07/17 08:03:45 robertj
72 * Allowed for adjustable copyright holder.
73 * Allowed for not having gif file for product name in default header.
75 * Revision 1.77 2001/10/10 08:06:49 robertj
76 * Fixed problem with not shutting down threads when closing listener.
78 * Revision 1.76 2001/09/11 02:37:41 robertj
79 * Fixed thread name for HTTP service connection handler.
81 * Revision 1.75 2001/08/28 06:44:45 craigs
82 * Added ability to override PHTTPServer creation
84 * Revision 1.74 2001/06/30 06:59:06 yurik
85 * Jac Goudsmit from Be submit these changes 6/28. Implemented by Yuri Kiryanov
87 * Revision 1.73 2001/06/27 04:14:48 robertj
88 * Added logging for listener thread open/close.
90 * Revision 1.72 2001/06/23 00:32:15 robertj
91 * Added parameter to be able to set REUSEADDR on listener socket.
93 * Revision 1.71 2001/05/07 23:27:06 robertj
94 * Added SO_LINGER setting to HTTP sockets to help with clearing up sockets
95 * when the application exits, which prevents new run of app as "port in use".
97 * Revision 1.70 2001/03/26 04:55:26 robertj
98 * Made sure OnConfigChanged() is called from OnStart() function.
100 * Revision 1.69 2001/03/21 06:29:31 robertj
101 * Fixed bug in calling OnConfigChanged after service macros are loaded,
102 * should be before so state can be changed before the macros translated.
104 * Revision 1.68 2001/03/19 02:41:53 robertj
105 * Made sure HTTP listener thread is shut down in OnStop().
107 * Revision 1.67 2001/03/16 03:33:21 robertj
108 * Fixed HTML signature code due to changes in encryption code.
110 * Revision 1.66 2001/03/04 02:24:44 robertj
111 * Removed default OnControl() from http service as cannot get port number.
113 * Revision 1.65 2001/02/21 04:33:46 robertj
114 * Fixed GNU warning.
116 * Revision 1.64 2001/02/20 02:32:41 robertj
117 * Added PServiceMacro version that can do substitutions on blocks of HTML.
119 * Revision 1.63 2001/02/15 01:12:15 robertj
120 * Moved some often repeated HTTP service code into PHTTPServiceProcess.
122 * Revision 1.62 2001/02/14 06:52:26 robertj
123 * Fixed GNU compatibility with last change to PServiceMacro.
125 * Revision 1.61 2001/02/14 02:30:59 robertj
126 * Moved HTTP Service Macro facility to public API so can be used by apps.
127 * Added ability to specify the service macro keyword, defaults to "macro".
128 * Added machine macro to get the OS version and hardware.
130 * Revision 1.60 2001/01/15 06:17:56 robertj
131 * Set HTTP resource members to private to assure are not modified by
132 * dscendents in non-threadsafe manner.
134 * Revision 1.59 2001/01/08 22:53:34 craigs
135 * Changed OnPOST to allow subtle usage of embedded commands
137 * Revision 1.58 2000/12/14 08:09:41 robertj
138 * Fixed missing immediate expiry date on string and file service HTTP resourcer.
140 * Revision 1.57 2000/12/11 13:15:17 robertj
141 * Added macro to include signed or unsigned chunks of HTML.
142 * Added flag to globally ignore HTML signatures (useful for develeopment).
144 * Revision 1.56 2000/10/23 09:17:26 robertj
145 * Fixed bug un Linux version where HTML macros didn't work correctly.
147 * Revision 1.55 2000/08/04 12:48:25 robertj
148 * Added mechanism by which a service can get at new HTTP connections, eg to add SSL.
150 * Revision 1.54 2000/05/02 02:58:49 robertj
151 * Fixed MSVC warning about unused parameters.
153 * Revision 1.53 2000/05/02 02:01:18 craigs
154 * Changed stricmp and added implementation of PServiceMacro::Translate
156 * Revision 1.52 2000/05/02 01:50:37 robertj
157 * Rewrite of PServiceMacro so does not use malloc (indirectly).
159 * Revision 1.51 2000/01/27 00:35:52 robertj
160 * Fixed benign warning about uninitialised variables in MSVC optimised compile.
162 * Revision 1.50 1999/08/07 06:50:52 robertj
163 * Removed silly (and incorrect) warning.
165 * Revision 1.49 1999/04/24 05:16:26 robertj
166 * Fixed incorrect date in copyright notice.
168 * Revision 1.48 1998/11/30 05:37:46 robertj
169 * New directory structure
171 * Revision 1.47 1998/11/24 23:05:14 robertj
172 * Fixed extra *** in demo message
174 * Revision 1.46 1998/11/16 07:23:15 robertj
175 * More PPC GNU compatibility.
177 * Revision 1.45 1998/11/16 06:50:40 robertj
178 * Fixed PPC GNU compiler compatibility.
180 * Revision 1.44 1998/10/31 12:49:25 robertj
181 * Added read/write mutex to the HTTP space variable to avoid thread crashes.
183 * Revision 1.43 1998/10/29 11:58:52 robertj
184 * Added ability to configure the HTTP threads stack size.
186 * Revision 1.42 1998/10/29 11:31:57 robertj
187 * Fixed default URL to have lower case and spaceless product name.
188 * Increased HTTP stack size.
190 * Revision 1.41 1998/10/15 01:53:35 robertj
191 * GNU compatibility.
193 * Revision 1.40 1998/10/13 14:06:24 robertj
194 * Complete rewrite of memory leak detection code.
196 * Revision 1.39 1998/09/23 06:22:15 robertj
197 * Added open source copyright license.
199 * Revision 1.38 1998/09/18 01:47:23 robertj
200 * Fixed bug that made files with signature on first line fail on unix systems.
202 * Revision 1.37 1998/08/20 06:01:02 robertj
203 * Improved internationalisation, registrationpage override.
205 * Revision 1.36 1998/04/21 02:43:40 robertj
206 * Fixed conditional around wrong way for requiring signature on HTML files.
208 * Revision 1.35 1998/04/01 01:55:41 robertj
209 * Fixed bug for automatically including GIF file in HTTP name space.
211 * Revision 1.34 1998/03/23 03:21:40 robertj
212 * Fixed missing invalid case in register page.
214 * Revision 1.33 1998/03/20 03:18:15 robertj
215 * Added special classes for specific sepahores, PMutex and PSyncPoint.
217 * Revision 1.32 1998/03/17 10:14:39 robertj
218 * Rewrite of registration page to allow for HTML file override.
220 * Revision 1.31 1998/03/09 07:17:48 robertj
221 * Added IP peer/local number macros.
222 * Set GetPageGraphic reference to GIF file to be at lop level directory.
224 * Revision 1.30 1998/02/16 00:14:09 robertj
225 * Added ProductName and BuildDate macros.
226 * Major rewrite of application info passed in PHTTPServiceProcess constructor.
228 * Revision 1.29 1998/02/03 06:22:45 robertj
229 * Allowed PHTTPServiceString to be overridden by html file after ';'.
231 * Revision 1.28 1998/01/26 02:49:19 robertj
232 * GNU support.
234 * Revision 1.27 1998/01/26 02:12:14 robertj
235 * GNU warnings.
237 * Revision 1.26 1998/01/26 00:45:44 robertj
238 * Added option flags to ProcessMacros to automatically load from file etc.
239 * Assured that all service HTTP resources are overidable with file, using ; URL field.
240 * Added a number of extra #equival macros.
241 * Added "Pty. Ltd." to company name.
243 * Revision 1.25 1997/11/10 12:40:05 robertj
244 * Changed SustituteEquivalSequence so can override standard macros.
246 * Revision 1.24 1997/11/04 06:02:46 robertj
247 * Allowed help gif file name to overridable in PServiceHTML, so can be in subdirectory.
249 * Revision 1.23 1997/10/30 10:21:26 robertj
250 * Added ability to customise regisration text by application.
252 * Revision 1.22 1997/08/28 14:19:40 robertj
253 * Fixed bug where HTTP directory was not processed for macros.
255 * Revision 1.20 1997/08/20 08:59:58 craigs
256 * Changed macro handling to commonise #equival sequence
258 * Revision 1.19 1997/07/26 11:38:22 robertj
259 * Support for overridable pages in HTTP service applications.
261 * Revision 1.18 1997/07/08 13:11:44 robertj
262 * Added standard header and copyright macros to service HTML.
264 * Revision 1.17 1997/06/16 13:20:15 robertj
265 * Fixed bug where PHTTPThread crashes on exit.
267 * Revision 1.16 1997/05/16 12:07:21 robertj
268 * Added operating system and version to hidden fields on registration form.
270 * Revision 1.15 1997/03/02 03:40:59 robertj
271 * Added error logging to standard HTTP Service HTTP Server.
273 * Revision 1.14 1997/02/05 11:54:54 robertj
274 * Added support for order form page overridiing.
276 * Revision 1.13 1997/01/28 11:45:19 robertj
279 * Revision 1.13 1997/01/27 10:22:37 robertj
280 * Numerous changes to support OEM versions of products.
282 * Revision 1.12 1997/01/03 06:33:23 robertj
283 * Removed slash from operating system version string, so says Windows NT rather than Windows/NT
285 * Revision 1.11 1996/11/16 10:50:26 robertj
286 * ??
288 * Revision 1.10 1996/11/04 03:58:23 robertj
289 * Changed to accept separate copyright and manufacturer strings.
291 * Revision 1.8 1996/10/08 13:08:29 robertj
292 * Changed standard graphic to use PHTML class.
294 * Revision 1.7 1996/09/14 13:09:33 robertj
295 * Major upgrade:
296 * rearranged sockets to help support IPX.
297 * added indirect channel class and moved all protocols to descend from it,
298 * separating the protocol from the low level byte transport.
300 * Revision 1.6 1996/08/25 09:39:00 robertj
301 * Prevented registration if no user etc entered.
303 * Revision 1.5 1996/08/19 13:39:55 robertj
304 * Fixed race condition in system restart logic.
306 * Revision 1.4 1996/08/08 13:36:39 robertj
307 * Fixed Registation page so no longer has static link, ie can be DLLed.
309 * Revision 1.3 1996/07/15 10:36:48 robertj
310 * Added registration info to bottom of order form so can be faxed to us.
312 * Revision 1.2 1996/06/28 13:21:30 robertj
313 * Fixed nesting problem in tables.
314 * Fixed PConfig page always restarting.
316 * Revision 1.1 1996/06/13 13:33:34 robertj
317 * Initial revision
321 #ifdef __GNUC__
322 #pragma implementation "httpsvc.h"
323 #endif
325 #include <ptlib.h>
326 #include <ptclib/httpsvc.h>
327 #include <ptlib/sockets.h>
330 PSORTED_LIST(PServiceMacros_base, PServiceMacro);
332 class PServiceMacros_list : public PServiceMacros_base
334 public:
335 PServiceMacros_list();
339 #define new PNEW
342 #define HOME_PAGE "http://www.equival.com"
343 #define EMAIL "equival@equival.com.au"
344 #define EQUIVALENCE "Equivalence Pty. Ltd."
347 static const PTime ImmediateExpiryTime(0, 0, 0, 1, 1, 1980);
350 ///////////////////////////////////////////////////////////////////////////////
352 PHTTPServiceProcess::PHTTPServiceProcess(const Info & inf)
353 : PServiceProcess(inf.manufacturerName, inf.productName,
354 inf.majorVersion, inf.minorVersion, inf.buildStatus, inf.buildNumber),
355 macroKeyword("macro"),
356 productKey(inf.productKey),
357 securedKeys(inf.securedKeyCount, inf.securedKeys),
358 signatureKey(inf.signatureKey),
359 compilationDate(inf.compilationDate),
360 manufacturersHomePage(inf.manufHomePage != NULL ? inf.manufHomePage : HOME_PAGE),
361 manufacturersEmail(inf.email != NULL ? inf.email : EMAIL),
362 productNameHTML(inf.productHTML != NULL ? inf.productHTML : inf.productName),
363 gifHTML(inf.gifHTML),
364 copyrightHolder(inf.copyrightHolder != NULL ? inf.copyrightHolder : EQUIVALENCE),
365 copyrightHomePage(inf.copyrightHomePage != NULL ? inf.copyrightHomePage : HOME_PAGE),
366 copyrightEmail(inf.copyrightEmail != NULL ? inf.copyrightEmail : EMAIL)
368 ignoreSignatures = FALSE;
370 if (inf.gifFilename != NULL) {
371 PDirectory exeDir = GetFile().GetDirectory();
372 #if defined(_WIN32) && defined(_DEBUG)
373 // Special check to aid in using DevStudio for debugging.
374 if (exeDir.Find("\\Debug\\") != P_MAX_INDEX)
375 exeDir = exeDir.GetParent();
376 #endif
377 httpNameSpace.AddResource(new PServiceHTTPFile(inf.gifFilename, exeDir+inf.gifFilename));
378 if (gifHTML.IsEmpty()) {
379 gifHTML = psprintf("<img src=\"/%s\" alt=\"%s!\"", inf.gifFilename, inf.productName);
380 if (inf.gifWidth != 0 && inf.gifHeight != 0)
381 gifHTML += psprintf(" width=%i height=%i", inf.gifWidth, inf.gifHeight);
382 gifHTML += " align=absmiddle>";
386 restartThread = NULL;
387 httpListeningSocket = NULL;
388 httpThreads.DisallowDeleteObjects();
392 PHTTPServiceProcess::~PHTTPServiceProcess()
394 ShutdownListener();
398 PHTTPServiceProcess & PHTTPServiceProcess::Current()
400 PHTTPServiceProcess & process = (PHTTPServiceProcess &)PProcess::Current();
401 PAssert(process.IsDescendant(PHTTPServiceProcess::Class()), "Not a HTTP service!");
402 return process;
406 BOOL PHTTPServiceProcess::OnStart()
408 if (!Initialise("Started"))
409 return FALSE;
411 OnConfigChanged();
412 return TRUE;
416 void PHTTPServiceProcess::OnStop()
418 ShutdownListener();
419 PSYSTEMLOG(Warning, GetName() << " stopped.");
420 PServiceProcess::OnStop();
424 BOOL PHTTPServiceProcess::OnPause()
426 OnConfigChanged();
427 return TRUE;
431 void PHTTPServiceProcess::OnContinue()
433 if (Initialise("Restarted"))
434 return;
436 OnStop();
437 Terminate();
441 #ifdef _WIN32
442 const char * PHTTPServiceProcess::GetServiceDependencies() const
444 return "EventLog\0Tcpip\0";
446 #endif
449 BOOL PHTTPServiceProcess::ListenForHTTP(WORD port,
450 PSocket::Reusability reuse,
451 PINDEX stackSize)
453 if (httpListeningSocket != NULL &&
454 httpListeningSocket->GetPort() == port &&
455 httpListeningSocket->IsOpen())
456 return TRUE;
458 return ListenForHTTP(new PTCPSocket(port), reuse, stackSize);
462 BOOL PHTTPServiceProcess::ListenForHTTP(PSocket * listener,
463 PSocket::Reusability reuse,
464 PINDEX stackSize)
466 if (httpListeningSocket != NULL)
467 ShutdownListener();
469 httpListeningSocket = PAssertNULL(listener);
470 if (!httpListeningSocket->Listen(5, 0, reuse)) {
471 PSYSTEMLOG(Debug, "HTTPSVC\tListen on port " << httpListeningSocket->GetPort()
472 << " failed: " << httpListeningSocket->GetErrorText());
473 return FALSE;
476 if (stackSize > 1000)
477 new PHTTPServiceThread(stackSize, *this);
479 return TRUE;
483 void PHTTPServiceProcess::ShutdownListener()
485 if (httpListeningSocket == NULL)
486 return;
488 if (!httpListeningSocket->IsOpen())
489 return;
491 PSYSTEMLOG(Debug, "HTTPSVC\tClosing listener socket on port "
492 << httpListeningSocket->GetPort());
494 httpListeningSocket->Close();
496 httpThreadsMutex.Wait();
497 for (PINDEX i = 0; i < httpThreads.GetSize(); i++)
498 httpThreads[i].Close();
500 while (httpThreads.GetSize() > 0) {
501 httpThreadsMutex.Signal();
502 Sleep(1);
503 httpThreadsMutex.Wait();
506 httpThreadsMutex.Signal();
508 delete httpListeningSocket;
509 httpListeningSocket = NULL;
513 PString PHTTPServiceProcess::GetCopyrightText()
515 PHTML html(PHTML::InBody);
516 html << "Copyright &copy;"
517 << compilationDate.AsString("yyyy") << " by "
518 << PHTML::HotLink(copyrightHomePage)
519 << copyrightHolder
520 << PHTML::HotLink()
521 << ", "
522 << PHTML::HotLink("mailto:" + copyrightEmail)
523 << copyrightEmail
524 << PHTML::HotLink();
525 return html;
529 PString PHTTPServiceProcess::GetPageGraphic()
531 PFile header;
532 if (header.Open("header.html", PFile::ReadOnly))
533 return header.ReadString(header.GetLength());
535 PHTML html(PHTML::InBody);
536 html << PHTML::TableStart()
537 << PHTML::TableRow()
538 << PHTML::TableData();
540 if (gifHTML.IsEmpty())
541 html << PHTML::Heading(1) << productNameHTML << "&nbsp;" << PHTML::Heading(1);
542 else
543 html << gifHTML;
545 html << PHTML::TableData()
546 << GetOSClass() << ' ' << GetOSName()
547 << " Version " << GetVersion(TRUE) << PHTML::BreakLine()
548 << ' ' << GetCompilationDate().AsString("d MMMM yyyy")
549 << PHTML::BreakLine()
550 << "By "
551 << PHTML::HotLink(manufacturersHomePage) << GetManufacturer() << PHTML::HotLink()
552 << ", "
553 << PHTML::HotLink("mailto:" + manufacturersEmail) << manufacturersEmail << PHTML::HotLink()
554 << PHTML::TableEnd()
555 << PHTML::HRule();
557 return html;
561 void PHTTPServiceProcess::GetPageHeader(PHTML & html)
563 GetPageHeader(html, GetName());
567 void PHTTPServiceProcess::GetPageHeader(PHTML & html, const PString & title)
569 html << PHTML::Title(title)
570 << PHTML::Body()
571 << GetPageGraphic();
575 PTCPSocket * PHTTPServiceProcess::AcceptHTTP()
577 if (httpListeningSocket == NULL)
578 return NULL;
580 if (!httpListeningSocket->IsOpen())
581 return NULL;
583 // get a socket when a client connects
584 PTCPSocket * socket = new PTCPSocket;
585 if (socket->Accept(*httpListeningSocket))
586 return socket;
588 if (socket->GetErrorCode() != PChannel::Interrupted)
589 PSYSTEMLOG(Error, "Accept failed for HTTP: " << socket->GetErrorText());
591 if (httpListeningSocket != NULL && httpListeningSocket->IsOpen())
592 return socket;
594 delete socket;
595 return NULL;
599 BOOL PHTTPServiceProcess::ProcessHTTP(PTCPSocket & socket)
601 if (!socket.IsOpen())
602 return TRUE;
604 PHTTPServer * server = CreateHTTPServer(socket);
605 if (server == NULL) {
606 PSYSTEMLOG(Error, "HTTP server creation/open failed.");
607 return TRUE;
610 // process requests
611 while (server->ProcessCommand())
614 // always close after the response has been sent
615 delete server;
617 // if a restart was requested, then do it, but only if we are not shutting down
618 if (httpListeningSocket->IsOpen())
619 CompleteRestartSystem();
621 return TRUE;
625 void PHTTPServiceProcess::BeginRestartSystem()
627 if (restartThread == NULL) {
628 restartThread = PThread::Current();
629 OnConfigChanged();
634 void PHTTPServiceProcess::CompleteRestartSystem()
636 if (restartThread == NULL)
637 return;
639 if (restartThread != PThread::Current())
640 return;
642 httpNameSpace.StartWrite();
644 if (Initialise("Restart\tInitialisation"))
645 restartThread = NULL;
647 httpNameSpace.EndWrite();
649 if (restartThread != NULL)
650 Terminate();
654 void PHTTPServiceProcess::AddRegisteredText(PHTML &)
659 void PHTTPServiceProcess::AddUnregisteredText(PHTML &)
664 BOOL PHTTPServiceProcess::SubstituteEquivalSequence(PHTTPRequest &, const PString &, PString &)
666 return FALSE;
670 PHTTPServer * PHTTPServiceProcess::CreateHTTPServer(PTCPSocket & socket)
672 #ifdef SO_LINGER
673 const linger ling = { 1, 5 };
674 socket.SetOption(SO_LINGER, &ling, sizeof(ling));
675 #endif
677 PHTTPServer * server = OnCreateHTTPServer(httpNameSpace);
679 if (server->Open(socket))
680 return server;
682 delete server;
683 return NULL;
687 PHTTPServer * PHTTPServiceProcess::OnCreateHTTPServer(const PHTTPSpace & httpNameSpace)
689 return new PHTTPServer(httpNameSpace);
693 //////////////////////////////////////////////////////////////
695 PHTTPServiceThread::PHTTPServiceThread(PINDEX stackSize,
696 PHTTPServiceProcess & app)
697 : PThread(stackSize, AutoDeleteThread, NormalPriority, "HTTP Service:%x"),
698 process(app)
700 process.httpThreadsMutex.Wait();
701 process.httpThreads.Append(this);
702 process.httpThreadsMutex.Signal();
704 myStackSize = stackSize;
705 socket = NULL;
706 Resume();
710 PHTTPServiceThread::~PHTTPServiceThread()
712 process.httpThreadsMutex.Wait();
713 process.httpThreads.Remove(this);
714 process.httpThreadsMutex.Signal();
715 delete socket;
719 void PHTTPServiceThread::Close()
721 if (socket != NULL)
722 socket->Close();
726 void PHTTPServiceThread::Main()
728 PTCPSocket * socket = process.AcceptHTTP();
729 if (socket != NULL) {
730 new PHTTPServiceThread(myStackSize, process);
731 process.ProcessHTTP(*socket);
736 //////////////////////////////////////////////////////////////
738 PConfigPage::PConfigPage(PHTTPServiceProcess & app,
739 const PString & title,
740 const PString & section,
741 const PHTTPAuthority & auth)
742 : PHTTPConfig(title, section, auth),
743 process(app)
748 PConfigPage::PConfigPage(PHTTPServiceProcess & app,
749 const PString & section,
750 const PHTTPAuthority & auth)
751 : PHTTPConfig(section.ToLower() + ".html", section, auth),
752 process(app)
757 void PConfigPage::OnLoadedText(PHTTPRequest & request, PString & text)
759 PServiceHTML::ProcessMacros(request, text,
760 GetURL().AsString(PURL::PathOnly).Mid(1),
761 PServiceHTML::LoadFromFile);
762 PHTTPConfig::OnLoadedText(request, text);
763 PServiceHTML::ProcessMacros(request, text, "", PServiceHTML::NoOptions);
767 BOOL PConfigPage::OnPOST(PHTTPServer & server,
768 const PURL & url,
769 const PMIMEInfo & info,
770 const PStringToString & data,
771 const PHTTPConnectionInfo & connectInfo)
773 PHTTPConfig::OnPOST(server, url, info, data, connectInfo);
774 return FALSE; // Make sure we break any persistent connections
778 BOOL PConfigPage::Post(PHTTPRequest & request,
779 const PStringToString & data,
780 PHTML & reply)
782 PSYSTEMLOG(Debug3, "Post to " << request.url << '\n' << data);
783 BOOL retval = PHTTPConfig::Post(request, data, reply);
785 if (request.code == PHTTP::RequestOK)
786 process.BeginRestartSystem();
788 PServiceHTML::ProcessMacros(request, reply,
789 GetURL().AsString(PURL::PathOnly).Mid(1),
790 PServiceHTML::LoadFromFile);
791 OnLoadedText(request, reply);
793 return retval;
797 BOOL PConfigPage::GetExpirationDate(PTime & when)
799 // Well and truly before now....
800 when = ImmediateExpiryTime;
801 return TRUE;
805 //////////////////////////////////////////////////////////////
807 PConfigSectionsPage::PConfigSectionsPage(PHTTPServiceProcess & app,
808 const PURL & url,
809 const PHTTPAuthority & auth,
810 const PString & prefix,
811 const PString & valueName,
812 const PURL & editSection,
813 const PURL & newSection,
814 const PString & newTitle,
815 PHTML & heading)
816 : PHTTPConfigSectionList(url, auth, prefix, valueName,
817 editSection, newSection, newTitle, heading),
818 process(app)
823 void PConfigSectionsPage::OnLoadedText(PHTTPRequest & request, PString & text)
825 PServiceHTML::ProcessMacros(request, text,
826 GetURL().AsString(PURL::PathOnly).Mid(1),
827 PServiceHTML::LoadFromFile);
828 PHTTPConfigSectionList::OnLoadedText(request, text);
832 BOOL PConfigSectionsPage::OnPOST(PHTTPServer & server,
833 const PURL & url,
834 const PMIMEInfo & info,
835 const PStringToString & data,
836 const PHTTPConnectionInfo & connectInfo)
838 PHTTPConfigSectionList::OnPOST(server, url, info, data, connectInfo);
839 return FALSE; // Make sure we break any persistent connections
843 BOOL PConfigSectionsPage::Post(PHTTPRequest & request,
844 const PStringToString & data,
845 PHTML & reply)
847 BOOL retval = PHTTPConfigSectionList::Post(request, data, reply);
848 if (request.code == PHTTP::RequestOK)
849 process.BeginRestartSystem();
850 return retval;
854 BOOL PConfigSectionsPage::GetExpirationDate(PTime & when)
856 // Well and truly before now....
857 when = ImmediateExpiryTime;
858 return TRUE;
862 //////////////////////////////////////////////////////////////
864 PRegisterPage::PRegisterPage(PHTTPServiceProcess & app,
865 const PHTTPAuthority & auth)
866 : PConfigPage(app, "register.html", "Secured Options", auth),
867 process(app)
872 PString PRegisterPage::LoadText(PHTTPRequest & request)
874 if (fields.GetSize() > 0)
875 return PConfigPage::LoadText(request);
877 PString mailURL = "mailto:" + process.GetEMailAddress();
878 PString orderURL = mailURL;
879 PString tempURL = mailURL;
880 if (process.GetHomePage() == HOME_PAGE) {
881 orderURL = "https://home.equival.com.au/purchase.html";
882 tempURL = "http://www.equival.com/" + process.GetName().ToLower() + "/register.html";
883 tempURL.Replace(" ", "", TRUE);
886 PServiceHTML regPage(process.GetName() & "Registration", NULL);
887 regPage << "<!--#registration start Permanent-->"
888 "Your registration key is permanent.<p>"
889 "Do not change your registration details or your key will not "
890 "operate correctly.<p>"
891 "If you need to "
892 << PHTML::HotLink(orderURL)
893 << "upgrade"
894 << PHTML::HotLink()
895 << " or "
896 << PHTML::HotLink(mailURL)
897 << "change"
898 << PHTML::HotLink()
899 << " your registration, then you may enter the new values sent "
900 << " to you from "
901 << process.GetManufacturer()
902 << " into the fields "
903 "below, and then press the Accept button.<p>"
904 << PHTML::HRule()
905 << "<!--#registration end Permanent-->"
906 "<!--#registration start Temporary-->"
907 "Your registration key is temporary and will expire on "
908 "<!--#registration ExpiryDate-->.<p>"
909 "Do not change your registration details or your key will not "
910 "operate correctly.<p>"
911 "You may "
912 << PHTML::HotLink(orderURL)
913 << "order a permanent key"
914 << PHTML::HotLink()
915 << " and enter the new values sent to you from "
916 << process.GetManufacturer()
917 << " into the fields below, and then press the Accept button.<p>"
918 << PHTML::HRule()
919 << "<!--#registration end Temporary-->"
920 "<!--#registration start Expired-->"
921 "Your temporary registration key has expired.<p>"
922 "You may "
923 << PHTML::HotLink(orderURL)
924 << "order a permanent key"
925 << PHTML::HotLink()
926 << " and enter the new values sent to you from "
927 << process.GetManufacturer()
928 << " into the fields below, and then press the Accept button.<P>"
929 << PHTML::HRule()
930 << "<!--#registration end Expired-->";
932 PSecureConfig securedConf(process.GetProductKey(), process.GetSecuredKeys());
933 PString prefix;
934 if (securedConf.GetValidation() != PSecureConfig::IsValid)
935 prefix = securedConf.GetPendingPrefix();
937 AddFields(prefix);
939 Add(new PHTTPStringField("Validation", 40));
940 BuildHTML(regPage, InsertIntoHTML);
942 regPage << "<!--#registration start Invalid-->"
943 "You have entered the values sent to you from "
944 << process.GetManufacturer()
945 << " incorrectly. Please enter them again. Note, "
946 << PHTML::Emphasis() << PHTML::Strong() << "all" << PHTML::Strong() << PHTML::Emphasis()
947 << "the fields must be entered "
948 << PHTML::Emphasis() << PHTML::Strong() << "exactly" << PHTML::Strong() << PHTML::Emphasis()
949 << " as they appear in the e-mail from "
950 << process.GetManufacturer()
951 << ". We strongly recommend using copy and paste of all the fields, and then "
952 "press the Accept button."
953 "<!--#registration end Invalid-->"
954 "<!--#registration start Default-->"
955 "You may "
956 << PHTML::HotLink(orderURL)
957 << "order a permanent key"
958 << PHTML::HotLink()
959 << " or "
960 << PHTML::HotLink(tempURL)
961 << "obtain a temporary key"
962 << PHTML::HotLink()
963 << " and enter the values sent to you from "
964 << process.GetManufacturer()
965 << " into the fields above, and then press the Accept button.<p>"
966 "<!--#registration end Default-->"
967 << PHTML::HRule()
968 << PHTML::Heading(3) << "Disclaimer" << PHTML::Heading(3)
969 << PHTML::Paragraph() << PHTML::Bold()
970 << "The information and code herein is provided \"as is\" "
971 "without warranty of any kind, either expressed or implied, "
972 "including but not limited to the implied warrenties of "
973 "merchantability and fitness for a particular purpose. In "
974 "no event shall " << process.GetManufacturer() << " be liable "
975 "for any damages whatsoever including direct, indirect, "
976 "incidental, consequential, loss of business profits or special "
977 "damages, even if " << process.GetManufacturer() << " has been "
978 "advised of the possibility of such damages."
979 << PHTML::Bold() << PHTML::Paragraph()
980 << process.GetCopyrightText()
981 << PHTML::Body();
983 SetString(regPage);
984 return PConfigPage::LoadText(request);
988 static BOOL FindSpliceBlock(const PRegularExpression & regex,
989 const PString & text,
990 PINDEX & pos,
991 PINDEX & len,
992 PINDEX & start,
993 PINDEX & finish)
995 if (!text.FindRegEx(regex, pos, len, 0))
996 return FALSE;
998 PINDEX endpos, endlen;
999 static PRegularExpression EndBlock("<?!--#registration[ \t\n]*end[ \t\n]*[a-z]*[ \t\n]*-->?",
1000 PRegularExpression::Extended|PRegularExpression::IgnoreCase);
1001 if (text.FindRegEx(EndBlock, endpos, endlen, pos)) {
1002 start = pos+len;
1003 finish = endpos-1;
1004 len = endpos - pos + endlen;
1007 return TRUE;
1012 void PRegisterPage::OnLoadedText(PHTTPRequest & request, PString & text)
1014 PString block;
1015 PINDEX pos, len, start = 0, finish = 0;
1016 PSecureConfig securedConf(process.GetProductKey(), process.GetSecuredKeys());
1017 PTime expiry = securedConf.GetTime(securedConf.GetExpiryDateKey());
1019 static PRegularExpression Default("<?!--#registration[ \t\n]*start[ \t\n]*Default[ \t\n]*-->?",
1020 PRegularExpression::Extended|PRegularExpression::IgnoreCase);
1021 static PRegularExpression Permanent("<?!--#registration[ \t\n]*start[ \t\n]*Permanent[ \t\n]*-->?",
1022 PRegularExpression::Extended|PRegularExpression::IgnoreCase);
1023 static PRegularExpression Temporary("<?!--#registration[ \t\n]*start[ \t\n]*Temporary[ \t\n]*-->?",
1024 PRegularExpression::Extended|PRegularExpression::IgnoreCase);
1025 static PRegularExpression Expired("<?!--#registration[ \t\n]*start[ \t\n]*Expired[ \t\n]*-->?",
1026 PRegularExpression::Extended|PRegularExpression::IgnoreCase);
1027 static PRegularExpression Invalid("<?!--#registration[ \t\n]*start[ \t\n]*Invalid[ \t\n]*-->?",
1028 PRegularExpression::Extended|PRegularExpression::IgnoreCase);
1029 static PRegularExpression Pending("name[ \t\n]*=[ \t\n]*\"" +
1030 securedConf.GetPendingPrefix() +
1031 "[^\"]+\"",
1032 PRegularExpression::Extended|PRegularExpression::IgnoreCase);
1034 PServiceHTML::ProcessMacros(request, text,
1035 GetURL().AsString(PURL::PathOnly).Mid(1),
1036 PServiceHTML::LoadFromFile);
1038 switch (securedConf.GetValidation()) {
1039 case PSecureConfig::Defaults :
1040 while (FindSpliceBlock(Default, text, pos, len, start, finish))
1041 text.Splice(text(start, finish), pos, len);
1042 while (FindSpliceBlock(Permanent, text, pos, len, start, finish))
1043 text.Delete(pos, len);
1044 while (FindSpliceBlock(Temporary, text, pos, len, start, finish))
1045 text.Delete(pos, len);
1046 while (FindSpliceBlock(Expired, text, pos, len, start, finish))
1047 text.Delete(pos, len);
1048 while (FindSpliceBlock(Invalid, text, pos, len, start, finish))
1049 text.Delete(pos, len);
1050 break;
1052 case PSecureConfig::Invalid :
1053 case PSecureConfig::Pending :
1054 while (FindSpliceBlock(Default, text, pos, len, start, finish))
1055 text.Delete(pos, len);
1056 while (FindSpliceBlock(Permanent, text, pos, len, start, finish))
1057 text.Delete(pos, len);
1058 while (FindSpliceBlock(Temporary, text, pos, len, start, finish))
1059 text.Delete(pos, len);
1060 while (FindSpliceBlock(Expired, text, pos, len, start, finish))
1061 text.Delete(pos, len);
1062 while (FindSpliceBlock(Invalid, text, pos, len, start, finish))
1063 text.Splice(text(start, finish), pos, len);
1064 break;
1066 case PSecureConfig::Expired :
1067 while (FindSpliceBlock(Default, text, pos, len, start, finish))
1068 text.Delete(pos, len);
1069 while (FindSpliceBlock(Permanent, text, pos, len, start, finish))
1070 text.Delete(pos, len);
1071 while (FindSpliceBlock(Temporary, text, pos, len, start, finish))
1072 text.Delete(pos, len);
1073 while (FindSpliceBlock(Expired, text, pos, len, start, finish))
1074 text.Splice(text(start, finish), pos, len);
1075 while (FindSpliceBlock(Invalid, text, pos, len, start, finish))
1076 text.Delete(pos, len);
1077 break;
1079 case PSecureConfig::IsValid :
1080 while (text.FindRegEx(Pending, pos, len)) {
1081 static PINDEX pendingLength = securedConf.GetPendingPrefix().GetLength();
1082 text.Delete(text.Find('"', pos)+1, pendingLength);
1083 start = pos + len - pendingLength;
1085 if (expiry.GetYear() < 2011) {
1086 while (FindSpliceBlock(Default, text, pos, len, start, finish))
1087 text.Delete(pos, len);
1088 while (FindSpliceBlock(Permanent, text, pos, len, start, finish))
1089 text.Delete(pos, len);
1090 while (FindSpliceBlock(Temporary, text, pos, len, start, finish))
1091 text.Splice(text(start, finish), pos, len);
1092 while (FindSpliceBlock(Expired, text, pos, len, start, finish))
1093 text.Delete(pos, len);
1094 while (FindSpliceBlock(Invalid, text, pos, len, start, finish))
1095 text.Delete(pos, len);
1097 else {
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.Splice(text(start, finish), pos, len);
1102 while (FindSpliceBlock(Temporary, text, pos, len, start, finish))
1103 text.Delete(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);
1111 static PRegularExpression ExpiryDate("<?!--#registration[ \t\n]*ExpiryDate[ \t\n]*-->?",
1112 PRegularExpression::Extended|PRegularExpression::IgnoreCase);
1113 while (text.FindRegEx(ExpiryDate, pos, len, 0))
1114 text.Splice(expiry.AsString(PTime::LongDate), pos, len);
1116 PHTTPConfig::OnLoadedText(request, text);
1117 PServiceHTML::ProcessMacros(request, text, "", PServiceHTML::NoOptions);
1121 BOOL PRegisterPage::Post(PHTTPRequest & request,
1122 const PStringToString & data,
1123 PHTML & reply)
1125 if (fields.GetSize() == 0)
1126 LoadText(request);
1128 BOOL retval = PHTTPConfig::Post(request, data, reply);
1129 if (request.code != PHTTP::RequestOK)
1130 return FALSE;
1132 PSecureConfig sconf(process.GetProductKey(), process.GetSecuredKeys());
1133 switch (sconf.GetValidation()) {
1134 case PSecureConfig::Defaults :
1135 sconf.ResetPending();
1136 break;
1138 case PSecureConfig::IsValid :
1139 break;
1141 case PSecureConfig::Pending :
1142 sconf.ValidatePending();
1143 break;
1145 default :
1146 sconf.ResetPending();
1149 RemoveAllFields();
1150 LoadText(request);
1151 OnLoadedText(request, reply);
1153 return retval;
1157 ///////////////////////////////////////////////////////////////////
1159 static void DigestSecuredKeys(PHTTPServiceProcess & process,
1160 PString & reginfo,
1161 PHTML * html)
1163 const PStringArray & securedKeys = process.GetSecuredKeys();
1164 PSecureConfig sconf(process.GetProductKey(), securedKeys);
1166 PString prefix;
1167 if (sconf.GetValidation() != PSecureConfig::IsValid)
1168 prefix = sconf.GetPendingPrefix();
1170 PMessageDigest5 digestor;
1172 PStringStream info;
1173 info << '"' << process.GetName() << "\" ===";
1175 PINDEX i;
1176 for (i = 0; i < securedKeys.GetSize(); i++) {
1177 PString val = sconf.GetString(prefix + securedKeys[i]).Trim();
1178 info << " \"" << val << '"';
1179 if (html != NULL)
1180 *html << PHTML::HiddenField(securedKeys[i], val);
1181 digestor.Process(val);
1184 PString digest = digestor.Complete();
1185 if (html != NULL)
1186 *html << PHTML::HiddenField("digest", digest);
1188 info.Replace("===", digest);
1189 reginfo = info;
1193 ///////////////////////////////////////////////////////////////////
1195 PServiceHTML::PServiceHTML(const char * title, const char * help, const char * helpGif)
1197 PHTTPServiceProcess::Current().GetPageHeader(*this, title);
1199 *this << PHTML::Heading(1) << title;
1201 if (help != NULL)
1202 *this << "&nbsp;"
1203 << PHTML::HotLink(help)
1204 << PHTML::Image(helpGif, "Help", 48, 23, "align=absmiddle")
1205 << PHTML::HotLink();
1207 *this << PHTML::Heading(1) << PHTML::Paragraph();
1211 PString PServiceHTML::ExtractSignature(PString & out)
1213 return ExtractSignature(*this, out);
1217 PString PServiceHTML::ExtractSignature(const PString & html,
1218 PString & out,
1219 const char * keyword)
1221 out = html;
1223 PRegularExpression SignatureRegEx("<?!--" + PString(keyword) + "[ \t\r\n]+"
1224 "signature[ \t\r\n]+(-?[^-])+-->?",
1225 PRegularExpression::Extended|PRegularExpression::IgnoreCase);
1227 PINDEX pos, len;
1228 if (out.FindRegEx(SignatureRegEx, pos, len)) {
1229 PString tag = out.Mid(pos, len);
1230 out.Delete(pos, len);
1231 return tag(tag.Find("signature")+10, tag.FindLast('-')-2).Trim();
1234 return PString::Empty();
1238 PString PServiceHTML::CalculateSignature()
1240 return CalculateSignature(*this);
1244 PString PServiceHTML::CalculateSignature(const PString & out)
1246 return CalculateSignature(out, PHTTPServiceProcess::Current().GetSignatureKey());
1250 PString PServiceHTML::CalculateSignature(const PString & out,
1251 const PTEACypher::Key & sig)
1253 // calculate the MD5 digest of the HTML data
1254 PMessageDigest5 digestor;
1256 PINDEX p1 = 0;
1257 PINDEX p2;
1258 while ((p2 = out.FindOneOf("\r\n", p1)) != P_MAX_INDEX) {
1259 if (p2 > p1)
1260 digestor.Process(out(p1, p2-1));
1261 digestor.Process("\r\n", 2);
1262 p1 = p2 + 1;
1263 if (out[p2] == '\r' && out[p1] == '\n') // CR LF pair
1264 p1++;
1266 digestor.Process(out(p1, P_MAX_INDEX));
1268 PMessageDigest5::Code md5;
1269 digestor.Complete(md5);
1271 // encode it
1272 PTEACypher cypher(sig);
1273 BYTE buf[sizeof(md5)+7];
1274 memcpy(buf, &md5, sizeof(md5));
1275 memset(&buf[sizeof(md5)], 0, sizeof(buf)-sizeof(md5));
1276 return cypher.Encode(buf, sizeof(buf));
1280 BOOL PServiceHTML::CheckSignature()
1282 return CheckSignature(*this);
1286 BOOL PServiceHTML::CheckSignature(const PString & html)
1288 if (PHTTPServiceProcess::Current().ShouldIgnoreSignatures())
1289 return TRUE;
1291 // extract the signature from the file
1292 PString out;
1293 PString signature = ExtractSignature(html, out);
1295 // calculate the signature on the data
1296 PString checkSignature = CalculateSignature(out);
1298 // return TRUE or FALSE
1299 return checkSignature == signature;
1303 static BOOL FindBrackets(const PString & args, PINDEX & open, PINDEX & close)
1305 open = args.FindOneOf("[{(", close);
1306 if (open == P_MAX_INDEX)
1307 return FALSE;
1309 switch (args[open]) {
1310 case '[' :
1311 close = args.Find(']', open+1);
1312 break;
1313 case '{' :
1314 close = args.Find('}', open+1);
1315 break;
1316 case '(' :
1317 close = args.Find(')', open+1);
1318 break;
1320 return close != P_MAX_INDEX;
1324 static BOOL ExtractVariables(const PString & args,
1325 PString & variable,
1326 PString & value)
1328 PINDEX open;
1329 PINDEX close = 0;
1330 if (FindBrackets(args, open, close))
1331 variable = args(open+1, close-1);
1332 else {
1333 variable = args.Trim();
1334 close = P_MAX_INDEX-1;
1336 if (variable.IsEmpty())
1337 return FALSE;
1339 if (FindBrackets(args, open, close))
1340 value = args(open+1, close-1);
1342 return TRUE;
1346 ///////////////////////////////////////////////////////////////////////////////
1348 PServiceMacro * PServiceMacro::list;
1351 PServiceMacro::PServiceMacro(const char * name, BOOL isBlock)
1353 macroName = name;
1354 isMacroBlock = isBlock;
1355 link = list;
1356 list = this;
1360 PServiceMacro::PServiceMacro(const PCaselessString & name, BOOL isBlock)
1362 macroName = name;
1363 isMacroBlock = isBlock;
1367 PObject::Comparison PServiceMacro::Compare(const PObject & obj) const
1369 PAssert(obj.IsDescendant(PServiceMacro::Class()), PInvalidCast);
1370 const PServiceMacro & other = (const PServiceMacro &)obj;
1372 if (isMacroBlock != other.isMacroBlock)
1373 return isMacroBlock ? GreaterThan : LessThan;
1375 int cmp = strcasecmp(macroName, other.macroName);
1376 if (cmp < 0)
1377 return LessThan;
1378 if (cmp > 0)
1379 return GreaterThan;
1380 return EqualTo;
1384 PString PServiceMacro::Translate(PHTTPRequest &, const PString &, const PString &) const
1386 return PString::Empty();
1391 PServiceMacros_list::PServiceMacros_list()
1393 DisallowDeleteObjects();
1394 PServiceMacro * macro = PServiceMacro::list;
1395 while (macro != NULL) {
1396 Append(macro);
1397 macro = macro->link;
1402 PCREATE_SERVICE_MACRO(Header,request,P_EMPTY)
1404 PString hdr = PHTTPServiceProcess::Current().GetPageGraphic();
1405 PServiceHTML::ProcessMacros(request, hdr, "header.html",
1406 PServiceHTML::LoadFromFile|PServiceHTML::NoURLOverride);
1407 return hdr;
1411 PCREATE_SERVICE_MACRO(Copyright,P_EMPTY,P_EMPTY)
1413 return PHTTPServiceProcess::Current().GetCopyrightText();
1417 PCREATE_SERVICE_MACRO(ProductName,P_EMPTY,P_EMPTY)
1419 return PHTTPServiceProcess::Current().GetProductName();
1423 PCREATE_SERVICE_MACRO(Manufacturer,P_EMPTY,P_EMPTY)
1425 return PHTTPServiceProcess::Current().GetManufacturer();
1429 PCREATE_SERVICE_MACRO(Version,P_EMPTY,P_EMPTY)
1431 return PHTTPServiceProcess::Current().GetVersion(TRUE);
1435 PCREATE_SERVICE_MACRO(BuildDate,P_EMPTY,args)
1437 const PTime & date = PHTTPServiceProcess::Current().GetCompilationDate();
1438 if (args.IsEmpty())
1439 return date.AsString("d MMMM yyyy");
1441 return date.AsString(args);
1445 PCREATE_SERVICE_MACRO(OS,P_EMPTY,P_EMPTY)
1447 return PHTTPServiceProcess::Current().GetOSClass() &
1448 PHTTPServiceProcess::Current().GetOSName();
1452 PCREATE_SERVICE_MACRO(Machine,P_EMPTY,P_EMPTY)
1454 return PHTTPServiceProcess::Current().GetOSVersion() + '-' +
1455 PHTTPServiceProcess::Current().GetOSHardware();
1459 PCREATE_SERVICE_MACRO(LongDateTime,P_EMPTY,P_EMPTY)
1461 return PTime().AsString(PTime::LongDateTime);
1465 PCREATE_SERVICE_MACRO(LongDate,P_EMPTY,P_EMPTY)
1467 return PTime().AsString(PTime::LongDate);
1471 PCREATE_SERVICE_MACRO(LongTime,P_EMPTY,P_EMPTY)
1473 return PTime().AsString(PTime::LongTime);
1477 PCREATE_SERVICE_MACRO(MediumDateTime,P_EMPTY,P_EMPTY)
1479 return PTime().AsString(PTime::MediumDateTime);
1483 PCREATE_SERVICE_MACRO(MediumDate,P_EMPTY,P_EMPTY)
1485 return PTime().AsString(PTime::MediumDate);
1489 PCREATE_SERVICE_MACRO(ShortDateTime,P_EMPTY,P_EMPTY)
1491 return PTime().AsString(PTime::ShortDateTime);
1495 PCREATE_SERVICE_MACRO(ShortDate,P_EMPTY,P_EMPTY)
1497 return PTime().AsString(PTime::ShortDate);
1501 PCREATE_SERVICE_MACRO(ShortTime,P_EMPTY,P_EMPTY)
1503 return PTime().AsString(PTime::ShortTime);
1507 PCREATE_SERVICE_MACRO(Time,P_EMPTY,args)
1509 PTime now;
1510 if (args.IsEmpty())
1511 return now.AsString();
1513 return now.AsString(args);
1517 PCREATE_SERVICE_MACRO(StartTime,P_EMPTY,P_EMPTY)
1519 return PProcess::Current().GetStartTime().AsString(PTime::MediumDateTime);
1523 PCREATE_SERVICE_MACRO(UpTime,P_EMPTY,P_EMPTY)
1525 PTimeInterval upTime = PTime() - PProcess::Current().GetStartTime();
1526 return upTime.AsString(0, PTimeInterval::IncludeDays);
1530 PCREATE_SERVICE_MACRO(LocalHost,request,P_EMPTY)
1532 if (request.localAddr != 0)
1533 return PIPSocket::GetHostName(request.localAddr);
1534 else
1535 return PIPSocket::GetHostName();
1539 PCREATE_SERVICE_MACRO(LocalIP,request,P_EMPTY)
1541 if (request.localAddr != 0)
1542 return request.localAddr;
1543 else
1544 return "127.0.0.1";
1548 PCREATE_SERVICE_MACRO(LocalPort,request,P_EMPTY)
1550 if (request.localPort != 0)
1551 return psprintf("%u", request.localPort);
1552 else
1553 return "80";
1557 PCREATE_SERVICE_MACRO(PeerHost,request,P_EMPTY)
1559 if (request.origin != 0)
1560 return PIPSocket::GetHostName(request.origin);
1561 else
1562 return "N/A";
1566 PCREATE_SERVICE_MACRO(PeerIP,request,P_EMPTY)
1568 if (request.origin != 0)
1569 return request.origin;
1570 else
1571 return "N/A";
1574 PCREATE_SERVICE_MACRO(MonitorInfo,request,P_EMPTY)
1576 const PTime & compilationDate = PHTTPServiceProcess::Current().GetCompilationDate();
1578 PString peerAddr = "N/A";
1579 if (request.origin != 0)
1580 peerAddr = request.origin.AsString();
1582 PString localAddr = "127.0.0.1";
1583 if (request.localAddr != 0)
1584 localAddr = request.localAddr.AsString();
1586 WORD localPort = 80;
1587 if (request.localPort != 0)
1588 localPort = request.localPort;
1590 PString timeFormat = "yyyyMMdd hhmmss z";
1592 PTime now;
1593 PTimeInterval upTime = now - PProcess::Current().GetStartTime();
1595 PStringStream monitorText;
1596 monitorText << "Program: " << PHTTPServiceProcess::Current().GetProductName() << "\n"
1597 << "Version: " << PHTTPServiceProcess::Current().GetVersion(TRUE) << "\n"
1598 << "Manufacturer: " << PHTTPServiceProcess::Current().GetManufacturer() << "\n"
1599 << "OS: " << PHTTPServiceProcess::Current().GetOSClass() << " " << PHTTPServiceProcess::Current().GetOSName() << "\n"
1600 << "OS Version: " << PHTTPServiceProcess::Current().GetOSVersion() << "\n"
1601 << "Hardware: " << PHTTPServiceProcess::Current().GetOSHardware() << "\n"
1602 << "Compilation date: " << compilationDate.AsString(timeFormat, PTime::GMT) << "\n"
1603 << "Start Date: " << PProcess::Current().GetStartTime().AsString(timeFormat, PTime::GMT) << "\n"
1604 << "Current Date: " << now.AsString(timeFormat, PTime::GMT) << "\n"
1605 << "Up time: " << upTime << "\n"
1606 << "Peer Addr: " << peerAddr << "\n"
1607 << "Local Host: " << PIPSocket::GetHostName() << "\n"
1608 << "Local Addr: " << localAddr << "\n"
1609 << "Local Port: " << localPort << "\n"
1612 return monitorText;
1616 PCREATE_SERVICE_MACRO(RegInfo,P_EMPTY,P_EMPTY)
1618 PString subs;
1619 DigestSecuredKeys(PHTTPServiceProcess::Current(), subs, NULL);
1620 return subs;
1624 static PString GetRegInfo(const char * info)
1626 PHTTPServiceProcess & process = PHTTPServiceProcess::Current();
1627 PSecureConfig sconf(process.GetProductKey(), process.GetSecuredKeys());
1628 PString pending = sconf.GetPendingPrefix();
1629 return sconf.GetString(info, sconf.GetString(pending+info));
1632 PCREATE_SERVICE_MACRO(RegUser,P_EMPTY,P_EMPTY)
1634 return GetRegInfo("Name");
1638 PCREATE_SERVICE_MACRO(RegCompany,P_EMPTY,P_EMPTY)
1640 return GetRegInfo("Company");
1644 PCREATE_SERVICE_MACRO(RegEmail,P_EMPTY,P_EMPTY)
1646 return GetRegInfo("EMail");
1650 PCREATE_SERVICE_MACRO(Registration,P_EMPTY,args)
1652 PHTTPServiceProcess & process = PHTTPServiceProcess::Current();
1653 PSecureConfig sconf(process.GetProductKey(), process.GetSecuredKeys());
1654 PString pending = sconf.GetPendingPrefix();
1656 PString regNow = "Register Now!";
1657 PString viewReg = "View Registration";
1658 PString demoCopy = "Unregistered Demonstration Copy";
1659 PINDEX open;
1660 PINDEX close = 0;
1661 if (FindBrackets(args, open, close)) {
1662 regNow = args(open+1, close-1);
1663 if (FindBrackets(args, open, close)) {
1664 viewReg = args(open+1, close-1);
1665 if (FindBrackets(args, open, close))
1666 demoCopy = args(open+1, close-1);
1670 PHTML out(PHTML::InBody);
1671 out << "<font size=5>"
1672 << sconf.GetString("Name", sconf.GetString(pending+"Name", "*** "+demoCopy+" ***"))
1673 << PHTML::BreakLine()
1674 << "<font size=4>"
1675 << sconf.GetString("Company", sconf.GetString(pending+"Company"))
1676 << PHTML::BreakLine()
1677 << PHTML::BreakLine()
1678 << "<font size=3>";
1680 if (sconf.GetString("Name").IsEmpty())
1681 process.AddUnregisteredText(out);
1682 else
1683 process.AddRegisteredText(out);
1685 out << PHTML::HotLink("/register.html")
1686 << (sconf.GetString("Name").IsEmpty() ? regNow : viewReg)
1687 << PHTML::HotLink();
1688 return out;
1692 PCREATE_SERVICE_MACRO(InputsFromQuery,request,P_EMPTY)
1694 PStringToString vars = request.url.GetQueryVars();
1695 PStringStream subs;
1696 for (PINDEX i = 0; i < vars.GetSize(); i++)
1697 subs << "<INPUT TYPE=hidden NAME=\"" << vars.GetKeyAt(i)
1698 << "\" VALUE=\"" << vars.GetDataAt(i) << "\">\r\n";
1699 return subs;
1703 PCREATE_SERVICE_MACRO(Query,request,args)
1705 if (args.IsEmpty())
1706 return request.url.GetQuery();
1708 PString variable, value;
1709 if (ExtractVariables(args, variable, value)) {
1710 value = request.url.GetQueryVars()(variable, value);
1711 if (!value)
1712 return value;
1714 return PString::Empty();
1718 PCREATE_SERVICE_MACRO(Get,request,args)
1720 PString variable, value;
1721 if (ExtractVariables(args, variable, value)) {
1722 PString section = request.url.GetQueryVars()("section");
1723 PINDEX slash = variable.FindLast('\\');
1724 if (slash != P_MAX_INDEX) {
1725 section += variable.Left(slash);
1726 variable = variable.Mid(slash+1);
1728 if (!section && !variable) {
1729 PConfig config(section);
1730 return config.GetString(variable, value);
1733 return PString::Empty();
1737 PCREATE_SERVICE_MACRO(URL,request,P_EMPTY)
1739 return request.url.AsString();
1743 PCREATE_SERVICE_MACRO(Include,P_EMPTY,args)
1745 PString text;
1747 if (!args) {
1748 PFile file;
1749 if (file.Open(args, PFile::ReadOnly))
1750 text = file.ReadString(file.GetLength());
1753 return text;
1757 PCREATE_SERVICE_MACRO(SignedInclude,P_EMPTY,args)
1759 PString text;
1761 if (!args) {
1762 PFile file;
1763 if (file.Open(args, PFile::ReadOnly)) {
1764 text = file.ReadString(file.GetLength());
1765 if (!PServiceHTML::CheckSignature(text)) {
1766 PHTTPServiceProcess & process = PHTTPServiceProcess::Current();
1767 PHTML html("Invalid OEM Signature");
1768 html << "The HTML file \""
1769 << args
1770 << "\" contains an invalid signature for \""
1771 << process.GetName()
1772 << "\" by \""
1773 << process.GetManufacturer()
1774 << '"'
1775 << PHTML::Body();
1776 text = html;
1781 return text;
1784 PCREATE_SERVICE_MACRO_BLOCK(IfQuery,request,args,block)
1786 PStringToString vars = request.url.GetQueryVars();
1788 PINDEX space = args.FindOneOf(" \t\r\n");
1789 PString var = args.Left(space);
1790 PString value = args.Mid(space).LeftTrim();
1792 BOOL ok;
1793 if (value.IsEmpty())
1794 ok = vars.Contains(var);
1795 else {
1796 PString operation;
1797 space = value.FindOneOf(" \t\r\n");
1798 if (space != P_MAX_INDEX) {
1799 operation = value.Left(space);
1800 value = value.Mid(space).LeftTrim();
1803 PString query = vars(var);
1804 if (operation == "!=")
1805 ok = query != value;
1806 else if (operation == "<")
1807 ok = query < value;
1808 else if (operation == ">")
1809 ok = query > value;
1810 else if (operation == "<=")
1811 ok = query <= value;
1812 else if (operation == ">=")
1813 ok = query >= value;
1814 else if (operation == "*=")
1815 ok = (query *= value);
1816 else
1817 ok = query == value;
1820 return ok ? block : PString::Empty();
1824 PCREATE_SERVICE_MACRO_BLOCK(IfInURL,request,args,block)
1826 if (request.url.AsString().Find(args) != P_MAX_INDEX)
1827 return block;
1829 return PString::Empty();
1833 PCREATE_SERVICE_MACRO_BLOCK(IfNotInURL,request,args,block)
1835 if (request.url.AsString().Find(args) == P_MAX_INDEX)
1836 return block;
1838 return PString::Empty();
1842 static void SplitCmdAndArgs(const PString & text, PINDEX pos, PCaselessString & cmd, PString & args)
1844 static const char whitespace[] = " \t\r\n";
1845 PString macro = text(text.FindOneOf(whitespace, pos)+1, text.Find("--", pos+3)-1).Trim();
1846 PINDEX endCmd = macro.FindOneOf(whitespace);
1847 if (endCmd == P_MAX_INDEX) {
1848 cmd = macro;
1849 args.MakeEmpty();
1851 else {
1852 cmd = macro.Left(endCmd);
1853 args = macro.Mid(endCmd+1).LeftTrim();
1858 BOOL PServiceHTML::ProcessMacros(PHTTPRequest & request,
1859 PString & text,
1860 const PString & defaultFile,
1861 unsigned options)
1863 PINDEX alreadyLoadedPrefixLength = 0;
1865 PString filename = defaultFile;
1866 if ((options&LoadFromFile) != 0) {
1867 if ((options&NoURLOverride) == 0) {
1868 filename = request.url.GetParameters();
1869 if (filename.IsEmpty())
1870 filename = defaultFile;
1873 if (!filename) {
1874 PString alreadyLoaded = "<!--#loadedfrom " + filename + "-->\r\n";
1875 alreadyLoadedPrefixLength = alreadyLoaded.GetLength();
1877 if (text.Find(alreadyLoaded) != 0) {
1878 PFile file;
1879 if (file.Open(filename, PFile::ReadOnly)) {
1880 text = alreadyLoaded + file.ReadString(file.GetLength());
1881 if ((options&NoSignatureForFile) == 0)
1882 options |= NeedSignature;
1888 if ((options&NeedSignature) != 0) {
1889 if (!CheckSignature(text.Mid(alreadyLoadedPrefixLength))) {
1890 PHTTPServiceProcess & process = PHTTPServiceProcess::Current();
1891 PHTML html("Invalid OEM Signature");
1892 html << "The HTML file \""
1893 << filename
1894 << "\" contains an invalid signature for \""
1895 << process.GetName()
1896 << "\" by \""
1897 << process.GetManufacturer()
1898 << '"'
1899 << PHTML::Body();
1900 text = html;
1901 return FALSE;
1905 static PServiceMacros_list ServiceMacros;
1907 PHTTPServiceProcess & process = PHTTPServiceProcess::Current();
1909 PRegularExpression StartBlockRegEx("<?!--#(equival|" + process.GetMacroKeyword() + ")"
1910 "start[ \t\r\n]+(-?[^-])+-->?",
1911 PRegularExpression::Extended|PRegularExpression::IgnoreCase);
1913 PRegularExpression MacroRegEx("<?!--#(equival|" + process.GetMacroKeyword() + ")[ \t\r\n]+(-?[^-])+-->?",
1914 PRegularExpression::Extended|PRegularExpression::IgnoreCase);
1916 BOOL substitedMacro;
1917 do {
1918 substitedMacro = FALSE;
1920 PINDEX pos = 0;
1921 PINDEX len;
1922 while (text.FindRegEx(StartBlockRegEx, pos, len, pos)) {
1923 PString substitution;
1925 PCaselessString cmd;
1926 PString args;
1927 SplitCmdAndArgs(text, pos, cmd, args);
1928 PINDEX idx = ServiceMacros.GetValuesIndex(PServiceMacro(cmd, TRUE));
1929 if (idx != P_MAX_INDEX) {
1930 PRegularExpression EndBlockRegEx("<?!--#(equival|" + process.GetMacroKeyword() + ")"
1931 "end[ \t\r\n]+" + cmd + "(-?[^-])*-->?",
1932 PRegularExpression::Extended|PRegularExpression::IgnoreCase);
1933 PINDEX endpos, endlen;
1934 if (text.FindRegEx(EndBlockRegEx, endpos, endlen, pos+len)) {
1935 PINDEX startpos = pos+len;
1936 len = endpos-pos + endlen;
1937 substitution = ServiceMacros[idx].Translate(request, args, text(startpos, endpos-1));
1938 substitedMacro = TRUE;
1942 text.Splice(substitution, pos, len);
1945 pos = 0;
1946 while (text.FindRegEx(MacroRegEx, pos, len, pos)) {
1947 PCaselessString cmd;
1948 PString args;
1949 SplitCmdAndArgs(text, pos, cmd, args);
1951 PString substitution;
1952 if (!process.SubstituteEquivalSequence(request, cmd & args, substitution)) {
1953 PINDEX idx = ServiceMacros.GetValuesIndex(PServiceMacro(cmd, FALSE));
1954 if (idx != P_MAX_INDEX) {
1955 substitution = ServiceMacros[idx].Translate(request, args, PString::Empty());
1956 substitedMacro = TRUE;
1960 text.Splice(substitution, pos, len);
1962 } while (substitedMacro);
1964 return TRUE;
1968 ///////////////////////////////////////////////////////////////////
1970 static void ServiceOnLoadedText(PString & text)
1972 PHTTPServiceProcess & process = PHTTPServiceProcess::Current();
1974 PString manuf = "<!--Standard_" + process.GetManufacturer() + "_Header-->";
1975 if (text.Find(manuf) != P_MAX_INDEX)
1976 text.Replace(manuf, process.GetPageGraphic(), TRUE);
1978 static const char equiv[] = "<!--Standard_Equivalence_Header-->";
1979 if (text.Find(equiv) != P_MAX_INDEX)
1980 text.Replace(equiv, process.GetPageGraphic(), TRUE);
1982 static const char copy[] = "<!--Standard_Copyright_Header-->";
1983 if (text.Find(copy) != P_MAX_INDEX)
1984 text.Replace(copy, process.GetCopyrightText(), TRUE);
1988 PString PServiceHTTPString::LoadText(PHTTPRequest & request)
1990 PString text = PHTTPString::LoadText(request);
1991 ServiceOnLoadedText(text);
1992 PServiceHTML::ProcessMacros(request, text, "", PServiceHTML::LoadFromFile);
1994 return text;
1997 BOOL PServiceHTTPString::GetExpirationDate(PTime & when)
1999 // Well and truly before now....
2000 when = ImmediateExpiryTime;
2001 return TRUE;
2005 void PServiceHTTPFile::OnLoadedText(PHTTPRequest & request, PString & text)
2007 ServiceOnLoadedText(text);
2008 PServiceHTML::ProcessMacros(request, text, GetURL().AsString(PURL::PathOnly),
2009 needSignature ? PServiceHTML::NeedSignature : PServiceHTML::NoOptions);
2012 BOOL PServiceHTTPFile::GetExpirationDate(PTime & when)
2014 // Well and truly before now....
2015 when = ImmediateExpiryTime;
2016 return TRUE;
2020 void PServiceHTTPDirectory::OnLoadedText(PHTTPRequest & request, PString & text)
2022 ServiceOnLoadedText(text);
2023 PServiceHTML::ProcessMacros(request, text, GetURL().AsString(PURL::PathOnly),
2024 needSignature ? PServiceHTML::NeedSignature : PServiceHTML::NoOptions);
2028 BOOL PServiceHTTPDirectory::GetExpirationDate(PTime & when)
2030 // Well and truly before now....
2031 when = ImmediateExpiryTime;
2032 return TRUE;
2036 ///////////////////////////////////////////////////////////////////