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
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
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
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
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
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
246 * Revision 1.27 1998/01/26 02:12:14 robertj
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
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
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
334 #pragma implementation "httpsvc.h"
338 #include <ptclib/httpsvc.h>
339 #include <ptlib/sockets.h>
342 PSORTED_LIST(PServiceMacros_base
, PServiceMacro
);
344 class PServiceMacros_list
: public PServiceMacros_base
347 PServiceMacros_list();
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();
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()
410 PHTTPServiceProcess
& PHTTPServiceProcess::Current()
412 PHTTPServiceProcess
& process
= (PHTTPServiceProcess
&)PProcess::Current();
413 PAssert(PIsDescendant(&process
, PHTTPServiceProcess
), "Not a HTTP service!");
418 BOOL
PHTTPServiceProcess::OnStart()
420 if (!Initialise("Started"))
428 void PHTTPServiceProcess::OnStop()
431 PSYSTEMLOG(Warning
, GetName() << " stopped.");
432 PServiceProcess::OnStop();
436 BOOL
PHTTPServiceProcess::OnPause()
443 void PHTTPServiceProcess::OnContinue()
445 if (Initialise("Restarted"))
454 const char * PHTTPServiceProcess::GetServiceDependencies() const
456 return "EventLog\0Tcpip\0";
461 BOOL
PHTTPServiceProcess::ListenForHTTP(WORD port
,
462 PSocket::Reusability reuse
,
465 if (httpListeningSocket
!= NULL
&&
466 httpListeningSocket
->GetPort() == port
&&
467 httpListeningSocket
->IsOpen())
470 return ListenForHTTP(new PTCPSocket(port
), reuse
, stackSize
);
474 BOOL
PHTTPServiceProcess::ListenForHTTP(PSocket
* listener
,
475 PSocket::Reusability reuse
,
478 if (httpListeningSocket
!= NULL
)
481 httpListeningSocket
= PAssertNULL(listener
);
482 if (!httpListeningSocket
->Listen(5, 0, reuse
)) {
483 PSYSTEMLOG(Debug
, "HTTPSVC\tListen on port " << httpListeningSocket
->GetPort()
484 << " failed: " << httpListeningSocket
->GetErrorText());
488 if (stackSize
> 1000)
489 new PHTTPServiceThread(stackSize
, *this);
495 void PHTTPServiceProcess::ShutdownListener()
497 if (httpListeningSocket
== NULL
)
500 if (!httpListeningSocket
->IsOpen())
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();
515 httpThreadsMutex
.Wait();
518 httpThreadsMutex
.Signal();
520 delete httpListeningSocket
;
521 httpListeningSocket
= NULL
;
525 PString
PHTTPServiceProcess::GetCopyrightText()
527 PHTML
html(PHTML::InBody
);
528 html
<< "Copyright ©"
529 << compilationDate
.AsString("yyyy") << " by "
530 << PHTML::HotLink(copyrightHomePage
)
534 << PHTML::HotLink("mailto:" + copyrightEmail
)
541 PString
PHTTPServiceProcess::GetPageGraphic()
544 if (header
.Open("header.html", PFile::ReadOnly
))
545 return header
.ReadString(header
.GetLength());
547 PHTML
html(PHTML::InBody
);
548 html
<< PHTML::TableStart()
550 << PHTML::TableData();
552 if (gifHTML
.IsEmpty())
553 html
<< PHTML::Heading(1) << productNameHTML
<< " " << PHTML::Heading(1);
557 html
<< PHTML::TableData()
558 << GetOSClass() << ' ' << GetOSName()
559 << " Version " << GetVersion(TRUE
) << PHTML::BreakLine()
560 << ' ' << GetCompilationDate().AsString("d MMMM yyyy")
561 << PHTML::BreakLine()
563 << PHTML::HotLink(manufacturersHomePage
) << GetManufacturer() << PHTML::HotLink()
565 << PHTML::HotLink("mailto:" + manufacturersEmail
) << manufacturersEmail
<< PHTML::HotLink()
573 void PHTTPServiceProcess::GetPageHeader(PHTML
& html
)
575 GetPageHeader(html
, GetName());
579 void PHTTPServiceProcess::GetPageHeader(PHTML
& html
, const PString
& title
)
581 html
<< PHTML::Title(title
)
587 PTCPSocket
* PHTTPServiceProcess::AcceptHTTP()
589 if (httpListeningSocket
== NULL
)
592 if (!httpListeningSocket
->IsOpen())
595 // get a socket when a client connects
596 PTCPSocket
* socket
= new PTCPSocket
;
597 if (socket
->Accept(*httpListeningSocket
))
600 if (socket
->GetErrorCode() != PChannel::Interrupted
)
601 PSYSTEMLOG(Error
, "Accept failed for HTTP: " << socket
->GetErrorText());
603 if (httpListeningSocket
!= NULL
&& httpListeningSocket
->IsOpen())
611 BOOL
PHTTPServiceProcess::ProcessHTTP(PTCPSocket
& socket
)
613 if (!socket
.IsOpen())
616 PHTTPServer
* server
= CreateHTTPServer(socket
);
617 if (server
== NULL
) {
618 PSYSTEMLOG(Error
, "HTTP server creation/open failed.");
623 while (server
->ProcessCommand())
626 // always close after the response has been sent
629 // if a restart was requested, then do it, but only if we are not shutting down
630 if (httpListeningSocket
->IsOpen())
631 CompleteRestartSystem();
637 void PHTTPServiceProcess::BeginRestartSystem()
639 if (restartThread
== NULL
) {
640 restartThread
= PThread::Current();
646 void PHTTPServiceProcess::CompleteRestartSystem()
648 if (restartThread
== NULL
)
651 if (restartThread
!= PThread::Current())
654 httpNameSpace
.StartWrite();
656 if (Initialise("Restart\tInitialisation"))
657 restartThread
= NULL
;
659 httpNameSpace
.EndWrite();
661 if (restartThread
!= NULL
)
666 void PHTTPServiceProcess::AddRegisteredText(PHTML
&)
671 void PHTTPServiceProcess::AddUnregisteredText(PHTML
&)
676 BOOL
PHTTPServiceProcess::SubstituteEquivalSequence(PHTTPRequest
&, const PString
&, PString
&)
682 PHTTPServer
* PHTTPServiceProcess::CreateHTTPServer(PTCPSocket
& socket
)
685 const linger ling
= { 1, 5 };
686 socket
.SetOption(SO_LINGER
, &ling
, sizeof(ling
));
689 PHTTPServer
* server
= OnCreateHTTPServer(httpNameSpace
);
691 if (server
->Open(socket
))
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"),
712 process
.httpThreadsMutex
.Wait();
713 process
.httpThreads
.Append(this);
714 process
.httpThreadsMutex
.Signal();
716 myStackSize
= stackSize
;
722 PHTTPServiceThread::~PHTTPServiceThread()
724 process
.httpThreadsMutex
.Wait();
725 process
.httpThreads
.Remove(this);
726 process
.httpThreadsMutex
.Signal();
731 void PHTTPServiceThread::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
),
760 PConfigPage::PConfigPage(PHTTPServiceProcess
& app
,
761 const PString
& section
,
762 const PHTTPAuthority
& auth
)
763 : PHTTPConfig(section
.ToLower() + ".html", section
, auth
),
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
,
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
,
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
);
809 BOOL
PConfigPage::GetExpirationDate(PTime
& when
)
811 // Well and truly before now....
812 when
= ImmediateExpiryTime
;
817 //////////////////////////////////////////////////////////////
819 PConfigSectionsPage::PConfigSectionsPage(PHTTPServiceProcess
& app
,
821 const PHTTPAuthority
& auth
,
822 const PString
& prefix
,
823 const PString
& valueName
,
824 const PURL
& editSection
,
825 const PURL
& newSection
,
826 const PString
& newTitle
,
828 : PHTTPConfigSectionList(url
, auth
, prefix
, valueName
,
829 editSection
, newSection
, newTitle
, heading
),
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
,
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
,
859 BOOL retval
= PHTTPConfigSectionList::Post(request
, data
, reply
);
860 if (request
.code
== PHTTP::RequestOK
)
861 process
.BeginRestartSystem();
866 BOOL
PConfigSectionsPage::GetExpirationDate(PTime
& when
)
868 // Well and truly before now....
869 when
= ImmediateExpiryTime
;
874 //////////////////////////////////////////////////////////////
876 PRegisterPage::PRegisterPage(PHTTPServiceProcess
& app
,
877 const PHTTPAuthority
& auth
)
878 : PConfigPage(app
, "register.html", "Secured Options", auth
),
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>"
904 << PHTML::HotLink(orderURL
)
908 << PHTML::HotLink(mailURL
)
911 << " your registration, then you may enter the new values sent "
913 << process
.GetManufacturer()
914 << " into the fields "
915 "below, and then press the Accept button.<p>"
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>"
924 << PHTML::HotLink(orderURL
)
925 << "order a permanent key"
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>"
931 << "<!--#registration end Temporary-->"
932 "<!--#registration start Expired-->"
933 "Your temporary registration key has expired.<p>"
935 << PHTML::HotLink(orderURL
)
936 << "order a permanent key"
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>"
942 << "<!--#registration end Expired-->";
944 PSecureConfig
securedConf(process
.GetProductKey(), process
.GetSecuredKeys());
946 if (securedConf
.GetValidation() != PSecureConfig::IsValid
)
947 prefix
= securedConf
.GetPendingPrefix();
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-->"
968 << PHTML::HotLink(orderURL
)
969 << "order a permanent key"
972 << PHTML::HotLink(tempURL
)
973 << "obtain a temporary key"
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-->"
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()
996 return PConfigPage::LoadText(request
);
1000 static BOOL
FindSpliceBlock(const PRegularExpression
& regex
,
1001 const PString
& text
,
1007 if (!text
.FindRegEx(regex
, pos
, len
, 0))
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
)) {
1016 len
= endpos
- pos
+ endlen
;
1024 void PRegisterPage::OnLoadedText(PHTTPRequest
& request
, PString
& text
)
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() +
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
);
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
);
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
);
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
);
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
,
1137 if (fields
.GetSize() == 0)
1140 BOOL retval
= PHTTPConfig::Post(request
, data
, reply
);
1141 if (request
.code
!= PHTTP::RequestOK
)
1144 PSecureConfig
sconf(process
.GetProductKey(), process
.GetSecuredKeys());
1145 switch (sconf
.GetValidation()) {
1146 case PSecureConfig::Defaults
:
1147 sconf
.ResetPending();
1150 case PSecureConfig::IsValid
:
1153 case PSecureConfig::Pending
:
1154 sconf
.ValidatePending();
1158 sconf
.ResetPending();
1163 OnLoadedText(request
, reply
);
1169 ///////////////////////////////////////////////////////////////////
1171 static void DigestSecuredKeys(PHTTPServiceProcess
& process
,
1175 const PStringArray
& securedKeys
= process
.GetSecuredKeys();
1176 PSecureConfig
sconf(process
.GetProductKey(), securedKeys
);
1179 if (sconf
.GetValidation() != PSecureConfig::IsValid
)
1180 prefix
= sconf
.GetPendingPrefix();
1182 PMessageDigest5 digestor
;
1185 info
<< '"' << process
.GetName() << "\" ===";
1188 for (i
= 0; i
< securedKeys
.GetSize(); i
++) {
1189 PString val
= sconf
.GetString(prefix
+ securedKeys
[i
]).Trim();
1190 info
<< " \"" << val
<< '"';
1192 *html
<< PHTML::HiddenField(securedKeys
[i
], val
);
1193 digestor
.Process(val
);
1196 PString digest
= digestor
.Complete();
1198 *html
<< PHTML::HiddenField("digest", digest
);
1200 info
.Replace("===", digest
);
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
;
1215 this_stream
<< " "
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
,
1232 const char * keyword
)
1236 PRegularExpression
SignatureRegEx("<?!--" + PString(keyword
) + "[ \t\r\n]+"
1237 "signature[ \t\r\n]+(-?[^-])+-->?",
1238 PRegularExpression::Extended
|PRegularExpression::IgnoreCase
);
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
;
1271 while ((p2
= out
.FindOneOf("\r\n", p1
)) != P_MAX_INDEX
) {
1273 digestor
.Process(out(p1
, p2
-1));
1274 digestor
.Process("\r\n", 2);
1276 if (out
[p2
] == '\r' && out
[p1
] == '\n') // CR LF pair
1279 digestor
.Process(out(p1
, P_MAX_INDEX
));
1281 PMessageDigest5::Code md5
;
1282 digestor
.Complete(md5
);
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())
1304 // extract the signature from the file
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
)
1322 switch (args
[open
]) {
1324 close
= args
.Find(']', open
+1);
1327 close
= args
.Find('}', open
+1);
1330 close
= args
.Find(')', open
+1);
1333 return close
!= P_MAX_INDEX
;
1337 static BOOL
ExtractVariables(const PString
& args
,
1343 if (FindBrackets(args
, open
, close
))
1344 variable
= args(open
+1, close
-1);
1346 variable
= args
.Trim();
1347 close
= P_MAX_INDEX
-1;
1349 if (variable
.IsEmpty())
1352 if (FindBrackets(args
, open
, close
))
1353 value
= args(open
+1, close
-1);
1359 ///////////////////////////////////////////////////////////////////////////////
1361 PServiceMacro
* PServiceMacro::list
;
1364 PServiceMacro::PServiceMacro(const char * name
, BOOL isBlock
)
1367 isMacroBlock
= isBlock
;
1373 PServiceMacro::PServiceMacro(const PCaselessString
& name
, BOOL isBlock
)
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
);
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
) {
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
);
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();
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
)
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
);
1548 return PIPSocket::GetHostName();
1552 PCREATE_SERVICE_MACRO(LocalIP
,request
,P_EMPTY
)
1554 if (request
.localAddr
!= 0)
1555 return request
.localAddr
;
1561 PCREATE_SERVICE_MACRO(LocalPort
,request
,P_EMPTY
)
1563 if (request
.localPort
!= 0)
1564 return psprintf("%u", request
.localPort
);
1570 PCREATE_SERVICE_MACRO(PeerHost
,request
,P_EMPTY
)
1572 if (request
.origin
!= 0)
1573 return PIPSocket::GetHostName(request
.origin
);
1579 PCREATE_SERVICE_MACRO(PeerIP
,request
,P_EMPTY
)
1581 if (request
.origin
!= 0)
1582 return request
.origin
;
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";
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"
1629 PCREATE_SERVICE_MACRO(RegInfo
,P_EMPTY
,P_EMPTY
)
1632 DigestSecuredKeys(PHTTPServiceProcess::Current(), subs
, NULL
);
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";
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()
1688 << sconf
.GetString("Company", sconf
.GetString(pending
+"Company"))
1689 << PHTML::BreakLine()
1690 << PHTML::BreakLine()
1693 if (sconf
.GetString("Name").IsEmpty())
1694 process
.AddUnregisteredText(out
);
1696 process
.AddRegisteredText(out
);
1698 out
<< PHTML::HotLink("/register.html")
1699 << (sconf
.GetString("Name").IsEmpty() ? regNow
: viewReg
)
1700 << PHTML::HotLink();
1705 PCREATE_SERVICE_MACRO(InputsFromQuery
,request
,P_EMPTY
)
1707 PStringToString vars
= request
.url
.GetQueryVars();
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";
1716 PCREATE_SERVICE_MACRO(Query
,request
,args
)
1719 return request
.url
.GetQuery();
1721 PString variable
, value
;
1722 if (ExtractVariables(args
, variable
, value
)) {
1723 value
= request
.url
.GetQueryVars()(variable
, 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
)
1762 if (file
.Open(args
, PFile::ReadOnly
))
1763 text
= file
.ReadString(file
.GetLength());
1770 PCREATE_SERVICE_MACRO(SignedInclude
,P_EMPTY
,args
)
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 \""
1783 << "\" contains an invalid signature for \""
1784 << process
.GetName()
1786 << process
.GetManufacturer()
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();
1806 if (value
.IsEmpty())
1807 ok
= vars
.Contains(var
);
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
== "<")
1821 else if (operation
== ">")
1823 else if (operation
== "<=")
1824 ok
= query
<= value
;
1825 else if (operation
== ">=")
1826 ok
= query
>= value
;
1827 else if (operation
== "*=")
1828 ok
= (query
*= value
);
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
)
1842 return PString::Empty();
1846 PCREATE_SERVICE_MACRO_BLOCK(IfNotInURL
,request
,args
,block
)
1848 if (request
.url
.AsString().Find(args
) == P_MAX_INDEX
)
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
) {
1865 cmd
= macro
.Left(endCmd
);
1866 args
= macro
.Mid(endCmd
+1).LeftTrim();
1871 BOOL
PServiceHTML::ProcessMacros(PHTTPRequest
& request
,
1873 const PString
& defaultFile
,
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
;
1887 PString alreadyLoaded
= "<!--#loadedfrom " + filename
+ "-->\r\n";
1888 alreadyLoadedPrefixLength
= alreadyLoaded
.GetLength();
1890 if (text
.Find(alreadyLoaded
) != 0) {
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 \""
1907 << "\" contains an invalid signature for \""
1908 << process
.GetName()
1910 << process
.GetManufacturer()
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
;
1931 substitedMacro
= FALSE
;
1935 while (text
.FindRegEx(StartBlockRegEx
, pos
, len
, pos
)) {
1936 PString substitution
;
1938 PCaselessString cmd
;
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
);
1959 while (text
.FindRegEx(MacroRegEx
, pos
, len
, pos
)) {
1960 PCaselessString cmd
;
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
);
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
);
2010 BOOL
PServiceHTTPString::GetExpirationDate(PTime
& when
)
2012 // Well and truly before now....
2013 when
= ImmediateExpiryTime
;
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
;
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
;
2049 ///////////////////////////////////////////////////////////////////