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" 57 #pragma warning( disable : 4127 ) // conditional expression is constant (needed because of FD_SET) 64 using namespace threadhelp;
68 static time_t last_load = 0;
93 while ( cf.
read( elem ) )
95 std::string ext, mime;
117 POLLOG.Format(
"wwwroot package is {}\n" ) << pkg->
desc();
122 POLLOG.Format(
"Package {} also provides a wwwroot, ignoring\n" ) << pkg->
desc();
142 if ( s.length() > 3000 )
162 sck.
send( (
void*)s.c_str(),
static_cast<unsigned int>( s.length() ) );
171 http_writeline( sck,
"<HTML><HEAD><TITLE>403 Forbidden</TITLE></HEAD>" );
173 http_writeline( sck,
"You are forbidden to access this server." );
182 http_writeline( sck,
"<HTML><HEAD><TITLE>403 Forbidden</TITLE></HEAD>" );
184 http_writeline( sck,
"You are forbidden to access to " + filename +
" on this server." );
194 http_writeline( sck,
"<HTML><HEAD><TITLE>401 Unauthorized</TITLE></HEAD>" );
196 http_writeline( sck,
"You are not authorized to access that page." );
205 http_writeline( sck,
"<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>" );
207 http_writeline( sck,
"The requested URL " + filename +
" was not found on this server." );
219 http_writeline( sck,
"<HTML><HEAD><TITLE>301 Moved Permanently</TITLE></HEAD>" );
221 http_writeline( sck,
"The requested URL has been moved to " + new_url );
232 const char* t = s.c_str();
233 while ( t !=
nullptr && *t !=
'\0' )
237 decoded.append( 1,
' ' );
240 else if ( *t !=
'%' )
242 decoded.append( 1, *t );
248 if ( isxdigit( *( t + 1 ) ) && isxdigit( *( t + 2 ) ) )
250 char chH = *( t + 1 );
251 char chL = *( t + 2 );
254 if ( isdigit( chH ) )
255 ch = ( chH -
'0' ) << 4;
257 ch = ( (
static_cast<char>( tolower( chH ) ) -
'a' ) + 10 ) << 4;
259 if ( isdigit( chL ) )
262 ch |= ( tolower( chL ) -
'a' ) + 10;
264 decoded.append( 1, ch );
277 if ( ch >=
'A' && ch <=
'Z' )
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 ==
'+' )
285 else if ( ch ==
'/' )
287 else if ( ch ==
'=' )
296 const char* t = b64s.c_str();
303 c[1] = *t ? *t++ :
'\0';
304 c[2] = *t ? *t++ :
'\0';
305 c[3] = *t ? *t++ :
'\0';
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 );
329 for (
const char* t = page.c_str(); *t; ++t )
332 if ( isalnum( ch ) || ( ch ==
'/' ) || ( ch ==
'_' ) )
336 else if ( ( ch ==
'.' ) && ( isalnum( *( t + 1 ) ) ) )
351 std::string::size_type lastslash = page.rfind(
'/' );
352 std::string::size_type dotpos = page.rfind(
'.' );
354 if ( lastslash != std::string::npos && dotpos != std::string::npos && lastslash > dotpos )
357 if ( dotpos != std::string::npos )
359 return page.substr( dotpos + 1 );
369 if ( page.substr( 0, 5 ) ==
"/pkg/" )
372 auto pkgname_end = page.find_first_of(
'/', 5 );
373 if ( pkgname_end != std::string::npos )
375 std::string pkg_name = page.substr( 5, pkgname_end - 5 );
378 if ( pkg !=
nullptr )
380 sd.
quickconfig( pkg,
"www/" + page.substr( pkgname_end + 1 ) );
404 const std::string& file_ecl,
const std::string& query_string )
416 POLLOG.Format(
"WebServer: not found: {}\n" ) << page_sd.
name();
426 if ( program.
get() == nullptr )
470 std::string filename =
"scripts/www" + page;
475 std::string* ppagetype, std::string* redirect_to )
477 std::string page = ipage;
482 if ( page.substr( 0, 5 ) ==
"/pkg/" )
485 std::string::size_type pkgname_end = page.find_first_of(
'/', 5 );
487 if ( pkgname_end != std::string::npos )
489 pkgname = page.substr( 5, pkgname_end - 5 );
490 page = page.substr( pkgname_end );
494 pkgname = page.substr( 5 );
500 if ( pkg ==
nullptr )
503 filedir = pkg->
dir() +
"www";
514 filedir =
"scripts/www";
515 retdir =
"scripts/www";
519 std::string filename = filedir + page;
523 if ( pagetype ==
"" )
526 if ( page.empty() || page[page.size() - 1] !=
'/' )
533 test = filename +
"index.ecl";
548 if ( pagetype ==
"ecl" )
555 retdir = pkg->
dir() +
"www";
560 *pfilename = retdir + page;
561 *ppagetype = pagetype;
567 std::ifstream ifs( filename.c_str() );
574 while ( getline( ifs, t ) )
586 const std::string& content_type )
601 unsigned int cur_read = 0;
602 while ( sck.
connected() && ifs.good() && cur_read < fsize )
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() ) );
639 if ( tmpstr.empty() )
641 if ( strncmp( tmpstr.c_str(),
"GET", 3 ) == 0 )
643 if ( strncmp( tmpstr.c_str(),
"Authorization:", 14 ) == 0 )
645 if ( strncmp( tmpstr.c_str(),
"Host: ", 5 ) == 0 )
646 host = tmpstr.substr( 6 );
657 std::string query_string;
659 is >> cmd >> url >> proto;
664 <<
"http-host: '" << host <<
"'\n" 665 <<
"http-url: '" << url <<
"'\n" 666 <<
"http-proto: '" << proto <<
"'\n";
673 std::string::size_type ques = url.find(
'?' );
675 if ( ques == std::string::npos )
682 page = url.substr( 0, ques );
683 query_string = url.substr( ques + 1 );
689 <<
"http-params: '" << query_string <<
"'\n" 698 std::string _auth, type, coded_unpw, unpw;
699 is2 >> _auth >> type >> coded_unpw;
703 INFO_PRINT <<
"http-pw: '" << coded_unpw <<
"'\n" 704 <<
"http-pw-decoded: '" << unpw <<
"'\n";
728 std::string filename;
729 std::string pagetype;
730 std::string redirect_to;
731 if ( !
decode_page( page, &pkg, &filename, &pagetype, &redirect_to ) )
736 if ( !redirect_to.empty() )
743 INFO_PRINT <<
"Page type: " << pagetype <<
"\n";
745 if ( pagetype ==
"ecl" )
750 else if ( pagetype ==
"htm" || pagetype ==
"html" )
757 if ( type.length() > 0 )
763 POLLOG_INFO <<
"HTTP server: I can't handle pagetype '" << pagetype <<
"'\n";
775 pthread_attr_init( &http_attr );
776 pthread_attr_setdetachstate( &http_attr, PTHREAD_CREATE_DETACHED );
781 const char* filename_expected,
const char* pagetype_expected,
782 const char* redirect_to_expected )
785 std::string filename;
786 std::string pagetype;
787 std::string redirect_to;
790 result =
decode_page( page, &pkg, &filename, &pagetype, &redirect_to );
794 assert( redirect_to == redirect_to_expected );
795 (void)redirect_to_expected;
796 if ( redirect_to.empty() )
839 ERROR_PRINT <<
"Unable to listen on socket: " << http_socket <<
"\n";
843 struct timeval listen_timeout = {0, 0};
849 FD_ZERO( &listen_fd );
851 FD_SET( http_socket, &listen_fd );
853 nfds = http_socket + 1;
859 listen_timeout.tv_sec = 5;
860 listen_timeout.tv_usec = 0;
861 res = select( nfds, &listen_fd,
nullptr,
nullptr, &listen_timeout );
870 if ( FD_ISSET( http_socket, &listen_fd ) )
875 struct sockaddr client_addr;
876 socklen_t addrlen =
sizeof client_addr;
877 SOCKET client_socket = accept( http_socket, &client_addr, &addrlen );
884 INFO_PRINT <<
"HTTP client connected from " << addrstr <<
"\n";
892 closesocket( http_socket );
894 close( http_socket );
bool get_script_page_filename(const std::string &page, ScriptDef &sd)
void send_html(Clib::Socket &sck, const std::string &page, const std::string &filename)
ref_ptr< Bscript::EScriptProgram > find_script2(const ScriptDef &script, bool complain_if_not_found, bool cache_script)
std::map< std::string, std::string > mime_types
void quickconfig(const Plib::Package *pkg, const std::string &name_ecl)
void apply_socket_options(SOCKET sck)
void setDebugLevel(DEBUG_LEVEL level)
std::string get_page_filename(const std::string &page)
void http_func(SOCKET client_socket)
void addModule(ExecutorModule *module)
char * binary(unsigned int val, int nbits)
std::string decint(unsigned short v)
bool provides_system_home_page() const
std::string get_pagetype(const std::string &page)
void start_thread(void(*entry)(void *), const char *thread_name, void *arg)
Package * find_package(const std::string &pkgname)
void http_not_found(Clib::Socket &sck, const std::string &filename)
bool recvbyte(unsigned char *byte, unsigned int waitms)
bool http_readline(Clib::Socket &sck, std::string &s)
const std::string & name() const
std::string decode_base64(const std::string &b64s)
bool decode_page(const std::string &ipage, Plib::Package **ppkg, std::string *pfilename, std::string *ppagetype, std::string *redirect_to)
int filesize(const char *fname)
UOExecutor * create_script_executor()
Plib::Package * wwwroot_pkg
void http_redirect(Clib::Socket &sck, const std::string &new_url)
bool setProgram(EScriptProgram *prog)
const char * AddressToString(struct sockaddr *addr)
void read_query_string(const std::string &query_string)
unsigned short web_server_port
void send_binary(Clib::Socket &sck, const std::string &page, const std::string &filename, const std::string &content_type)
SOCKET open_listen_socket(unsigned short port)
bool remove_prop(const char *propname, std::string *value)
void load_mime_config(void)
void http_forbidden(Clib::Socket &sck)
unsigned char cvt_8to6(char ch)
void http_writeline(Clib::Socket &sck, const std::string &s)
void init_http_thread_support()
bool start_http_script(Clib::Socket &sck, const std::string &page, Plib::Package *pkg, const std::string &file_ecl, const std::string &query_string)
std::string http_decodestr(const std::string &s)
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)
void schedule_executor(UOExecutor *ex)
void push(const msg &msg)
simply fire and forget only the deconstructor ensures the msg to be finished
bool FileExists(const char *filename)
bool read(ConfigElem &elem)
#define passert_always(exp)
void send(const void *data, unsigned length)
std::atomic< bool > exit_signalled
bool legal_pagename(const std::string &page)
const std::string & dir() const
void http_not_authorized(Clib::Socket &sck, const std::string &)