1 /*
2  * Copyright (C) 2008 Emweb bv, Herent, Belgium.
3  *
4  * All rights reserved.
5  */
6 
7 #include "Wt/WServer.h"
8 #include "Wt/WLogger.h"
9 #include "Wt/WResource.h"
10 
11 #include "Configuration.h"
12 #include "WebUtils.h"
13 
14 #include <boost/algorithm/string.hpp>
15 #ifdef WT_BOOST_CONF_LOCK
16 #include <boost/thread.hpp>
17 #endif
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <fcntl.h>
21 #include <algorithm>
22 #include <array>
23 #include <vector>
24 #include <iostream>
25 #include <fstream>
26 #include <stdlib.h>
27 #include <regex>
28 
29 #include <Wt/AsioWrapper/system_error.hpp>
30 
31 #include "3rdparty/rapidxml/rapidxml.hpp"
32 #include "3rdparty/rapidxml/rapidxml_print.hpp"
33 
34 #ifdef WT_WIN32
35 #include <io.h>
36 #include <process.h>
37 #endif
38 
39 #if defined(WT_STD_CONF_LOCK)
40 #define READ_LOCK std::shared_lock<std::shared_mutex> lock(mutex_)
41 #define WRITE_LOCK std::unique_lock<std::shared_mutex> lock(mutex_)
42 #elif defined(WT_BOOST_CONF_LOCK)
43 #define READ_LOCK boost::shared_lock<boost::shared_mutex> lock(mutex_)
44 #define WRITE_LOCK boost::lock_guard<boost::shared_mutex> lock(mutex_)
45 #else
46 #define READ_LOCK
47 #define WRITE_LOCK
48 #endif // WT_CONF_LOCK
49 
50 using namespace Wt::rapidxml;
51 
52 namespace {
53 
54 static const std::string EMPTY_STR;
55 
56 using namespace Wt;
57 
regexMatchAny(const std::string & agent,const std::vector<std::string> & regexList)58 bool regexMatchAny(const std::string& agent,
59 		   const std::vector<std::string>& regexList) {
60   WT_USTRING s = WT_USTRING::fromUTF8(agent);
61   for (unsigned i = 0; i < regexList.size(); ++i) {
62     std::regex expr(regexList[i]);
63 
64     if (std::regex_match(s.toUTF8(), expr))
65       return true;
66   }
67 
68   return false;
69 }
70 
singleChildElement(xml_node<> * element,const char * tagName)71 xml_node<> *singleChildElement(xml_node<> *element, const char* tagName)
72 {
73   xml_node<> *result = element->first_node(tagName);
74   if (result) {
75     xml_node<> *next = result->next_sibling(tagName);
76 
77     if (next) {
78       throw WServer::Exception
79 	(std::string("Expected only one child <") + tagName
80 	 + "> in <" + element->name() + ">");
81     }
82   }
83 
84   return result;
85 }
86 
attributeValue(xml_node<> * element,const char * attributeName,std::string & result)87 bool attributeValue(xml_node<> *element, const char *attributeName,
88 		    std::string& result)
89 {
90   xml_attribute<> *attr = element->first_attribute(attributeName);
91 
92   if (attr) {
93     result = attr->value();
94 
95     return true;
96   } else
97     return false;
98 }
99 
elementValue(xml_node<> * element,const char * elementName)100 std::string elementValue(xml_node<> *element, const char *elementName)
101 {
102   for (xml_node<> *e = element->first_node(); e; e = e->next_sibling())
103     if (e->type() != node_data && e->type() != node_cdata)
104       throw WServer::Exception(std::string("<")
105 			       + elementName
106 			       + "> should only contain text.");
107 
108   return element->value();
109 }
110 
singleChildElementValue(xml_node<> * element,const char * tagName,const std::string & defaultValue)111 std::string singleChildElementValue(xml_node<> *element, const char* tagName,
112 				    const std::string& defaultValue)
113 {
114   xml_node<> *child = singleChildElement(element, tagName);
115 
116   if (!child)
117     return defaultValue;
118   else
119     return elementValue(child, tagName);
120 }
121 
setBoolean(xml_node<> * element,const char * tagName,bool & result)122 void setBoolean(xml_node<> *element, const char *tagName, bool& result)
123 {
124   std::string v = singleChildElementValue(element, tagName, "");
125 
126   if (!v.empty()) {
127     if (v == "true")
128       result = true;
129     else if (v == "false")
130       result = false;
131     else
132       throw WServer::Exception("<" + std::string(tagName)
133 			       + ">: expecting 'true' or 'false'");
134   }
135 }
136 
setInt(xml_node<> * element,const char * tagName,int & result)137 void setInt(xml_node<> *element, const char *tagName, int& result)
138 {
139   std::string v = singleChildElementValue(element, tagName, "");
140 
141   if (!v.empty()) {
142     try {
143       result = Utils::stoi(v);
144     } catch (std::exception& e) {
145       throw WServer::Exception("<" + std::string(tagName)
146 			       + ">: expecting integer value");
147     }
148   }
149 }
150 
childElements(xml_node<> * element,const char * tagName)151 std::vector<xml_node<> *> childElements(xml_node<> *element,
152 					const char *tagName)
153 {
154   std::vector<xml_node<> *> result;
155 
156   for (xml_node<> *r = element->first_node(tagName); r;
157        r = r->next_sibling(tagName))
158     result.push_back(r);
159 
160   return result;
161 }
162 
163 template<std::size_t N>
prefixMatches(const std::array<unsigned char,N> & networkBytes,const std::array<unsigned char,N> & addressBytes,const unsigned char prefixLength)164 bool prefixMatches(const std::array<unsigned char, N> &networkBytes,
165                    const std::array<unsigned char, N> &addressBytes,
166                    const unsigned char prefixLength)
167 {
168   for (std::size_t i = 0; i < N; ++i) {
169     if ((i + 1) * 8 < prefixLength) {
170       if (networkBytes[i] != addressBytes[i]) {
171         return false;
172       }
173     } else {
174       const unsigned char shift = (i + 1) * 8 - prefixLength;
175       return (networkBytes[i] >> shift) == (addressBytes[i] >> shift);
176     }
177   }
178   return true;
179 }
180 
181 static std::string FORWARD_SLASH = "/";
182 
183 }
184 
185 namespace Wt {
186 
187 LOGGER("config");
188 
HeadMatter(std::string contents,std::string userAgent)189 HeadMatter::HeadMatter(std::string contents,
190 		       std::string userAgent)
191   : contents_(contents),
192     userAgent_(userAgent)
193 { }
194 
fromString(const std::string & s)195 Configuration::Network Configuration::Network::fromString(const std::string &s)
196 {
197   const auto slashPos = s.find('/');
198   if (slashPos == std::string::npos) {
199     AsioWrapper::error_code ec;
200     const auto address = AsioWrapper::asio::ip::address::from_string(s, ec);
201     if (ec) {
202       throw std::invalid_argument("'" + s + "' is not a valid IP address");
203     }
204     const unsigned char prefixLength = address.is_v6() ? 128 : 32;
205     return Network { address, prefixLength };
206   } else {
207     AsioWrapper::error_code ec;
208     const auto address = AsioWrapper::asio::ip::address::from_string(s.substr(0, slashPos), ec);
209     if (ec) {
210       throw std::invalid_argument("'" + s + "' is not a valid IP address");
211     }
212     const unsigned int prefixLength = Utils::stoi(s.substr(slashPos + 1));
213     if (prefixLength < 0 ||
214         (address.is_v4() && prefixLength > 32) ||
215         (address.is_v6() && prefixLength > 128)) {
216       throw std::invalid_argument("Invalid prefix length " + s.substr(slashPos + 1) + " for IPv" +
217                                   std::string(address.is_v4() ? "4" : "6") + " address");
218     }
219     return Network { address, static_cast<unsigned char>(prefixLength) };
220   }
221 }
222 
contains(const AsioWrapper::asio::ip::address & address)223 bool Configuration::Network::contains(const AsioWrapper::asio::ip::address &address) const
224 {
225   if (this->address.is_v6() && address.is_v6()) {
226     const auto networkBytes = this->address.to_v6().to_bytes();
227     const auto addressBytes = address.to_v6().to_bytes();
228     return prefixMatches(networkBytes, addressBytes, prefixLength);
229   } else if (this->address.is_v4() && address.is_v4()) {
230     const auto networkBytes = this->address.to_v4().to_bytes();
231     const auto addressBytes = address.to_v4().to_bytes();
232     return prefixMatches(networkBytes, addressBytes, prefixLength);
233   } else {
234     return false;
235   }
236 }
237 
Configuration(const std::string & applicationPath,const std::string & appRoot,const std::string & configurationFile,WServer * server)238 Configuration::Configuration(const std::string& applicationPath,
239 			     const std::string& appRoot,
240 			     const std::string& configurationFile,
241 			     WServer *server)
242   : server_(server),
243     applicationPath_(applicationPath),
244     appRoot_(appRoot),
245     configurationFile_(configurationFile),
246     runDirectory_(RUNDIR),
247     connectorSlashException_(false), // need to use ?_=
248     connectorNeedReadBody_(false),
249     connectorWebSockets_(true),
250     defaultEntryPoint_("/")
251 {
252   reset();
253   readConfiguration(false);
254 }
255 
reset()256 void Configuration::reset()
257 {
258   sessionPolicy_ = SharedProcess;
259   numProcesses_ = 1;
260   numThreads_ = 10;
261   maxNumSessions_ = 100;
262   maxRequestSize_ = 128 * 1024;
263   maxFormDataSize_ = 5 * 1024 * 1024;
264   maxPendingEvents_ = 1000;
265   isapiMaxMemoryRequestSize_ = 128 * 1024;
266   sessionTracking_ = URL;
267   reloadIsNewSession_ = true;
268   sessionTimeout_ = 600;
269   idleTimeout_ = -1;
270   bootstrapTimeout_ = 10;
271   indicatorTimeout_ = 500;
272   doubleClickTimeout_ = 200;
273   serverPushTimeout_ = 50;
274   valgrindPath_ = "";
275   errorReporting_ = ErrorMessage;
276   if (!runDirectory_.empty()) // disabled by connector
277     runDirectory_ = RUNDIR;
278   sessionIdLength_ = 16;
279   properties_.clear();
280   xhtmlMimeType_ = false;
281   behindReverseProxy_ = false;
282   originalIPHeader_ = "X-Forwarded-For";
283   trustedProxies_.clear();
284   redirectMsg_ = "Load basic HTML";
285   serializedEvents_ = false;
286   webSockets_ = false;
287   inlineCss_ = true;
288   ajaxAgentList_.clear();
289   botList_.clear();
290   ajaxAgentWhiteList_ = false;
291   persistentSessions_ = false;
292   splitScript_ = false;
293   maxPlainSessionsRatio_ = 1;
294   ajaxPuzzle_ = false;
295   sessionIdCookie_ = false;
296   cookieChecks_ = true;
297   webglDetection_ = true;
298   bootstrapConfig_.clear();
299   numSessionThreads_ = -1;
300   allowedOrigins_.clear();
301 
302   if (!appRoot_.empty())
303     setAppRoot(appRoot_);
304 }
305 
setAppRoot(const std::string & path)306 void Configuration::setAppRoot(const std::string& path)
307 {
308   appRoot_ = path;
309   properties_["appRoot"] = appRoot_;
310 }
311 
sessionPolicy()312 Configuration::SessionPolicy Configuration::sessionPolicy() const
313 {
314   READ_LOCK;
315   return sessionPolicy_;
316 }
317 
numProcesses()318 int Configuration::numProcesses() const
319 {
320   READ_LOCK;
321   return numProcesses_;
322 }
323 
numThreads()324 int Configuration::numThreads() const
325 {
326   READ_LOCK;
327   return numThreads_;
328 }
329 
maxNumSessions()330 int Configuration::maxNumSessions() const
331 {
332   READ_LOCK;
333   return maxNumSessions_;
334 }
335 
maxRequestSize()336 ::int64_t Configuration::maxRequestSize() const
337 {
338   return maxRequestSize_;
339 }
340 
341 
maxFormDataSize()342 ::int64_t Configuration::maxFormDataSize() const
343 {
344   return maxFormDataSize_;
345 }
346 
maxPendingEvents()347 int Configuration::maxPendingEvents() const
348 {
349   return maxPendingEvents_;
350 }
351 
isapiMaxMemoryRequestSize()352 ::int64_t Configuration::isapiMaxMemoryRequestSize() const
353 {
354   READ_LOCK;
355   return isapiMaxMemoryRequestSize_;
356 }
357 
sessionTracking()358 Configuration::SessionTracking Configuration::sessionTracking() const
359 {
360   READ_LOCK;
361   return sessionTracking_;
362 }
363 
reloadIsNewSession()364 bool Configuration::reloadIsNewSession() const
365 {
366   READ_LOCK;
367   return reloadIsNewSession_;
368 }
369 
sessionTimeout()370 int Configuration::sessionTimeout() const
371 {
372   READ_LOCK;
373   return sessionTimeout_;
374 }
375 
idleTimeout()376 int Configuration::idleTimeout() const
377 {
378   READ_LOCK;
379   return idleTimeout_;
380 }
381 
keepAlive()382 int Configuration::keepAlive() const
383 {
384   int timeout = sessionTimeout();
385   if (timeout == -1)
386     return 1000000;
387   else
388     return timeout / 2;
389 }
390 
391 // The multisession cookie timeout should be longer than
392 // sessionTimeout() + keepAlive(), to avoid the situation
393 // where the session has not timed out yet, but the multi
394 // session cookie has expired. Let's just set
395 // multiSessionCookieTimeout() to sessionTimeout() * 2
multiSessionCookieTimeout()396 int Configuration::multiSessionCookieTimeout() const
397 {
398   return sessionTimeout() * 2;
399 }
400 
bootstrapTimeout()401 int Configuration::bootstrapTimeout() const
402 {
403   READ_LOCK;
404   return bootstrapTimeout_;
405 }
406 
indicatorTimeout()407 int Configuration::indicatorTimeout() const
408 {
409   READ_LOCK;
410   return indicatorTimeout_;
411 }
412 
doubleClickTimeout()413 int Configuration::doubleClickTimeout() const
414 {
415   READ_LOCK;
416   return doubleClickTimeout_;
417 }
418 
serverPushTimeout()419 int Configuration::serverPushTimeout() const
420 {
421   READ_LOCK;
422   return serverPushTimeout_;
423 }
424 
valgrindPath()425 std::string Configuration::valgrindPath() const
426 {
427   READ_LOCK;
428   return valgrindPath_;
429 }
430 
errorReporting()431 Configuration::ErrorReporting Configuration::errorReporting() const
432 {
433   READ_LOCK;
434   return errorReporting_;
435 }
436 
debug()437 bool Configuration::debug() const
438 {
439   READ_LOCK;
440   return errorReporting_ != ErrorMessage;
441 }
442 
runDirectory()443 std::string Configuration::runDirectory() const
444 {
445   READ_LOCK;
446   return runDirectory_;
447 }
448 
sessionIdLength()449 int Configuration::sessionIdLength() const
450 {
451   READ_LOCK;
452   return sessionIdLength_;
453 }
454 
sessionIdPrefix()455 std::string Configuration::sessionIdPrefix() const
456 {
457   READ_LOCK;
458   return connectorSessionIdPrefix_;
459 }
460 
fullSessionIdLength()461 int Configuration::fullSessionIdLength() const
462 {
463   READ_LOCK;
464   return sessionIdLength_ + static_cast<int>(connectorSessionIdPrefix_.size());
465 }
466 
behindReverseProxy()467 bool Configuration::behindReverseProxy() const
468 {
469   READ_LOCK;
470   return behindReverseProxy_;
471 }
472 
originalIPHeader()473 std::string Configuration::originalIPHeader() const {
474   READ_LOCK;
475   return originalIPHeader_;
476 }
477 
trustedProxies()478 std::vector<Configuration::Network> Configuration::trustedProxies() const {
479   READ_LOCK;
480   return trustedProxies_;
481 }
482 
isTrustedProxy(const std::string & ipAddress)483 bool Configuration::isTrustedProxy(const std::string &ipAddress) const {
484   READ_LOCK;
485   AsioWrapper::error_code ec;
486   const auto address = AsioWrapper::asio::ip::address::from_string(ipAddress, ec);
487   if (ec) {
488     return false;
489   }
490   return std::any_of(begin(trustedProxies_), end(trustedProxies_), [&address](const Network &network) {
491     return network.contains(address);
492   });
493 }
494 
redirectMessage()495 std::string Configuration::redirectMessage() const
496 {
497   READ_LOCK;
498   return redirectMsg_;
499 }
500 
serializedEvents()501 bool Configuration::serializedEvents() const
502 {
503   READ_LOCK;
504   return serializedEvents_;
505 }
506 
webSockets()507 bool Configuration::webSockets() const
508 {
509   return webSockets_;
510 }
511 
inlineCss()512 bool Configuration::inlineCss() const
513 {
514   READ_LOCK;
515   return inlineCss_;
516 }
517 
persistentSessions()518 bool Configuration::persistentSessions() const
519 {
520   READ_LOCK;
521   return persistentSessions_;
522 }
523 
progressiveBoot(const std::string & internalPath)524 bool Configuration::progressiveBoot(const std::string& internalPath) const
525 {
526   READ_LOCK;
527   bool result = false;
528 
529   for (unsigned i = 0; i < bootstrapConfig_.size(); ++i) {
530     const BootstrapEntry& e = bootstrapConfig_[i];
531     if (e.prefix) {
532       if (internalPath == e.path ||
533 	  boost::starts_with(internalPath, e.path + '/'))
534 	result = e.method == Progressive;
535     } else
536       if (internalPath == e.path)
537 	result = e.method == Progressive;
538   }
539 
540   return result;
541 }
542 
splitScript()543 bool Configuration::splitScript() const
544 {
545   READ_LOCK;
546   return splitScript_;
547 }
548 
maxPlainSessionsRatio()549 float Configuration::maxPlainSessionsRatio() const
550 {
551   READ_LOCK;
552   return maxPlainSessionsRatio_;
553 }
554 
ajaxPuzzle()555 bool Configuration::ajaxPuzzle() const
556 {
557   READ_LOCK;
558   return ajaxPuzzle_;
559 }
560 
uaCompatible()561 std::string Configuration::uaCompatible() const
562 {
563   READ_LOCK;
564   return uaCompatible_;
565 }
566 
sessionIdCookie()567 bool Configuration::sessionIdCookie() const
568 {
569   READ_LOCK;
570   return sessionIdCookie_;
571 }
572 
cookieChecks()573 bool Configuration::cookieChecks() const
574 {
575   READ_LOCK;
576   return cookieChecks_;
577 }
578 
useSlashExceptionForInternalPaths()579 bool Configuration::useSlashExceptionForInternalPaths() const
580 {
581   return connectorSlashException_;
582 }
583 
needReadBodyBeforeResponse()584 bool Configuration::needReadBodyBeforeResponse() const
585 {
586   return connectorNeedReadBody_;
587 }
588 
webglDetect()589 bool Configuration::webglDetect() const
590 {
591   READ_LOCK;
592   return webglDetection_;
593 }
594 
numSessionThreads()595 int Configuration::numSessionThreads() const
596 {
597   READ_LOCK;
598   return numSessionThreads_;
599 }
600 
isAllowedOrigin(const std::string & origin)601 bool Configuration::isAllowedOrigin(const std::string &origin) const
602 {
603   READ_LOCK;
604   if (allowedOrigins_.size() == 1 &&
605       allowedOrigins_[0] == "*")
606     return true;
607   else {
608     for (std::size_t i = 0; i < allowedOrigins_.size(); ++i) {
609       if (origin == allowedOrigins_[i])
610         return true;
611     }
612     return false;
613   }
614 }
615 
agentIsBot(const std::string & agent)616 bool Configuration::agentIsBot(const std::string& agent) const
617 {
618   READ_LOCK;
619 
620   return regexMatchAny(agent, botList_);
621 }
622 
agentSupportsAjax(const std::string & agent)623 bool Configuration::agentSupportsAjax(const std::string& agent) const
624 {
625   READ_LOCK;
626 
627   bool matches = regexMatchAny(agent, ajaxAgentList_);
628   if (ajaxAgentWhiteList_)
629     return matches;
630   else
631     return !matches;
632 }
633 
appRoot()634 std::string Configuration::appRoot() const
635 {
636   READ_LOCK;
637 
638   std::string approot;
639 
640   if (!readConfigurationProperty("appRoot", approot)) {
641     return "";
642   }
643 
644   if (!approot.empty() && approot[approot.length() - 1] != '/'
645 #ifdef WT_WIN32
646       && approot[approot.length() - 1] != '\\'
647 #endif
648      ) {
649     approot += "/";
650   }
651 
652   return approot;
653 }
654 
locateAppRoot()655 std::string Configuration::locateAppRoot()
656 {
657   char *value;
658 
659   if ((value = ::getenv("WT_APP_ROOT")))
660     return value;
661   else
662     return std::string();
663 }
664 
locateConfigFile(const std::string & appRoot)665 std::string Configuration::locateConfigFile(const std::string& appRoot)
666 {
667   char *value;
668 
669   if ((value = ::getenv("WT_CONFIG_XML")))
670     return value;
671   else {
672     // Configuration file could be $WT_APP_ROOT/wt_config.xml
673     if (!appRoot.empty()) {
674       std::string result = appRoot + "/wt_config.xml";
675       std::ifstream s(result.c_str(), std::ios::in | std::ios::binary);
676       if (s)
677 	return result;
678     }
679 
680     return WT_CONFIG_XML;
681   }
682 }
683 
registerEntryPoint(const EntryPoint & ep)684 void Configuration::registerEntryPoint(const EntryPoint &ep)
685 {
686   const std::string &path = ep.path();
687 
688   assert(path[0] == '/');
689 
690   // The PathSegment in the routing tree where this entrypoint will end up
691   PathSegment *pathSegment = &rootPathSegment_;
692 
693   typedef boost::split_iterator<std::string::const_iterator> spliterator;
694   for (spliterator it = spliterator(path.begin() + 1, path.end(),
695                                     boost::first_finder(FORWARD_SLASH, boost::is_equal()));
696        it != spliterator(); ++it) {
697     PathSegment *childSegment = nullptr;
698     if (boost::starts_with(*it, "${") &&
699         boost::ends_with(*it, "}")) {
700       // This is a dynamic segment, e.g. ${var}
701       if (!pathSegment->dynamicChild) {
702         pathSegment->dynamicChild = std::unique_ptr<PathSegment>(new PathSegment("", pathSegment));
703       }
704       childSegment = pathSegment->dynamicChild.get();
705     } else {
706       // This is a normal segment
707       auto &children = pathSegment->children;
708       auto c = std::find_if(children.begin(), children.end(), [&it](const std::unique_ptr<PathSegment> &c) {
709         return c->segment == *it;
710       });
711       if (c != children.end())
712         childSegment = c->get();
713 
714       if (!childSegment) {
715         if (it->empty()) {
716           // Empty part (entry point with trailing slash)
717           // Put it in front by convention
718           children.insert(children.begin(), std::make_unique<PathSegment>("", pathSegment));
719           childSegment = children.front().get();
720         } else {
721           children.push_back(std::make_unique<PathSegment>(boost::copy_range<std::string>(*it), pathSegment));
722           childSegment = children.back().get();
723         }
724       }
725     }
726     pathSegment = childSegment;
727   }
728 
729   pathSegment->entryPoint = &ep;
730 }
731 
addEntryPoint(const EntryPoint & ep)732 void Configuration::addEntryPoint(const EntryPoint& ep)
733 {
734   if (ep.type() == EntryPointType::StaticResource)
735     ep.resource()->currentUrl_ = ep.path();
736 
737   WRITE_LOCK;
738   entryPoints_.push_back(ep);
739 
740   registerEntryPoint(entryPoints_.back());
741 }
742 
tryAddResource(const EntryPoint & ep)743 bool Configuration::tryAddResource(const EntryPoint& ep)
744 {
745   WRITE_LOCK;
746   for (std::size_t i = 0; i < entryPoints_.size(); ++i) {
747     if (entryPoints_[i].path() == ep.path()) {
748       return false;
749     }
750   }
751 
752   if (ep.type() == EntryPointType::StaticResource)
753     ep.resource()->currentUrl_ = ep.path();
754 
755   entryPoints_.push_back(ep);
756 
757   registerEntryPoint(entryPoints_.back());
758 
759   return true;
760 }
761 
removeEntryPoint(const std::string & path)762 void Configuration::removeEntryPoint(const std::string& path)
763 {
764   for (unsigned i = 0; i < entryPoints_.size(); ++i) {
765     const EntryPoint &ep = entryPoints_[i];
766     if (ep.path() == path) {
767       rootPathSegment_.children.clear();
768       entryPoints_.erase(entryPoints_.begin() + i);
769       for (std::size_t j = 0; j < entryPoints_.size(); ++j) {
770         registerEntryPoint(entryPoints_[j]);
771       }
772       break;
773     }
774   }
775 }
776 
setDefaultEntryPoint(const std::string & path)777 void Configuration::setDefaultEntryPoint(const std::string& path)
778 {
779   defaultEntryPoint_ = path;
780 }
781 
matchEntryPoint(const std::string & scriptName,const std::string & path,bool matchAfterSlash)782 EntryPointMatch Configuration::matchEntryPoint(const std::string &scriptName,
783                                                  const std::string &path,
784                                                  bool matchAfterSlash) const
785 {
786   if (!scriptName.empty()) {
787     LOG_DEBUG("matchEntryPoint: matching entry point, scriptName: '" << scriptName << "', path: '" << path << '\'');
788     EntryPointMatch m = matchEntryPoint(EMPTY_STR, scriptName + path, matchAfterSlash);
789     if (m.entryPoint)
790       return m;
791     else
792       return matchEntryPoint(EMPTY_STR, path, matchAfterSlash);
793   }
794   LOG_DEBUG("matchEntryPoint: matching entry point, path: '" << path << '\'');
795 
796   READ_LOCK;
797   // Only one default entry point.
798   if (entryPoints_.size() == 1
799       && entryPoints_[0].path().empty()) {
800     LOG_DEBUG("matchEntryPoint: only one entry point: matching path '" << path << "' with default entry point");
801     return EntryPointMatch(&entryPoints_[0], 0);
802   }
803 
804   assert(path.empty() || path[0] == '/');
805 
806   const PathSegment *pathSegment = &rootPathSegment_;
807 
808   // Flag marking whether any of the matched segments is dynamic
809   bool dynamic = false;
810 
811   typedef boost::split_iterator<std::string::const_iterator> spliterator;
812 
813   spliterator it;
814   if (!path.empty()) {
815     it = spliterator(path.begin() + 1, path.end(),
816                      boost::first_finder(FORWARD_SLASH, boost::is_equal()));
817     // Move down the routing tree, segment per segment
818     for (;it != spliterator(); ++it) {
819       // Find exact path match for segment
820       const auto &children = pathSegment->children;
821       const PathSegment *childSegment = nullptr;
822       auto c = std::find_if(children.begin(), children.end(), [&it](const std::unique_ptr<PathSegment> &c) {
823         return c->segment == *it;
824       });
825       if (c != children.end())
826         childSegment = c->get();
827 
828       // No exact match, see if there is a dynamic segment
829       if (!childSegment &&
830           !it->empty() &&
831           pathSegment->dynamicChild) {
832         childSegment = pathSegment->dynamicChild.get();
833         dynamic = true;
834       }
835 
836       // No match, deepest match reached
837       if (!childSegment)
838         break;
839 
840       // Move to the next segment
841       pathSegment = childSegment;
842     }
843   } else {
844     matchAfterSlash = true;
845   }
846 
847   // Move up from the found segment, until we find one that corresponds to an entrypoint
848   const EntryPoint *match = nullptr;
849   for (; pathSegment != nullptr; pathSegment = pathSegment->parent) {
850     // If matchAfterSlash is true,
851     // then the path /head/tail
852     // may match the entry point /head/
853     if (matchAfterSlash && (path.empty() || it != spliterator())) {
854       const auto &children = pathSegment->children;
855       if (!children.empty() && children.front()->segment.empty()) {
856         match = children.front()->entryPoint;
857         break;
858       }
859     }
860     // If the current segment has an entrypoint, we're done
861     if (pathSegment->entryPoint) {
862       match = pathSegment->entryPoint;
863       break;
864     }
865   }
866 
867   if (match && dynamic) {
868     // Process path parameters
869     EntryPointMatch result;
870     result.entryPoint = match;
871     // Iterate concurrently over the path (it1),
872     // and the matched endpoint's path (it2)
873     spliterator it1 = spliterator(path.begin() + 1, path.end(),
874                                   boost::first_finder(FORWARD_SLASH, boost::is_equal()));
875     for (spliterator it2 = spliterator(match->path().begin() + 1, match->path().end(),
876                                        boost::first_finder(FORWARD_SLASH, boost::is_equal()));
877          it1 != spliterator() && it2 != spliterator(); ++it1, ++it2) {
878       // Check dynamic segment (e.g. "${var}")
879       if (boost::starts_with(*it2, "${") &&
880           boost::ends_with(*it2, "}")) {
881         auto range = boost::iterator_range<std::string::const_iterator>(it2->begin() + 2, it2->end() - 1);
882         result.urlParams.push_back(std::make_pair(boost::copy_range<std::string>(range),
883                                                   boost::copy_range<std::string>(*it1)));
884       }
885     }
886     if (it1 == spliterator())
887       result.extra = path.size(); // no extra path
888     else
889       result.extra = std::distance(path.begin(), it1->begin()) - 1; // there's more
890 
891     LOG_DEBUG("matchEntryPoint: path '" << path << "' matches dynamic entry point: '" << match->path() << '\'');
892     return result;
893   } else if (match) {
894     LOG_DEBUG("matchEntryPoint: path '" << path << "' matches entry point: '" << match->path() << '\'');
895     return EntryPointMatch(match, path.empty() ? 0 : match->path().size()); // simple match
896   } else {
897     LOG_DEBUG("matchEntryPoint: no entry point match found for path: '" << path << '\'');
898     return EntryPointMatch(); // no match
899   }
900 }
901 
matchesPath(const std::string & path,const std::string & prefix,bool matchAfterSlash)902 bool Configuration::matchesPath(const std::string &path,
903                                 const std::string &prefix,
904 				bool matchAfterSlash)
905 {
906   if (boost::starts_with(path, prefix)) {
907     std::size_t prefixLength = prefix.length();
908 
909     if (path.length() > prefixLength) {
910       char next = path[prefixLength];
911 
912       if (next == '/')
913 	return true;
914       else if (matchAfterSlash) {
915 	char last = prefix[prefixLength - 1];
916 
917 	if (last == '/')
918 	  return true;
919       }
920     } else
921       return true;
922   }
923 
924   return false;
925 }
926 
setSessionTimeout(int sessionTimeout)927 void Configuration::setSessionTimeout(int sessionTimeout)
928 {
929   sessionTimeout_ = sessionTimeout;
930 }
931 
setSessionIdPrefix(const std::string & prefix)932 void Configuration::setSessionIdPrefix(const std::string& prefix)
933 {
934   connectorSessionIdPrefix_ = prefix;
935 }
936 
setWebSockets(bool enabled)937 void Configuration::setWebSockets(bool enabled)
938 {
939   connectorWebSockets_ = enabled;
940   if (!enabled)
941     webSockets_ = false;
942 }
943 
setNeedReadBodyBeforeResponse(bool needed)944 void Configuration::setNeedReadBodyBeforeResponse(bool needed)
945 {
946   connectorNeedReadBody_ = needed;
947 }
948 
setUseSlashExceptionForInternalPaths(bool use)949 void Configuration::setUseSlashExceptionForInternalPaths(bool use)
950 {
951   connectorSlashException_ = use;
952 }
953 
setRunDirectory(const std::string & path)954 void Configuration::setRunDirectory(const std::string& path)
955 {
956   runDirectory_ = path;
957 }
958 
setNumThreads(int threads)959 void Configuration::setNumThreads(int threads)
960 {
961   numThreads_ = threads;
962 }
963 
setBehindReverseProxy(bool enabled)964 void Configuration::setBehindReverseProxy(bool enabled)
965 {
966   behindReverseProxy_ = enabled;
967 }
968 
setOriginalIPHeader(const std::string & originalIPHeader)969 void Configuration::setOriginalIPHeader(const std::string &originalIPHeader)
970 {
971   originalIPHeader_ = originalIPHeader;
972 }
973 
setTrustedProxies(const std::vector<Network> & trustedProxies)974 void Configuration::setTrustedProxies(const std::vector<Network> &trustedProxies)
975 {
976   trustedProxies_ = trustedProxies;
977 }
978 
setBootstrapMethod(BootstrapMethod method)979 void Configuration::setBootstrapMethod(BootstrapMethod method)
980 {
981   bootstrapConfig_.clear();
982   if (method == Progressive) {
983     bootstrapConfig_.insert(bootstrapConfig_.begin(), BootstrapEntry());
984     bootstrapConfig_.front().prefix = true;
985     bootstrapConfig_.front().method = Progressive;
986   }
987 }
988 
readApplicationSettings(xml_node<> * app)989 void Configuration::readApplicationSettings(xml_node<> *app)
990 {
991   xml_node<> *sess = singleChildElement(app, "session-management");
992 
993   if (sess) {
994     xml_node<> *dedicated = singleChildElement(sess, "dedicated-process");
995     xml_node<> *shared = singleChildElement(sess, "shared-process");
996     std::string tracking = singleChildElementValue(sess, "tracking", "");
997 
998     if (dedicated && shared)
999       throw WServer::Exception("<application-settings> requires either "
1000 			       "<dedicated-process> or <shared-process>, "
1001 			       "not both");
1002 
1003     if (dedicated) {
1004       sessionPolicy_ = DedicatedProcess;
1005       setInt(dedicated, "max-num-sessions", maxNumSessions_);
1006       setInt(dedicated, "num-session-threads", numSessionThreads_);
1007     }
1008 
1009     if (shared) {
1010       sessionPolicy_ = SharedProcess;
1011 
1012       setInt(shared, "num-processes", numProcesses_);
1013     }
1014 
1015     if (!tracking.empty()) {
1016       if (tracking == "Auto")
1017 	sessionTracking_ = CookiesURL;
1018       else if (tracking == "URL")
1019 	sessionTracking_ = URL;
1020       else if (tracking == "Combined")
1021 	sessionTracking_ = Combined;
1022       else
1023 	throw WServer::Exception("<session-tracking>: expecting 'Auto', "
1024 				 "'URL', or 'Combined'");
1025     }
1026 
1027     setInt(sess, "timeout", sessionTimeout_);
1028     setInt(sess, "idle-timeout", idleTimeout_);
1029     setInt(sess, "bootstrap-timeout", bootstrapTimeout_);
1030     setInt(sess, "server-push-timeout", serverPushTimeout_);
1031     setBoolean(sess, "reload-is-new-session", reloadIsNewSession_);
1032   }
1033 
1034   std::string maxRequestStr
1035     = singleChildElementValue(app, "max-request-size", "");
1036   if (!maxRequestStr.empty())
1037     maxRequestSize_ = Utils::stoll(maxRequestStr) * 1024;
1038 
1039   std::string maxFormDataStr =
1040     singleChildElementValue(app, "max-formdata-size", "");
1041   if (!maxFormDataStr.empty())
1042     maxFormDataSize_ = Utils::stoll(maxFormDataStr) * 1024;
1043 
1044   std::string maxPendingEventsStr =
1045     singleChildElementValue(app, "max-pending-events", "");
1046   if (!maxPendingEventsStr.empty())
1047     maxPendingEvents_ = Utils::stoi(maxPendingEventsStr);
1048 
1049   std::string debugStr = singleChildElementValue(app, "debug", "");
1050 
1051   if (!debugStr.empty()) {
1052     if (debugStr == "stack" || debugStr == "false")
1053       errorReporting_ = ErrorMessage;
1054     else if (debugStr == "naked")
1055       errorReporting_ = NoErrors;
1056     else if (debugStr == "true")
1057       errorReporting_ = ServerSideOnly;
1058     else
1059       throw WServer::Exception("<debug>: expecting 'true', 'false',"
1060 			       "'naked', or 'stack'");
1061   }
1062 
1063   setInt(app, "num-threads", numThreads_);
1064 
1065   xml_node<> *fcgi = singleChildElement(app, "connector-fcgi");
1066   if (!fcgi)
1067     fcgi = app; // backward compatibility
1068 
1069   valgrindPath_ = singleChildElementValue(fcgi, "valgrind-path",
1070 					  valgrindPath_);
1071   runDirectory_ = singleChildElementValue(fcgi, "run-directory",
1072 					  runDirectory_);
1073 
1074   xml_node<> *isapi = singleChildElement(app, "connector-isapi");
1075   if (!isapi)
1076     isapi = app; // backward compatibility
1077 
1078   std::string maxMemoryRequestSizeStr =
1079     singleChildElementValue(isapi, "max-memory-request-size", "");
1080   if (!maxMemoryRequestSizeStr.empty()) {
1081     isapiMaxMemoryRequestSize_ = Utils::stol(maxMemoryRequestSizeStr) * 1024;
1082   }
1083 
1084   setInt(app, "session-id-length", sessionIdLength_);
1085 
1086   /*
1087    * If a session-id-prefix is defined in the configuration file, then
1088    * we loose the prefix defined by the connector (e.g. wthttpd), but who
1089    * would do such a thing ? */
1090   connectorSessionIdPrefix_
1091     = singleChildElementValue(app,"session-id-prefix",
1092 			      connectorSessionIdPrefix_);
1093 
1094   setBoolean(app, "send-xhtml-mime-type", xhtmlMimeType_);
1095   if (xhtmlMimeType_)
1096     LOG_WARN("ignoring send-xhtml-mime-type setting: HTML5 is now always used");
1097   redirectMsg_ = singleChildElementValue(app, "redirect-message", redirectMsg_);
1098 
1099   setBoolean(app, "behind-reverse-proxy", behindReverseProxy_);
1100   if (behindReverseProxy_) {
1101     LOG_WARN("The behind-reverse-proxy configuration option is deprecated, "
1102              "use a <trusted-proxy-config> block instead");
1103   }
1104 
1105   xml_node<> *trustedProxyCfg = singleChildElement(app, "trusted-proxy-config");
1106 
1107   if (trustedProxyCfg) {
1108     originalIPHeader_ = singleChildElementValue(app, "original-ip-header", originalIPHeader_);
1109 
1110     xml_node<> *trustedProxies = singleChildElement(trustedProxyCfg, "trusted-proxies");
1111 
1112     std::vector<xml_node<> *> proxies = childElements(trustedProxies, "proxy");
1113     for (const auto *proxyNode : proxies) {
1114       try {
1115         std::string value = proxyNode->value();
1116         boost::trim(value);
1117         trustedProxies_.push_back(Network::fromString(value));
1118       } catch (const std::invalid_argument &e) {
1119         throw WServer::Exception("Invalid trusted <proxy>: " + std::string(e.what()));
1120       }
1121     }
1122   }
1123 
1124   setBoolean(app, "strict-event-serialization", serializedEvents_);
1125   setBoolean(app, "web-sockets", webSockets_);
1126 
1127   setBoolean(app, "inline-css", inlineCss_);
1128   setBoolean(app, "persistent-sessions", persistentSessions_);
1129 
1130   uaCompatible_ = singleChildElementValue(app, "UA-Compatible", "");
1131 
1132   bool progressive = false;
1133   setBoolean(app, "progressive-bootstrap", progressive);
1134 
1135   xml_node<> *bootstrap = singleChildElement(app, "bootstrap-method");
1136   if (bootstrap) {
1137     progressive = std::string(bootstrap->value()) == "progressive";
1138 
1139     std::vector<xml_node<> *> entries = childElements(bootstrap, "for");
1140     for (unsigned i = 0; i < entries.size(); ++i) {
1141       xml_node<> *entry = entries[i];
1142 
1143       std::string path;
1144       if (!attributeValue(entry, "path", path) || path.empty())
1145 	throw WServer::Exception("<for> requires attribute 'path'");
1146 
1147       bootstrapConfig_.push_back(BootstrapEntry());
1148       BootstrapEntry& e = bootstrapConfig_.back();
1149 
1150       e.prefix = path[path.length() - 1] == '*';
1151       e.method = std::string(entry->value()) == "progressive"
1152 	? Progressive : DetectAjax;
1153       if (e.prefix) {
1154 	e.path = path.substr(0, path.length() - 1);
1155 	if (!e.path.empty() && e.path[e.path.length() - 1] == '/')
1156 	  e.path.erase(e.path.length() - 1);
1157       } else
1158 	e.path = path;
1159     }
1160   }
1161 
1162   if (progressive) {
1163     bootstrapConfig_.insert(bootstrapConfig_.begin(), BootstrapEntry());
1164     bootstrapConfig_.front().prefix = true;
1165     bootstrapConfig_.front().method = Progressive;
1166   }
1167 
1168   if (progressive)
1169     setBoolean(app, "split-script", splitScript_);
1170   setBoolean(app, "session-id-cookie", sessionIdCookie_);
1171   setBoolean(app, "cookie-checks", cookieChecks_);
1172   setBoolean(app, "webgl-detection", webglDetection_);
1173 
1174   std::string plainAjaxSessionsRatioLimit
1175     = singleChildElementValue(app, "plain-ajax-sessions-ratio-limit", "");
1176 
1177   if (!plainAjaxSessionsRatioLimit.empty())
1178     maxPlainSessionsRatio_ = Utils::stof(plainAjaxSessionsRatioLimit);
1179 
1180   setBoolean(app, "ajax-puzzle", ajaxPuzzle_);
1181   setInt(app, "indicator-timeout", indicatorTimeout_);
1182   setInt(app, "double-click-timeout", doubleClickTimeout_);
1183 
1184   std::vector<xml_node<> *> userAgents = childElements(app, "user-agents");
1185 
1186   for (unsigned i = 0; i < userAgents.size(); ++i) {
1187     xml_node<> *userAgentsList = userAgents[i];
1188 
1189     std::string type;
1190     if (!attributeValue(userAgentsList, "type", type))
1191       throw WServer::Exception("<user-agents> requires attribute 'type'");
1192 
1193     std::string mode;
1194     attributeValue(userAgentsList, "mode", mode);
1195 
1196     AgentList *list;
1197     if (type == "ajax") {
1198       list = &ajaxAgentList_;
1199       if (mode == "black-list")
1200 	ajaxAgentWhiteList_ = false;
1201       else if (mode == "white-list")
1202 	ajaxAgentWhiteList_ = true;
1203       else
1204 	throw WServer::Exception
1205 	  ("<user-agents type=\"ajax\" requires attribute 'mode' with value "
1206 	   "\"white-list\" or \"black-list\"");
1207     } else if (type == "bot")
1208       list = &botList_;
1209     else
1210       throw WServer::Exception
1211 	("<user-agents> requires attribute 'type' with value "
1212 	 "\"ajax\" or \"bot\"");
1213 
1214     std::vector<xml_node<> *> agents
1215       = childElements(userAgentsList, "user-agent");
1216 
1217     for (unsigned j = 0; j < agents.size(); ++j)
1218       list->push_back(elementValue(agents[j], "user-agent"));
1219   }
1220 
1221   xml_node<> *properties = singleChildElement(app, "properties");
1222 
1223   if (properties) {
1224     std::vector<xml_node<> *> nodes = childElements(properties, "property");
1225 
1226     for (unsigned i = 0; i < nodes.size(); ++i) {
1227       xml_node<> *property = nodes[i];
1228 
1229       std::string name;
1230       if (!attributeValue(property, "name", name))
1231 	throw WServer::Exception("<property> requires attribute 'name'");
1232 
1233       std::string value = elementValue(property, "property");
1234 
1235       if (name == "approot")
1236 	name = "appRoot";
1237 
1238       if (name == "appRoot" && !appRoot_.empty())
1239 	LOG_WARN("ignoring configuration property 'appRoot' ("
1240 		 << value
1241 		 << ") because was already set to " << appRoot_);
1242       else
1243         properties_[name] = value;
1244     }
1245   }
1246 
1247   // deprecated
1248   std::vector<xml_node<> *> metaHeaders = childElements(app, "meta-headers");
1249   for (unsigned i = 0; i < metaHeaders.size(); ++i) {
1250     xml_node<> *metaHeader = metaHeaders[i];
1251 
1252     std::string userAgent;
1253     attributeValue(metaHeader, "user-agent", userAgent);
1254 
1255     std::vector<xml_node<> *> metas = childElements(metaHeader, "meta");
1256     for (unsigned j = 0; j < metas.size(); ++j) {
1257       xml_node<> *meta = metas[j];
1258 
1259       std::string name, property, httpEquiv, content;
1260       attributeValue(meta, "name", name);
1261       attributeValue(meta, "http-equiv", httpEquiv);
1262       attributeValue(meta, "property", property);
1263       attributeValue(meta, "content", content);
1264 
1265       MetaHeaderType type;
1266       if (!name.empty())
1267 	type = MetaHeaderType::Meta;
1268       else if (!httpEquiv.empty()) {
1269 	type = MetaHeaderType::HttpHeader;
1270 	name = httpEquiv;
1271       } else if (!property.empty()) {
1272 	type = MetaHeaderType::Property;
1273 	name = property;
1274       } else {
1275 	throw WServer::Exception
1276 	  ("<meta> requires attribute 'name', 'property' or 'http-equiv'");
1277       }
1278 
1279       metaHeaders_.push_back(MetaHeader(type, name, content, "", userAgent));
1280     }
1281   }
1282 
1283   std::vector<xml_node<> *> headMatters = childElements(app, "head-matter");
1284   for (unsigned i = 0; i < headMatters.size(); ++i) {
1285     xml_node<> *headMatter = headMatters[i];
1286 
1287     std::string userAgent;
1288     attributeValue(headMatter, "user-agent", userAgent);
1289 
1290     std::stringstream ss;
1291     for (xml_node<> *r = headMatter->first_node(); r;
1292          r = r->next_sibling()) {
1293       rapidxml::print(static_cast<std::ostream&>(ss), *r);
1294     }
1295     headMatter_.push_back(HeadMatter(ss.str(), userAgent));
1296   }
1297 
1298   std::string allowedOrigins
1299     = singleChildElementValue(app, "allowed-origins", "");
1300   boost::split(allowedOrigins_, allowedOrigins, boost::is_any_of(","));
1301   for (std::size_t i = 0; i < allowedOrigins_.size(); ++i)
1302     boost::trim(allowedOrigins_[i]);
1303 }
1304 
rereadConfiguration()1305 void Configuration::rereadConfiguration()
1306 {
1307   WRITE_LOCK;
1308 
1309   try {
1310     LOG_INFO("Rereading configuration...");
1311     Configuration conf(applicationPath_, appRoot_, configurationFile_, nullptr);
1312     reset();
1313     readConfiguration(true);
1314     LOG_INFO("New configuration read.");
1315   } catch (WException& e) {
1316     LOG_ERROR("Error reading configuration: " << e.what());
1317   }
1318 }
1319 
readConfiguration(bool silent)1320 void Configuration::readConfiguration(bool silent)
1321 {
1322   std::ifstream s(configurationFile_.c_str(), std::ios::in | std::ios::binary);
1323 
1324   if (!s) {
1325     if (configurationFile_ != WT_CONFIG_XML)
1326       throw WServer::Exception
1327 	("Error reading '" + configurationFile_ + "': could not open file.");
1328     else
1329       return;
1330   }
1331 
1332   s.seekg(0, std::ios::end);
1333   int length = s.tellg();
1334   s.seekg(0, std::ios::beg);
1335 
1336   std::unique_ptr<char[]> text(new char[length + 1]);
1337   s.read(text.get(), length);
1338   s.close();
1339   text[length] = 0;
1340 
1341   try {
1342     xml_document<> doc;
1343     doc.parse<parse_normalize_whitespace
1344       | parse_trim_whitespace
1345       | parse_validate_closing_tags>(text.get());
1346 
1347     xml_node<> *root = doc.first_node();
1348 
1349     if (!root)
1350       throw WServer::Exception("<server> expected.");
1351 
1352     std::vector<xml_node<> *> applications
1353       = childElements(root, "application-settings");
1354 
1355     /*
1356      * Scan the config file first to determine the logFile, in order
1357      * to setup logging before parsing the other settings.
1358      */
1359     std::string logFile;
1360     std::string logConfig;
1361     for (unsigned i = 0; i < applications.size(); ++i) {
1362       xml_node<> *app = applications[i];
1363 
1364       std::string appLocation;
1365       if (!attributeValue(app, "location", appLocation))
1366 	throw WServer::Exception("<application-settings> requires attribute "
1367 				 "'location'");
1368 
1369       if (appLocation == "*" || appLocation == applicationPath_) {
1370 	logFile = singleChildElementValue(app, "log-file", logFile);
1371 	logConfig = singleChildElementValue(app, "log-config", logConfig);
1372       }
1373     }
1374 
1375     if (server_)
1376       server_->initLogger(logFile, logConfig);
1377 
1378     if (!silent)
1379       LOG_INFO("reading Wt config file: " << configurationFile_
1380 	       << " (location = '" << applicationPath_ << "')");
1381 
1382     /*
1383      * Now read application settings.
1384      */
1385     for (unsigned i = 0; i < applications.size(); ++i) {
1386       xml_node<> *app = applications[i];
1387 
1388       std::string appLocation;
1389       attributeValue(app, "location", appLocation);
1390 
1391       if (appLocation == "*" || appLocation == applicationPath_)
1392 	readApplicationSettings(app);
1393     }
1394   } catch (std::exception& e) {
1395     throw WServer::Exception("Error reading: " + configurationFile_ + ": "
1396 			     + e.what());
1397   } catch (...) {
1398     throw WServer::Exception("Exception of unknown type!\n");
1399   }
1400 }
1401 
registerSessionId(const std::string & oldId,const std::string & newId)1402 bool Configuration::registerSessionId(const std::string& oldId,
1403 				      const std::string& newId)
1404 {
1405   if (!runDirectory_.empty()) {
1406 
1407     if (!newId.empty()) {
1408       std::string socketPath = sessionSocketPath(newId);
1409 
1410       struct stat finfo;
1411       if (stat(socketPath.c_str(), &finfo) != -1)
1412 	return false;
1413 
1414       if (oldId.empty()) {
1415 	if (sessionPolicy_ == SharedProcess) {
1416 	  std::ofstream f(socketPath.c_str());
1417 	  f << getpid() << std::endl;
1418 	  f.flush();
1419 	}
1420       }
1421     }
1422 
1423     if (!oldId.empty()) {
1424       if (newId.empty())
1425 	unlink(sessionSocketPath(oldId).c_str());
1426       else
1427 	std::rename(sessionSocketPath(oldId).c_str(),
1428 	            sessionSocketPath(newId).c_str());
1429     }
1430   }
1431 
1432   return true;
1433 }
1434 
generateSessionId()1435 std::string Configuration::generateSessionId()
1436 {
1437   std::string sessionId = sessionIdPrefix();
1438   sessionId += WRandom::generateId(sessionIdLength());
1439   return sessionId;
1440 }
1441 
sessionSocketPath(const std::string & sessionId)1442 std::string Configuration::sessionSocketPath(const std::string& sessionId)
1443 {
1444   return runDirectory_ + "/" + sessionId;
1445 }
1446 
readConfigurationProperty(const std::string & name,std::string & value)1447 bool Configuration::readConfigurationProperty(const std::string& name,
1448                                               std::string& value) const
1449 {
1450   PropertyMap::const_iterator i = properties_.find(name);
1451 
1452   if (i != properties_.end()) {
1453     value = i->second;
1454     return true;
1455   } else
1456     return false;
1457 }
1458 
log(const std::string & type)1459 WLogEntry Configuration::log(const std::string& type) const
1460 {
1461   if (server_)
1462     return server_->log(type);
1463   else
1464     return Wt::log(type);
1465 }
1466 
1467 }
1468