Pol  Revision:cb584c9
polwww.cpp
Go to the documentation of this file.
1 
15 #include "polwww.h"
16 
17 #include <assert.h>
18 #include <ctype.h>
19 #include <errno.h>
20 #include <iosfwd>
21 #include <string>
22 #include <time.h>
23 
24 #include "../clib/cfgelem.h"
25 #include "../clib/cfgfile.h"
26 #include "../clib/esignal.h"
27 #include "../clib/fileutil.h"
28 #include "../clib/logfacility.h"
29 #include "../clib/passert.h"
30 #include "../clib/refptr.h"
31 #include "../clib/stlutil.h"
32 #include "../clib/strutil.h"
33 #include "../clib/threadhelp.h"
34 #include "../clib/wnsckt.h"
35 #include "../plib/pkg.h"
36 #include "../plib/systemstate.h"
37 #include "globals/uvars.h"
38 #include "module/httpmod.h"
39 #include "module/uomod.h"
40 #include "polcfg.h"
41 #include "polsem.h"
42 #include "scrdef.h"
43 #include "scrsched.h"
44 #include "scrstore.h"
45 #include "sockets.h"
46 #include "sockio.h"
47 #include "uoexec.h"
48 
49 #ifdef _WIN32
50 #include <process.h>
51 #else
52 #include <pthread.h>
53 #endif
54 
55 
56 #ifdef _MSC_VER
57 #pragma warning( disable : 4127 ) // conditional expression is constant (needed because of FD_SET)
58 #endif
59 
60 namespace Pol
61 {
62 namespace Core
63 {
64 using namespace threadhelp;
65 
66 void load_mime_config( void )
67 {
68  static time_t last_load = 0;
69 
70  if ( !Clib::FileExists( "config/www.cfg" ) )
71  {
72  if ( last_load )
73  {
74  gamestate.mime_types.clear();
75  last_load = 0;
76  }
77  gamestate.mime_types["jpg"] = "image/jpeg";
78  gamestate.mime_types["jpeg"] = "image/jpeg";
79  gamestate.mime_types["gif"] = "image/gif";
80  return;
81  }
82 
83  try
84  {
85  Clib::ConfigFile cf( "config/www.cfg" );
86  if ( cf.modified() <= last_load )
87  { // not modified
88  return;
89  }
90  last_load = cf.modified();
91  gamestate.mime_types.clear();
92  Clib::ConfigElem elem;
93  while ( cf.read( elem ) )
94  {
95  std::string ext, mime;
96  elem.remove_prop( "Extension", &ext );
97  elem.remove_prop( "MIME", &mime );
98  gamestate.mime_types[ext] = mime;
99  }
100  }
101  catch ( ... )
102  {
103  POLLOG_ERROR << "Error while parsing www.cfg\n";
104  }
105 }
106 
108 {
109  for ( Plib::Packages::iterator itr = Plib::systemstate.packages.begin();
110  itr != Plib::systemstate.packages.end(); ++itr )
111  {
112  Plib::Package* pkg = ( *itr );
113  if ( pkg->provides_system_home_page() )
114  {
115  if ( gamestate.wwwroot_pkg == nullptr )
116  {
117  POLLOG.Format( "wwwroot package is {}\n" ) << pkg->desc();
118  gamestate.wwwroot_pkg = pkg;
119  }
120  else
121  {
122  POLLOG.Format( "Package {} also provides a wwwroot, ignoring\n" ) << pkg->desc();
123  }
124  }
125  }
127 }
128 
129 // TODO: The http server is susceptible to DOS attacks
130 // TODO: limit access to localhost by default, probably
131 
132 bool http_readline( Clib::Socket& sck, std::string& s )
133 {
134  bool res = false;
135  s = "";
136  unsigned char ch;
137  while ( sck.connected() && sck.recvbyte( &ch, 10000 ) )
138  {
139  if ( isprint( ch ) )
140  {
141  s.append( 1, ch );
142  if ( s.length() > 3000 )
143  {
144  sck.close();
145  break; // return false;
146  }
147  }
148  else
149  {
150  if ( ch == '\n' )
151  {
152  res = true;
153  break;
154  // return true;
155  }
156  }
157  }
158  return res;
159 }
160 void http_writeline( Clib::Socket& sck, const std::string& s )
161 {
162  sck.send( (void*)s.c_str(), static_cast<unsigned int>( s.length() ) );
163  sck.send( "\n", 1 );
164 }
165 
167 {
168  http_writeline( sck, "HTTP/1.1 403 Forbidden" );
169  http_writeline( sck, "Content-Type: text/html" );
170  http_writeline( sck, "" );
171  http_writeline( sck, "<HTML><HEAD><TITLE>403 Forbidden</TITLE></HEAD>" );
172  http_writeline( sck, "<BODY><H1>Forbidden</H1>" );
173  http_writeline( sck, "You are forbidden to access this server." );
174  http_writeline( sck, "</BODY></HTML>" );
175 }
176 
177 void http_forbidden( Clib::Socket& sck, const std::string& filename )
178 {
179  http_writeline( sck, "HTTP/1.1 403 Forbidden" );
180  http_writeline( sck, "Content-Type: text/html" );
181  http_writeline( sck, "" );
182  http_writeline( sck, "<HTML><HEAD><TITLE>403 Forbidden</TITLE></HEAD>" );
183  http_writeline( sck, "<BODY><H1>Forbidden</H1>" );
184  http_writeline( sck, "You are forbidden to access to " + filename + " on this server." );
185  http_writeline( sck, "</BODY></HTML>" );
186 }
187 
188 void http_not_authorized( Clib::Socket& sck, const std::string& /*filename*/ )
189 {
190  http_writeline( sck, "HTTP/1.1 401 Unauthorized" );
191  http_writeline( sck, "WWW-Authenticate: Basic realm=\"pol\"" );
192  http_writeline( sck, "Content-Type: text/html" );
193  http_writeline( sck, "" );
194  http_writeline( sck, "<HTML><HEAD><TITLE>401 Unauthorized</TITLE></HEAD>" );
195  http_writeline( sck, "<BODY><H1>Unauthorized</H1>" );
196  http_writeline( sck, "You are not authorized to access that page." );
197  http_writeline( sck, "</BODY></HTML>" );
198 }
199 
200 void http_not_found( Clib::Socket& sck, const std::string& filename )
201 {
202  http_writeline( sck, "HTTP/1.1 404 Not Found" );
203  http_writeline( sck, "Content-Type: text/html" );
204  http_writeline( sck, "" );
205  http_writeline( sck, "<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>" );
206  http_writeline( sck, "<BODY><H1>Not Found</H1>" );
207  http_writeline( sck, "The requested URL " + filename + " was not found on this server." );
208  http_writeline( sck, "</BODY></HTML>" );
209 }
210 
211 void http_redirect( Clib::Socket& sck, const std::string& new_url )
212 {
213  // cerr << "http: redirecting to " << new_url << endl;
214 
215 
216  http_writeline( sck, "HTTP/1.1 301 Moved Permanently" );
217  http_writeline( sck, "Location: " + new_url );
218  http_writeline( sck, "" );
219  http_writeline( sck, "<HTML><HEAD><TITLE>301 Moved Permanently</TITLE></HEAD>" );
220  http_writeline( sck, "<BODY><H1>Moved Permanently</H1>" );
221  http_writeline( sck, "The requested URL has been moved to " + new_url );
222  http_writeline( sck, "</BODY></HTML>" );
223 }
224 // http_decodestr: turn all those %2F etc into what they represent
225 // rules:
226 // '+' -> ' '
227 // %HH -> (hex value)
228 // other -> itself
229 std::string http_decodestr( const std::string& s )
230 {
231  std::string decoded;
232  const char* t = s.c_str();
233  while ( t != nullptr && *t != '\0' )
234  {
235  if ( *t == '+' )
236  {
237  decoded.append( 1, ' ' );
238  ++t;
239  }
240  else if ( *t != '%' )
241  {
242  decoded.append( 1, *t );
243  ++t;
244  }
245  else
246  {
247  // if first is null terminator, won't look at second
248  if ( isxdigit( *( t + 1 ) ) && isxdigit( *( t + 2 ) ) )
249  {
250  char chH = *( t + 1 );
251  char chL = *( t + 2 );
252  t += 3;
253  char ch = 0;
254  if ( isdigit( chH ) )
255  ch = ( chH - '0' ) << 4;
256  else
257  ch = ( ( static_cast<char>( tolower( chH ) ) - 'a' ) + 10 ) << 4;
258 
259  if ( isdigit( chL ) )
260  ch |= ( chL - '0' );
261  else
262  ch |= ( tolower( chL ) - 'a' ) + 10;
263 
264  decoded.append( 1, ch );
265  }
266  else // garbage - just return what we have so far.
267  {
268  return decoded;
269  }
270  }
271  }
272  return decoded;
273 }
274 
275 unsigned char cvt_8to6( char ch )
276 {
277  if ( ch >= 'A' && ch <= 'Z' )
278  return ch - 'A';
279  else if ( ch >= 'a' && ch <= 'z' )
280  return ch - 'a' + 26;
281  else if ( ch >= '0' && ch <= '9' )
282  return ch - '0' + 52;
283  else if ( ch == '+' )
284  return 62;
285  else if ( ch == '/' )
286  return 63;
287  else if ( ch == '=' )
288  return 0x40; // pad
289  else
290  return 0x80; // error
291 }
292 
293 std::string decode_base64( const std::string& b64s )
294 {
295  std::string s;
296  const char* t = b64s.c_str();
297  char c[4];
298  char x[3];
299  char b[4];
300  while ( *t )
301  {
302  c[0] = *t++;
303  c[1] = *t ? *t++ : '\0';
304  c[2] = *t ? *t++ : '\0';
305  c[3] = *t ? *t++ : '\0';
306 
307  b[0] = cvt_8to6( c[0] );
308  b[1] = cvt_8to6( c[1] );
309  b[2] = cvt_8to6( c[2] );
310  b[3] = cvt_8to6( c[3] );
311 
312  x[0] = ( ( b[0] << 2 ) & 0xFC ) | ( ( b[1] >> 4 ) & 0x03 );
313  x[1] = ( ( b[1] << 4 ) & 0xF0 ) | ( ( b[2] >> 2 ) & 0x0F );
314  x[2] = ( ( b[2] << 6 ) & 0xC0 ) | ( b[3] & 0x3F );
315 
316  if ( x[0] )
317  s.append( 1, x[0] );
318  if ( x[1] )
319  s.append( 1, x[1] );
320  if ( x[2] )
321  s.append( 1, x[2] );
322  }
323  return s;
324 }
325 
326 bool legal_pagename( const std::string& page )
327 {
328  // make sure the page isn't going to go visit our hard disk
329  for ( const char* t = page.c_str(); *t; ++t )
330  {
331  char ch = *t;
332  if ( isalnum( ch ) || ( ch == '/' ) || ( ch == '_' ) )
333  {
334  continue;
335  }
336  else if ( ( ch == '.' ) && ( isalnum( *( t + 1 ) ) ) )
337  {
338  continue;
339  }
340  else
341  {
342  return false;
343  }
344  }
345  return true;
346 }
347 
348 // get_pagetype: extract the extension, basically
349 std::string get_pagetype( const std::string& page )
350 {
351  std::string::size_type lastslash = page.rfind( '/' );
352  std::string::size_type dotpos = page.rfind( '.' );
353 
354  if ( lastslash != std::string::npos && dotpos != std::string::npos && lastslash > dotpos )
355  return "";
356 
357  if ( dotpos != std::string::npos )
358  {
359  return page.substr( dotpos + 1 );
360  }
361  else
362  {
363  return "";
364  }
365 }
366 
367 bool get_script_page_filename( const std::string& page, ScriptDef& sd )
368 {
369  if ( page.substr( 0, 5 ) == "/pkg/" )
370  {
371  // cerr << "package page script: " << page << endl;
372  auto pkgname_end = page.find_first_of( '/', 5 );
373  if ( pkgname_end != std::string::npos )
374  {
375  std::string pkg_name = page.substr( 5, pkgname_end - 5 );
376  // cerr << "pkg name: " << pkg_name << endl;
377  Plib::Package* pkg = Plib::find_package( pkg_name );
378  if ( pkg != nullptr )
379  {
380  sd.quickconfig( pkg, "www/" + page.substr( pkgname_end + 1 ) );
381  return true;
382  }
383  else
384  {
385  return false;
386  }
387  }
388  else
389  {
390  return false;
391  }
392  }
393  else
394  {
395  sd.quickconfig( "scripts/www" + page + ".ecl" );
396  return true;
397  }
398 }
399 
400 // FIXME this is just ugly! The HttpExecutorModule takes ownership of the
401 // socket, through the Socket copy-constructor.. But sometimes, after we've
402 // built it, we need to send data (on errors).
403 bool start_http_script( Clib::Socket& sck, const std::string& page, Plib::Package* pkg,
404  const std::string& file_ecl, const std::string& query_string )
405 {
406  bool res = true;
407 
408  ScriptDef page_sd;
409  if ( pkg )
410  page_sd.quickconfig( pkg, file_ecl );
411  else
412  page_sd.quickconfig( file_ecl );
413 
414  if ( !page_sd.exists() )
415  {
416  POLLOG.Format( "WebServer: not found: {}\n" ) << page_sd.name();
417  http_not_found( sck, page );
418  return false;
419  }
420 
421  PolLock2 lck;
422 
424  find_script2( page_sd, true, Plib::systemstate.config.cache_interactive_scripts );
425  // find_script( filename, true, config.cache_interactive_scripts );
426  if ( program.get() == nullptr )
427  {
428  ERROR_PRINT << "Error reading script " << page_sd.name() << "\n";
429  res = false;
430  lck.unlock();
431  http_not_found( sck, page );
432  lck.lock();
433  }
434  else
435  {
438  ex->addModule( uoemod );
440 
441  hem->read_query_string( query_string );
442  hem->read_query_ip();
443 
444  ex->addModule( hem );
445 
446  if ( !ex->setProgram( program.get() ) )
447  {
448  lck.unlock();
449  http_not_found( hem->sck_, page );
450  lck.lock();
451  delete ex;
452  res = false;
453  }
454  else
455  {
456  http_writeline( hem->sck_, "HTTP/1.1 200 OK" );
457  http_writeline( hem->sck_, "Content-Type: text/html" );
458  http_writeline( hem->sck_, "" );
460  schedule_executor( ex );
461  }
462  }
463  program.clear(); // do this so deletion happens while we're locked
464  lck.unlock();
465  return res;
466 }
467 
468 std::string get_page_filename( const std::string& page )
469 {
470  std::string filename = "scripts/www" + page;
471  return filename;
472 }
473 
474 bool decode_page( const std::string& ipage, Plib::Package** ppkg, std::string* pfilename,
475  std::string* ppagetype, std::string* redirect_to )
476 {
477  std::string page = ipage;
478  Plib::Package* pkg = nullptr;
479  std::string filedir;
480  std::string retdir;
481 
482  if ( page.substr( 0, 5 ) == "/pkg/" )
483  {
484  // cerr << "package page: " << page << endl;
485  std::string::size_type pkgname_end = page.find_first_of( '/', 5 );
486  std::string pkgname;
487  if ( pkgname_end != std::string::npos )
488  {
489  pkgname = page.substr( 5, pkgname_end - 5 );
490  page = page.substr( pkgname_end );
491  }
492  else
493  {
494  pkgname = page.substr( 5 );
495  page = "/";
496  }
497 
498  // cerr << "pkg name: " << pkgname << endl;
499  pkg = Plib::find_package( pkgname );
500  if ( pkg == nullptr )
501  return false;
502 
503  filedir = pkg->dir() + "www";
504  }
505  else
506  {
507  if ( gamestate.wwwroot_pkg != nullptr )
508  {
509  filedir = gamestate.wwwroot_pkg->dir() + "www";
510  retdir = gamestate.wwwroot_pkg->dir() + "www";
511  }
512  else
513  {
514  filedir = "scripts/www";
515  retdir = "scripts/www";
516  }
517  }
518 
519  std::string filename = filedir + page;
520 
521  std::string pagetype = get_pagetype( page );
522 
523  if ( pagetype == "" ) // didn't specify, so assume it's a directory.
524  { // have to redirect...
525  page = ipage;
526  if ( page.empty() || page[page.size() - 1] != '/' )
527  {
528  page += "/";
529  // filename += "/";
530  }
531 
532  std::string test;
533  test = filename + "index.ecl";
534  if ( Clib::FileExists( test.c_str() ) )
535  {
536  page += "index.ecl";
537  }
538  else
539  {
540  page += "index.htm";
541  }
542  *redirect_to = page;
543  return true;
544  }
545 
546  if ( pkg )
547  {
548  if ( pagetype == "ecl" )
549  {
550  // uh, I don't get this:
551  retdir = "www";
552  }
553  else
554  {
555  retdir = pkg->dir() + "www";
556  }
557  }
558 
559  *ppkg = pkg;
560  *pfilename = retdir + page;
561  *ppagetype = pagetype;
562  return true;
563 }
564 
565 void send_html( Clib::Socket& sck, const std::string& page, const std::string& filename )
566 {
567  std::ifstream ifs( filename.c_str() );
568  if ( ifs.is_open() )
569  {
570  http_writeline( sck, "HTTP/1.1 200 OK" );
571  http_writeline( sck, "Content-Type: text/html" );
572  http_writeline( sck, "" );
573  std::string t;
574  while ( getline( ifs, t ) )
575  {
576  http_writeline( sck, t );
577  }
578  }
579  else
580  {
581  http_not_found( sck, page );
582  }
583 }
584 
585 void send_binary( Clib::Socket& sck, const std::string& page, const std::string& filename,
586  const std::string& content_type )
587 {
588  // string filename = get_page_filename( page );
589  unsigned int fsize = Clib::filesize( filename.c_str() );
590  std::ifstream ifs( filename.c_str(), std::ios::binary );
591  if ( ifs.is_open() )
592  {
593  http_writeline( sck, "HTTP/1.1 200 OK" );
594  http_writeline( sck, "Accept-Ranges: bytes" );
595  http_writeline( sck, "Content-Length: " + Clib::decint( fsize ) );
596  http_writeline( sck, "Content-Type: " + content_type );
597  http_writeline( sck, "" );
598 
599  // Actual reading and outputting.
600  char bfr[256];
601  unsigned int cur_read = 0;
602  while ( sck.connected() && ifs.good() && cur_read < fsize )
603  {
604  ifs.read( bfr, sizeof( bfr ) );
605  cur_read += static_cast<unsigned int>( ifs.gcount() );
606  sck.send( bfr, static_cast<unsigned int>( ifs.gcount() ) ); // This was sizeof bfr, which
607  // would send garbage... fixed --
608  // Nando, 2009-02-22
609  }
610  // -------------
611  }
612  else
613  {
614  http_not_found( sck, page );
615  }
616 }
617 
618 void http_func( SOCKET client_socket )
619 {
620  Clib::Socket sck( client_socket );
621  std::string get;
622  std::string auth;
623  std::string tmpstr;
624  std::string host;
625 
626  if ( Plib::systemstate.config.web_server_local_only )
627  {
628  if ( !sck.is_local() )
629  {
630  http_forbidden( sck );
631  return;
632  }
633  }
634 
635  while ( sck.connected() && http_readline( sck, tmpstr ) )
636  {
637  if ( Plib::systemstate.config.web_server_debug )
638  INFO_PRINT << "http(" << sck.handle() << "): '" << tmpstr << "'\n";
639  if ( tmpstr.empty() )
640  break;
641  if ( strncmp( tmpstr.c_str(), "GET", 3 ) == 0 )
642  get = tmpstr;
643  if ( strncmp( tmpstr.c_str(), "Authorization:", 14 ) == 0 )
644  auth = tmpstr;
645  if ( strncmp( tmpstr.c_str(), "Host: ", 5 ) == 0 )
646  host = tmpstr.substr( 6 );
647  }
648  if ( !sck.connected() )
649  return;
650 
651  ISTRINGSTREAM is( get );
652 
653  std::string cmd; // GET, POST (we only handle GET)
654  std::string url; // The whole URL (xx.ecl?a=b&c=d)
655  std::string proto; // HTTP/1.1
656  std::string page; // xx.ecl
657  std::string query_string; // a=b&c=d
658 
659  is >> cmd >> url >> proto;
660 
661  if ( Plib::systemstate.config.web_server_debug )
662  {
663  INFO_PRINT << "http-cmd: '" << cmd << "'\n"
664  << "http-host: '" << host << "'\n"
665  << "http-url: '" << url << "'\n"
666  << "http-proto: '" << proto << "'\n";
667  }
668 
669  // if (url == "/")
670  // url = "/index.htm";
671 
672  // spliturl( url, page, params ); ??
673  std::string::size_type ques = url.find( '?' );
674 
675  if ( ques == std::string::npos )
676  {
677  page = url;
678  query_string = "";
679  }
680  else
681  {
682  page = url.substr( 0, ques );
683  query_string = url.substr( ques + 1 );
684  }
685 
686  if ( Plib::systemstate.config.web_server_debug )
687  {
688  INFO_PRINT << "http-page: '" << page << "'\n"
689  << "http-params: '" << query_string << "'\n"
690  << "http-decode: '" << http_decodestr( query_string ) << "'\n";
691  }
692 
693  if ( !Plib::systemstate.config.web_server_password.empty() )
694  {
695  if ( !auth.empty() )
696  {
697  ISTRINGSTREAM is2( auth );
698  std::string _auth, type, coded_unpw, unpw;
699  is2 >> _auth >> type >> coded_unpw;
700  unpw = decode_base64( coded_unpw );
701  if ( Plib::systemstate.config.web_server_debug )
702  {
703  INFO_PRINT << "http-pw: '" << coded_unpw << "'\n"
704  << "http-pw-decoded: '" << unpw << "'\n";
705  }
706  if ( Plib::systemstate.config.web_server_password != unpw )
707  {
708  http_not_authorized( sck, url );
709  return;
710  }
711  }
712  else
713  {
714  http_not_authorized( sck, url );
715  return;
716  }
717  }
718 
719  if ( !legal_pagename( page ) )
720  {
721  // FIXME should probably be access denied
722  http_forbidden( sck, page );
723  return;
724  }
725 
726 
727  Plib::Package* pkg = nullptr;
728  std::string filename;
729  std::string pagetype;
730  std::string redirect_to;
731  if ( !decode_page( page, &pkg, &filename, &pagetype, &redirect_to ) )
732  {
733  http_not_found( sck, page );
734  return;
735  }
736  if ( !redirect_to.empty() )
737  {
738  http_redirect( sck, /*"http://" + host +*/ redirect_to );
739  return;
740  }
741 
742  if ( Plib::systemstate.config.web_server_debug )
743  INFO_PRINT << "Page type: " << pagetype << "\n";
744 
745  if ( pagetype == "ecl" )
746  {
747  // Note it takes ownership of the socket
748  start_http_script( sck, page, pkg, filename, query_string );
749  }
750  else if ( pagetype == "htm" || pagetype == "html" )
751  {
752  send_html( sck, page, filename );
753  }
754  else
755  {
756  std::string type = gamestate.mime_types[pagetype];
757  if ( type.length() > 0 )
758  {
759  send_binary( sck, page, filename, type );
760  }
761  else
762  {
763  POLLOG_INFO << "HTTP server: I can't handle pagetype '" << pagetype << "'\n";
764  }
765  }
766 }
767 
768 
769 #ifdef _WIN32
770 void init_http_thread_support() {}
771 #else
772 pthread_attr_t http_attr;
774 {
775  pthread_attr_init( &http_attr );
776  pthread_attr_setdetachstate( &http_attr, PTHREAD_CREATE_DETACHED );
777 }
778 #endif
779 
780 void test_decode( const char* page, bool result_expected, Plib::Package* pkg_expected,
781  const char* filename_expected, const char* pagetype_expected,
782  const char* redirect_to_expected )
783 {
784  Plib::Package* pkg = nullptr;
785  std::string filename;
786  std::string pagetype;
787  std::string redirect_to;
788  bool result;
789 
790  result = decode_page( page, &pkg, &filename, &pagetype, &redirect_to );
791  passert_always( result == result_expected );
792  if ( result )
793  {
794  assert( redirect_to == redirect_to_expected );
795  (void)redirect_to_expected;
796  if ( redirect_to.empty() )
797  {
798  passert_always( pkg == pkg_expected );
799  passert_always( filename == filename_expected );
800  passert_always( pagetype == pagetype_expected );
801  }
802  }
803 }
804 
806 {
807  /*
808  lock();
809  if (find_package( "testwww" ))
810  {
811  test_decode( "/", true, nullptr, "scripts/www/index.htm", "htm" );
812  test_decode( "/pkg/testwww1",
813  true, find_package( "testwww1" ), "pkg/test/testwww1/www/index.htm", "htm", "" );
814  test_decode( "/pkg/testwww1/noexist.ecl",
815  true, find_package( "testwww1" ), "www/noexist.ecl", "ecl", "" );
816  test_decode( "/pkg/testwww3",
817  true, find_package( "testwww3" ), "www/index.ecl", "ecl" );
818  }
819  unlock();
820  */
821 }
822 
823 
824 void http_thread( void )
825 {
826  test_decode();
827 
828 
831 
832  // if (1)
833  INFO_PRINT << "Listening for HTTP requests on port " << Plib::systemstate.config.web_server_port
834  << "\n";
835 
836  SOCKET http_socket = Network::open_listen_socket( Plib::systemstate.config.web_server_port );
837  if ( http_socket == INVALID_SOCKET )
838  {
839  ERROR_PRINT << "Unable to listen on socket: " << http_socket << "\n";
840  return;
841  }
842  fd_set listen_fd;
843  struct timeval listen_timeout = {0, 0};
844 
845  Pol::threadhelp::TaskThreadPool worker_threads( 2, "http" ); // two threads should be enough
846  while ( !Clib::exit_signalled )
847  {
848  int nfds = 0;
849  FD_ZERO( &listen_fd );
850 
851  FD_SET( http_socket, &listen_fd );
852 #ifndef _WIN32
853  nfds = http_socket + 1;
854 #endif
855 
856  int res;
857  do
858  {
859  listen_timeout.tv_sec = 5;
860  listen_timeout.tv_usec = 0;
861  res = select( nfds, &listen_fd, nullptr, nullptr, &listen_timeout );
862  } while ( res < 0 && !Clib::exit_signalled && socket_errno == SOCKET_ERRNO( EINTR ) );
863 
864  if ( res <= 0 )
865  {
867  continue;
868  }
869 
870  if ( FD_ISSET( http_socket, &listen_fd ) )
871  {
872  if ( Plib::systemstate.config.web_server_debug )
873  INFO_PRINT << "Accepting connection..\n";
874 
875  struct sockaddr client_addr; // inet_addr
876  socklen_t addrlen = sizeof client_addr;
877  SOCKET client_socket = accept( http_socket, &client_addr, &addrlen );
878  if ( client_socket == INVALID_SOCKET )
879  return;
880 
881  Network::apply_socket_options( client_socket );
882 
883  std::string addrstr = Network::AddressToString( &client_addr );
884  INFO_PRINT << "HTTP client connected from " << addrstr << "\n";
885 
886  worker_threads.push(
887  [=]() { http_func( client_socket ); } ); // copy socket into queue to keep it valid
888  }
889  }
890  gamestate.mime_types.clear(); // cleanup on exit
891 #ifdef _WIN32
892  closesocket( http_socket );
893 #else
894  close( http_socket );
895 #endif
896 }
897 
899 {
901 }
902 }
903 }
bool get_script_page_filename(const std::string &page, ScriptDef &sd)
Definition: polwww.cpp:367
void send_html(Clib::Socket &sck, const std::string &page, const std::string &filename)
Definition: polwww.cpp:565
ref_ptr< Bscript::EScriptProgram > find_script2(const ScriptDef &script, bool complain_if_not_found, bool cache_script)
Definition: scrstore.cpp:83
std::map< std::string, std::string > mime_types
Definition: uvars.h:228
void quickconfig(const Plib::Package *pkg, const std::string &name_ecl)
Definition: scrdef.cpp:112
void http_thread(void)
Definition: polwww.cpp:824
void apply_socket_options(SOCKET sck)
Definition: sockio.cpp:143
void setDebugLevel(DEBUG_LEVEL level)
Definition: executor.h:369
pthread_attr_t http_attr
Definition: polwww.cpp:772
std::string get_page_filename(const std::string &page)
Definition: polwww.cpp:468
SystemState systemstate
Definition: systemstate.cpp:12
#define INVALID_SOCKET
Definition: wnsckt.h:12
std::string desc() const
Definition: pkg.cpp:176
int SOCKET
Definition: wnsckt.h:10
void http_func(SOCKET client_socket)
Definition: polwww.cpp:618
Core::PolConfig config
Definition: systemstate.h:43
bool connected() const
Definition: wnsckt.cpp:308
void addModule(ExecutorModule *module)
Definition: executor.cpp:3032
SOCKET handle() const
Definition: wnsckt.cpp:112
char * binary(unsigned int val, int nbits)
T * get() const
Definition: refptr.h:176
#define POLLOG_INFO
Definition: logfacility.h:213
std::string decint(unsigned short v)
Definition: strutil.cpp:64
bool provides_system_home_page() const
Definition: pkg.cpp:27
std::string get_pagetype(const std::string &page)
Definition: polwww.cpp:349
void start_thread(void(*entry)(void *), const char *thread_name, void *arg)
Definition: threadhelp.cpp:233
#define POLLOG_ERROR
Definition: logfacility.h:207
Package * find_package(const std::string &pkgname)
Definition: pkg.cpp:33
void http_not_found(Clib::Socket &sck, const std::string &filename)
Definition: polwww.cpp:200
bool recvbyte(unsigned char *byte, unsigned int waitms)
Definition: wnsckt.cpp:354
bool http_readline(Clib::Socket &sck, std::string &s)
Definition: polwww.cpp:132
const std::string & name() const
Definition: scrdef.h:45
std::string decode_base64(const std::string &b64s)
Definition: polwww.cpp:293
bool decode_page(const std::string &ipage, Plib::Package **ppkg, std::string *pfilename, std::string *ppagetype, std::string *redirect_to)
Definition: polwww.cpp:474
void clear()
Definition: refptr.h:283
int filesize(const char *fname)
Definition: fileutil.cpp:127
bool exists() const
Definition: scrdef.cpp:126
UOExecutor * create_script_executor()
Definition: scrsched.cpp:644
Plib::Package * wwwroot_pkg
Definition: uvars.h:227
void http_redirect(Clib::Socket &sck, const std::string &new_url)
Definition: polwww.cpp:211
bool setProgram(EScriptProgram *prog)
Definition: executor.cpp:731
#define socket_errno
Definition: wnsckt.cpp:34
#define SOCKET_ERRNO(x)
Definition: wnsckt.cpp:33
const char * AddressToString(struct sockaddr *addr)
Definition: sockio.cpp:212
#define POLLOG
Definition: logfacility.h:219
void read_query_string(const std::string &query_string)
Definition: httpmod.cpp:166
unsigned short web_server_port
Definition: polcfg.h:46
void start_http_server()
Definition: polwww.cpp:898
GameState gamestate
Definition: uvars.cpp:74
void send_binary(Clib::Socket &sck, const std::string &page, const std::string &filename, const std::string &content_type)
Definition: polwww.cpp:585
SOCKET open_listen_socket(unsigned short port)
Definition: sockio.cpp:159
bool is_local() const
Definition: wnsckt.cpp:638
bool remove_prop(const char *propname, std::string *value)
Definition: cfgfile.cpp:128
void load_mime_config(void)
Definition: polwww.cpp:66
void http_forbidden(Clib::Socket &sck)
Definition: polwww.cpp:166
unsigned char cvt_8to6(char ch)
Definition: polwww.cpp:275
void http_writeline(Clib::Socket &sck, const std::string &s)
Definition: polwww.cpp:160
#define ISTRINGSTREAM
Definition: stlutil.h:73
void init_http_thread_support()
Definition: polwww.cpp:773
bool start_http_script(Clib::Socket &sck, const std::string &page, Plib::Package *pkg, const std::string &file_ecl, const std::string &query_string)
Definition: polwww.cpp:403
std::string http_decodestr(const std::string &s)
Definition: polwww.cpp:229
#define ERROR_PRINT
Definition: logfacility.h:230
void config_web_server()
Definition: polwww.cpp:107
void test_decode(const char *page, bool result_expected, Plib::Package *pkg_expected, const char *filename_expected, const char *pagetype_expected, const char *redirect_to_expected)
Definition: polwww.cpp:780
void schedule_executor(UOExecutor *ex)
Definition: scrsched.cpp:662
void push(const msg &msg)
simply fire and forget only the deconstructor ensures the msg to be finished
Definition: threadhelp.cpp:405
bool FileExists(const char *filename)
Definition: fileutil.cpp:118
bool read(ConfigElem &elem)
Definition: cfgfile.cpp:1015
#define passert_always(exp)
Definition: passert.h:80
#define INFO_PRINT
Definition: logfacility.h:223
Definition: berror.cpp:12
void send(const void *data, unsigned length)
Definition: wnsckt.cpp:553
std::atomic< bool > exit_signalled
time_t modified() const
Definition: cfgfile.cpp:591
bool legal_pagename(const std::string &page)
Definition: polwww.cpp:326
const std::string & dir() const
Definition: pkg.h:79
void http_not_authorized(Clib::Socket &sck, const std::string &)
Definition: polwww.cpp:188