diff options
Diffstat (limited to 'yhttpd/src')
60 files changed, 10575 insertions, 0 deletions
diff --git a/yhttpd/src/Makefile.in b/yhttpd/src/Makefile.in new file mode 100644 index 0000000..262f3a6 --- /dev/null +++ b/yhttpd/src/Makefile.in @@ -0,0 +1,21 @@ +SRCS=WILLBEADDEDBYCONFIGURE +OBJS=$(addprefix ../obj/,$(SRCS:.cpp=.o)) +CC=WILLBEADDEDBYCONFIGURE +LIBADD=`cat libs.add` +LDFLAGS=$(LIBADD) -lstdc++ +LDADD=-pthread -D_THREAD_SAFE -export-dynamic -ldl +INCLUDES=`cat includes.add` +CFLAGS=-fno-inline -fno-default-inline -frepo +all: yhttpd +$(SRCS): + $(CC) $(INCLUDES) $(CFLAGS) -c $*.cpp +infotext: + @echo Compiling base +yhttpd: infotext $(OBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LDADD) + @mv yhttpd ../bin + @echo -n "Size of linked executable: " + @du -hc ../bin/yhttpd | tail -n 1 +clean: + @echo Cleaning base obj + @if test -d ../obj; then rm -Rf ../obj; fi diff --git a/yhttpd/src/cli/cli.cpp b/yhttpd/src/cli/cli.cpp new file mode 100644 index 0000000..dbbc729 --- /dev/null +++ b/yhttpd/src/cli/cli.cpp @@ -0,0 +1,300 @@ +#ifndef CLI_CPP +#define CLI_CPP + +#include "cli.h" + +#ifdef CLI +using namespace std; + +cli::cli( ) +{ +#ifdef NCURSES + start(); +#endif +} + +cli::~cli() +{} + +int +cli::parse_input( string s_input ) +{ + string s_param = ""; + unsigned i_pos = s_input.find_first_of(" "); + + if ( i_pos != string::npos ) + { + s_param = s_input.substr(i_pos+1); + s_input = s_input.substr(0, i_pos); + } + + if ( s_input.compare("help") == 0 || s_input.compare("h") == 0) + { + cout << CLIPRMO << "COMMAND LINE INTERFACE HELP MENU" << endl + << CLIPRMO << " !command - Uses system to run a command" << endl; +#ifdef DEBUG + + cout << CLIPRMO << " (d)ebug - Starts debug routine (cli.cpp)" << endl; +#endif + + cout << CLIPRMO << " (du)mp [part] - Prints out a dump of the data structure" << endl; + cout << CLIPRMO << " (Run without part to list all possibilities)" << endl; + cout << CLIPRMO << " (e)cho VAR - Prints out configuration value of VAR" << endl; + cout << CLIPRMO << " Wildcards can be used too, example: echo http*" << endl; +#ifdef NCURSES + + cout << CLIPRMO << " (ex)it - Quits CLI mode and respawns ncurses mode" << endl; +#endif + + cout << CLIPRMO << " (h)elp - Prints out this help!" << endl; +#ifdef EXPERIM + + cout << CLIPRMO << " (re)conf - Reloads configuration (EXPERIMENTAL)" << endl; +#endif + + cout << CLIPRMO << " (r)usage - Shows current resource usage" << endl + << CLIPRMO << " (ru)sageh - Shows resource usage history (yhttpd needs to run > 1 day)" << endl + << CLIPRMO << " (set) VAR VAL - Sets configuration value VAR to VAL" << endl + << CLIPRMO << " (sh)ell - Runs a system shell" << endl + << CLIPRMO << " (s)hutdown - Shuts down the whole server" << endl + << CLIPRMO << " (t)ime - Prints out time and uptime" << endl; + cout << CLIPRMO << " (u)nset VAR - Deletes configuration value VAR" << endl + << CLIPRMO << " (v)ersion - Prints out version" << endl; + cout << CLIPRMI; + } + else if( s_input.at(0) == '!' ) + { + system( s_input.substr(1).c_str() ); + cout << CLIPRMI; + } + +#ifdef DEBUG + else if ( s_input.compare("d") == 0 || s_input.compare("debug") == 0 ) + { + debug_routine(); + cout << CLIPRMI; + } +#endif + else if ( s_input.compare("du") == 0 || s_input.compare("dump") == 0 ) + { + dump d(vectorize(s_param)); + cout << CLIPRMI; + } + else if( s_input.compare("echo") == 0 || s_input.compare("e") == 0 ) + { + string s_val; + + // Check wildcards + unsigned i_pos = s_param.find("*"); + if ( i_pos != string::npos ) + { + s_param = s_param.substr( 0, i_pos ); + vector<string>* p_vec = wrap::CONF->get_key_vector(); + sort(p_vec->begin(), p_vec->end()); + vector<string>::iterator iter; + + for ( iter = p_vec->begin(); iter != p_vec->end(); iter++ ) + if ( iter->find(s_param) == 0 ) + s_val.append( *iter + " := " + wrap::CONF->get_elem(*iter) + "\n" + CLIPRMO ); + delete p_vec; + } + else + { + s_val = wrap::CONF->get_elem(s_param); + } + + if( s_val.empty() ) + s_val = "Value not set"; + + cout << CLIPRMO << s_val << endl; + cout << CLIPRMI; + } + + +#ifdef NCURSES + else if( s_input.compare("exit") == 0 || s_input.compare("ex") == 0 ) + { + return 0; + } +#endif + + +#ifdef EXPERIM + else if( s_input.compare("reconf") == 0 || s_input.compare("re") == 0 ) + { + wrap::HTTPD->reconf(); + cout << CLIPRMI; + } +#endif + + else if( s_input.compare("rusage") == 0 || s_input.compare("r") == 0 ) + { + print_rusage(); + cout << CLIPRMI; + } + else if( s_input.compare("ru") == 0 || s_input.compare("rusageh") == 0 ) + { + cout << wrap::STAT->get_rusage_history( "ru_maxrss", string(CLIPRMO) + " " ); + cout << CLIPRMI; + } + else if( s_input.compare("set") == 0 ) + { + string s_varname = ""; + i_pos = s_param.find_first_of(" "); + + if ( i_pos != string::npos ) + { + s_varname = s_param.substr(0, i_pos); + + if ( s_param.length() > i_pos+1 ) + s_param = s_param.substr(i_pos+1); + + else + s_param = ""; + } + + string s_old_val = wrap::CONF->get_elem(s_varname); + + if ( !s_old_val.empty() ) + { + cout << CLIPRMO << "Old value: " << s_old_val << endl; + wrap::CONF->del_elem(s_varname); + } + + wrap::CONF->add_elem(s_param, s_varname); + cout << CLIPRMO << "New value: " << s_param << endl; + cout << CLIPRMI; + } + else if( s_input.compare("shell") == 0 || s_input.compare("sh") == 0 ) + { + cout << CLIPRMO << CLISHEL << endl; + system(wrap::CONF->get_elem("httpd.system.shell").c_str()); + cout << CLIPRMO << CLIWELC << endl; + cout << CLIPRMI; + } + else if( s_input.compare("shutdown") == 0 || s_input.compare("s") == 0 ) + { + exit(0); + } + else if( s_input.compare("t") == 0 || s_input.compare("time") == 0 ) + { + cout << CLIPRMO << "Time: " << wrap::TIMR->get_time() << endl + << CLIPRMO << "Uptime: " << wrap::TIMR->get_uptime() << endl; + cout << CLIPRMI; + } + + + else if( s_input.compare("u") == 0 || s_input.compare("unset") == 0 ) + { + if (!s_param.empty()) + { + string s_old_val = wrap::CONF->get_elem(s_param); + if ( !s_old_val.empty() ) + { + cout << CLIPRMO << "Deleted variable " << s_param << " with value: " << s_old_val << endl; + wrap::CONF->del_elem(s_param); + } + else + { + cout << CLIPRMO << "Variable " << s_param << " was not set" << endl; + } + cout << CLIPRMI; + } + } + else if( s_input.compare("v") == 0 || s_input.compare("version") == 0 ) + { + cout << CLIPRMO << tool::yhttpd_version() << " " << UNAME << endl; + cout << CLIPRMI; + } + else + { + cout << CLIPRMO << CLIHELP << "\"" << s_input << "\"" << endl; + cout << CLIPRMI; + } + + return 1; +} + +void +#ifndef NCURSES +cli::start(void* p_void) +#else +cli::start() +#endif +{ + cout << CLIPRMO << CLIWELC << endl; + + string s_input; + cout << CLIPRMI; + + do + { + getline(cin, s_input); + } + while( parse_input(s_input) ); +} + +void +cli::print_rusage() +{ + rusage* p_rusage = new rusage; + getrusage( RUSAGE_SELF, p_rusage ); + + cout << CLIPRMO << "ru_maxrss: " << p_rusage->ru_maxrss << "\t(max resident set size)" << endl + << CLIPRMO << "ru_ixrss: " << p_rusage->ru_ixrss << "\t(integral shared text memory size)" << endl + << CLIPRMO << "ru_idrss: " << p_rusage->ru_idrss << "\t(integral unshared data size)" << endl + << CLIPRMO << "ru_isrss: " << p_rusage->ru_isrss << "\t(integral unshared stack size)" << endl + << CLIPRMO << "ru_minflt: " << p_rusage->ru_minflt << "\t(page reclaims)" << endl + << CLIPRMO << "ru_majflt: " << p_rusage->ru_majflt << "\t(page faults)" << endl + << CLIPRMO << "ru_nswap: " << p_rusage->ru_nswap << "\t(swaps)" << endl + << CLIPRMO << "ru_inblock: " << p_rusage->ru_inblock << "\t(block input operations)" << endl + << CLIPRMO << "ru_oublock: " << p_rusage->ru_oublock << "\t(block output operations)" << endl + << CLIPRMO << "ru_msgsnd: " << p_rusage->ru_msgsnd << "\t(messages sent)" << endl + << CLIPRMO << "ru_msgrcv: " << p_rusage->ru_msgrcv << "\t(messages received)" << endl + << CLIPRMO << "ru_nsignals: " << p_rusage->ru_nsignals << "\t(signals received)" << endl + << CLIPRMO << "ru_nvcsw: " << p_rusage->ru_nvcsw << "\t(voluntary context switches)" << endl + << CLIPRMO << "ru_nivcsw: " << p_rusage->ru_nivcsw << "\t(involuntary context switches)" << endl; + + delete p_rusage; +} + +vector<string> +cli::vectorize(string s_param) +{ + vector<string> vec_ret; + unsigned i_pos; + + for (i_pos = s_param.find(" "); + i_pos != string::npos; + i_pos = s_param.find(" ")) + { + vec_ret.push_back(s_param.substr(0, i_pos)); + s_param = s_param.substr(i_pos+1); + } + + if (!s_param.empty()) + vec_ret.push_back(s_param); + + return vec_ret; +} + +#ifdef DEBUG +void +cli::debug_routine() +{ + rusage* p_rusage = new rusage; + for(;;) + { + /* + ossl *p_tmp = new ossl; + getrusage( RUSAGE_SELF, p_rusage ); + cout << CLIPRMO << "ru_maxrss: " << p_rusage->ru_maxrss << "\t(max resident set size)" << endl; + delete p_tmp; + */ + } + delete p_rusage; +} +#endif + +#endif +#endif diff --git a/yhttpd/src/cli/cli.h b/yhttpd/src/cli/cli.h new file mode 100644 index 0000000..8ec75cb --- /dev/null +++ b/yhttpd/src/cli/cli.h @@ -0,0 +1,61 @@ +#include <stdio.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <iostream> +#include <vector> + +#ifndef RUSAGE_SELF +#define RUSAGE_SELF 0 +#endif +#ifndef RUSAGE_CHILDREN +#define RUSAGE_CHILDREN -1 +#endif + +#include "../incl.h" + +#ifndef CLI_H +#define CLI_H +#ifdef CLI + +#ifndef NCURSES +#include "../thrd/thro.h" +#endif + +#include "../monitor/dump.h" + +using namespace std; + +#ifndef NCURSES +class cli : public thro +{ +#else +class cli +{ +#endif +private: + int parse_input(string s_input); + vector<string> vectorize(string s_param); + +public: + cli( ); + ~cli( ); + +#ifdef DEBUG + + void debug_routine(); +#endif + + void print_rusage(); + +#ifndef NCURSES + + void start(void* p_void); +#else + + void start(); +#endif +}; + +#endif +#endif diff --git a/yhttpd/src/conf/conf.cpp b/yhttpd/src/conf/conf.cpp new file mode 100644 index 0000000..25884ca --- /dev/null +++ b/yhttpd/src/conf/conf.cpp @@ -0,0 +1,186 @@ +#ifndef CONF_CPP +#define CONF_CPP + +#include <fstream> +#include "conf.h" + +using namespace std; + +conf::conf( string s_conf, map<string,string>* p_main_loop_params ) : name::name( s_conf ) +{ + string s_check[] = { + get_name(), + string(getenv("HOME"))+string("/.yhttpd/") + get_name(), + string("./etc/") + get_name(), + string("/etc/") + get_name(), + string(PREFIX+string("etc/")+get_name()) }; + + string s_config; + + for ( int i = 0; i < 4; ++i ) + { + cout << "Checking for " << s_check[i]; + ifstream if_check( s_check[i].c_str() ); + if( if_check ) + { + s_config = s_check[i]; + if_check.close(); + cout << "... ok!" << endl; + break; + } + cout << "... not ok!" << endl; + } + + if ( s_config.empty() ) + { + cout << CFILEFA << endl; + exit(1); + } + + else + { + cout << CFILEOK << "..." << endl; + } + + p_xml = new TiXmlDocument(s_config.c_str()); + TiXmlBase::SetCondenseWhiteSpace( false ); + + if ( !p_xml->LoadFile() ) + { + cout << XMLER1 << endl; + exit(1); + } + + vector<string> vec_string; + parse_xml(p_xml, &vec_string); + + shashmap<string>::add_elem_insecure(tool::yhttpd_version(), "yhttpd.version"); + + // Overrides yhttpd.conf values with command line options (yhttpd -o key1 value1 -o key2 value2 ...) + map<string,string>::iterator iter; + for ( iter = p_main_loop_params->begin(); iter != p_main_loop_params->end(); iter++ ) + { + shashmap<string>::del_elem_insecure(iter->first); + shashmap<string>::add_elem_insecure(iter->second, iter->first); + } + + delete p_xml; +} + +void +conf::parse_xml(TiXmlNode* p_node, vector<string>* p_vec) +{ + if ( p_node->NoChildren() ) + return; + + for ( TiXmlNode* p_child = p_node->FirstChild(); p_child; p_child = p_child->NextSibling() ) + { + //cout << p_vec->size() << ": (Value:" << p_child->Value() << ") (Type:" << p_child->Type() << ")" << endl; + + if ( strcmp(p_child->Value(),"config") == 0 ) + { + parse_xml(p_child, p_vec); + } + + else if ( strcmp(p_child->Value(),"category") == 0 ) + { + p_vec->push_back(p_child->ToElement()->Attribute("name")); + parse_xml(p_child, p_vec); + p_vec->pop_back(); + } + + else if ( strcmp(p_child->Value(),"option") == 0 ) + { + string s_option_name = ""; + + for ( vector<string>::iterator iter = p_vec-> + begin(); + iter != p_vec->end(); + ++iter ) + s_option_name.append(*iter + "."); + + TiXmlElement* p_element = p_child->ToElement(); + exit_if_xml_error(); + + s_option_name.append(p_element->Attribute("name")); + +#ifdef VERBOSE + cout << XMLREAD << s_option_name; +#endif + + TiXmlNode *p_node_value = p_element->FirstChild("value"); + TiXmlNode *p_node_descr = p_element->FirstChild("descr"); + + TiXmlText* p_text_value = NULL; + TiXmlText* p_text_descr = NULL; + + if ( p_node_value && p_node_value->FirstChild() + ) + p_text_value = p_node_value->FirstChild()->ToText(); + + if ( p_node_descr && p_node_descr->FirstChild() + ) + p_text_descr = p_node_descr->FirstChild()->ToText(); + + if ( !p_text_value ) + continue; + +#ifdef VERBOSE + cout << " := " << p_text_value->Value() << endl; +#endif + + shashmap<string>::add_elem_insecure(p_text_value->Value(), s_option_name); + + if ( !p_text_descr ) + continue; + + s_option_name.append(".descr"); +#ifdef VERBOSE + cout << XMLREAD << s_option_name << endl; +#endif + shashmap<string>::add_elem_insecure(p_text_descr->Value(), s_option_name); + } + } + + exit_if_xml_error() + ; +} + +conf::~conf() +{ + delete p_xml; +} + +void +conf::exit_if_xml_error() const +{ + if ( p_xml->Error() ) + { + cout << XMLERR << p_xml->ErrorDesc() << endl; + exit(1); + } +} + + +int +conf::get_int(string s_key) +{ + return tool::string2int(get_elem(s_key)); +} + +vector<string> +conf::get_vector(string s_key) +{ + vector<string> vec_ret; + string s_val = get_elem(s_key); + + for (unsigned i_pos = s_val.find(" "); i_pos != string::npos; i_pos = s_val.find(" ")) + { + vec_ret.push_back(s_val.substr(0, i_pos)); + s_val = s_val.substr(i_pos+1); + } + + vec_ret.push_back(s_val); + return vec_ret; +} +#endif diff --git a/yhttpd/src/conf/conf.h b/yhttpd/src/conf/conf.h new file mode 100644 index 0000000..b083d8c --- /dev/null +++ b/yhttpd/src/conf/conf.h @@ -0,0 +1,29 @@ +#ifndef CONF_H +#define CONF_H + +class conf; // Predefine for nmap.tmpl + +#include <map> +#include "../incl.h" +#include "../maps/shashmap.h" +#include "../name.h" +#include "../contrib/xml/tinyxml.h" + +using namespace std; + +class conf : public shashmap<string>, name +{ +private: + TiXmlDocument* p_xml; + void exit_if_xml_error() const; + void parse_xml( TiXmlNode* p_node, vector<string>* p_vec); + +public: + conf(string s_conf, map<string,string>* p_main_loop_params); + ~conf(); + + int get_int(string s_key); + vector<string> get_vector(string s_key); +}; + +#endif diff --git a/yhttpd/src/configure b/yhttpd/src/configure new file mode 100755 index 0000000..54cf650 --- /dev/null +++ b/yhttpd/src/configure @@ -0,0 +1,350 @@ +#!/bin/sh + +# +# The yhttpd Project (2003 - 2005) +# + +if ! ../scripts/checkperl.sh +then + exit 1 +fi + +if ! test -f ../g++.version +then + echo You need to run ./configure of the top level source dir first + exit 1 +fi + +perl -e ' + use strict; + $|=1; + + my %libadd; + my %incadd; + my $deepness = 500; + + my @headers = ( + "dlfcn.h", + "pthread.h", + "netinet/in.h", + "time.h", + "ncurses.h", + "openssl/ssl.h", + "::test::ext/hash_map" + ); + + my @libs = ( + "libncurses.so", + "libssl.so", + "libcrypto.so" + ); + + my @headerpaths = ( + $ENV{HOME}."/include", + $ENV{HOME}."/usr/include", + "/include", + "/usr/include", + "/usr/local/include", + "/usr/lib/", + "/usr/pkg/include", + "/opt/include", + "/opt/local/include" + ); + + my @libpaths = ( + $ENV{HOME}."/lib", + $ENV{HOME}."/usr/lib", + "/lib", + "/usr/lib", + "/usr/local/lib", + "/usr/pkg/lib", + "/opt/lib", + "/opt/local/lib" + ); + + my %dependfiles = ( + database => ["data"], + ycurses => ["curses", "ycui.cpp", "ycui.h"], + logging => ["logd.cpp", "logd.h"], + cli => ["cli"], + opnssl => ["sock/sslsock.cpp", "sock/sslsock.h"] + ); + + open FILE, "glob.h" or die "glob.h: $!\n"; + while(<FILE>) { + if ( /\/\/#define DATABASE/ ) { + remove_from_array("mysql/mysql.h",\@headers); + remove_from_array("libmysqlclient.so",\@libs); + mkdir "../backuped" unless -d "../backuped"; + `mv $_ ../backuped` for @{$dependfiles{database}}; + } + + elsif ( /^#define DATABASE/ && !-d "data") { + `mv ../backuped/$_ .` for @{$dependfiles{database}}; + } + + if ( /\/\/#define LOGGING/ ) { + mkdir "../backuped" unless -d "../backuped"; + `mv $_ ../backuped` for @{$dependfiles{logging}}; + } + + elsif ( /^#define LOGGING/ && !-f "logd.cpp") { + `mv ../backuped/$_ .` for @{$dependfiles{logging}}; + } + + if ( /\/\/#define CLI/ ) { + mkdir "../backuped" unless -d "../backuped"; + `mv $_ ../backuped` for @{$dependfiles{cli}}; + } + + elsif ( /^#define CLI/ && !-d "cli") { + `mv ../backuped/$_ .` for @{$dependfiles{cli}}; + } + + if ( /\/\/#define YCURSES/ ) { + for ("ncurses", "menu", "panel") { + remove_from_array("$_.h",\@headers); + remove_from_array("lib$_.so",\@libs); + } + + mkdir "../backuped" unless -d "../backuped"; + `mv $_ ../backuped` for @{$dependfiles{ycurses}}; + } + + elsif ( /^#define YCURSES/ && !-d "curses") { + `mv ../backuped/$_ .` for @{$dependfiles{ycurses}}; + } + + if ( /\/\/#define OPENSSL/ ) { + remove_from_array("openssl/ssl.h",\@headers); + remove_from_array("lib$_.so",\@libs) for ("ssl", "crypto"); + } + } + close FILE; + + if ( defined $ENV{YHTTPDHEADERPATHS} ) { + map { print "Adding $_...\n"; unshift @headerpaths, $_ } + split /:/, $ENV{YHTTPDHEADERPATHS}; + } + + if ( defined $ENV{YHTTPDLIBPATHS} ) { + map { print "Adding $_...\n"; unshift @libpaths, $_ } + split /:/, $ENV{YHTTPDLIBPATHS}; + } + + print "Headers:\n"; + + my $testit = 0; + map { $incadd{&check($deepness, $_, @headerpaths)}++ } + @headers; + + $testit = 0; + print "Libraries:\n"; + map { $libadd{&check($deepness, $_, @libpaths)}++ } + @libs; + + my $incadd = &make_add("-I", \%incadd); + my $libadd = &make_add("-L", \%libadd); + + for ( @libs ) { $libadd .= "-l$_ " if s/^lib// and s/\.so$//; } + + print "Incadd: $incadd\n"; + print "Libadd: $libadd\n"; + + `echo $incadd > includes.add`; + `echo $libadd > libs.add`; + + print "Creating new base Makefile...\n"; + unlink("Makefile") if -f "Makefile"; + unlink("../err") if -f "../err"; + open Fin, "Makefile.in" or die "Makefile.in: $!\n"; + open Fout, ">Makefile" or die "Makefile: $!\n"; + + my $cpp = `echo *.cpp */*.cpp contrib/*/*.cpp | sort`; + my $compiler = `tail -n 1 ../g++.version`; + my $version = `tail -n 2 ../g++.version | head -n 1`; + my $uname = `uname -srm`; + my $compopt = join "; ", split /\n/, `cat ../g++.version`; + + chomp $uname; + chomp $compopt; + + print "Configuring for $uname...\n"; + chomp $cpp; + chomp $version; + + while (<Fin>) { + s/^(CC=).*\n/$1$compiler/; + s/^(SRCS=).*/$1$cpp/; + s/ -frepo//; # unless $version =~ /3\.4/; + if ( $uname !~ /Linux/i ) { + print "Disabling -ldl flag...\n" if s/ -ldl//; + } + print Fout; + } + close Fin; + + my $args = join(" -",@ARGV); + $args = "-".$args unless $args eq ""; + + for my $cppfile (split / /, $cpp) { + my $ofile = $cppfile; + $ofile =~ s/\.cpp/\.o/; + print Fout "../obj/$ofile: $cppfile\n"; + print Fout "\t\@if ! test -d `dirname ../obj/$ofile`; then mkdir -p `dirname ../obj/$ofile`; fi\n"; + my $class = $ofile; + $class =~ s/\.o//; + + my $text; + if ( $class =~ /contrib\/.+/ ) { + my $dirname = `dirname $class`; + $text = "\t\@echo -n \"Contributed class $class \"\n"; + } + + else { + $text = "\t\@echo -n \"Base class $class \"\n"; + } + + print Fout "\t\@\$(CC) \$(CFLAGS) \$(INCLUDES) $args -c -o ../obj/$ofile $cppfile\n"; + print Fout $text."\t\@du -hc ../obj/$ofile | tail -n 1 | sed s/total// | sed \"s/ //g\"\n"; + } + + close Fout; + + open F, "msgs.h" or die "msgs.h: $!\n"; + my @msgs = <F>; + close F; + unlink("msgs.h"); + open F, ">msgs.h" or die "msgs.h: $!\n"; + + for (@msgs) { + s/(UNAME)(.+)$/UNAME "$uname"/; + s/(COMPOPT)(.+)$/COMPOPT "$compopt"/; + print F; + } + close F; + + if ( -d "mods" ) { + chdir("mods"); + my $cflags = "-fno-inline -fno-default-inline"; + + $cflags .= " -nostdlib" if $uname =~ /FreeBSD/i && `uname -r` =~ /^4\./; + + system("echo $cflags > cflags.add"); + system("./configure"); + chdir(".."); + } + + sub remove_from_array { + my $elem = shift; + my $array = shift; + + for ( my $i = 0; $i <= $#$array; ++$i ) { + if ( $$array[$i] eq $elem ) { + splice(@$array,$i,1); + last; + } + } # for + } + + sub check { + my $deep = shift; + + if ($deep == 0) { + print "Looking too deep! ($deepness)\n"; + exit(1); + } + + my $check = shift; + $testit = 1 if $check =~ s/::test:://; + + my $print = 1; + if ( $_[-1] eq "subsearch" ) { + $print = 0; + pop(@_); + } + + if ($print) { + print "Checking for $check..."; + print "\n" if $testit; + } + + for (@_) { + if ( -f "$_/$check" ) { + if ($testit) { + return $_ if test_include($_, $check); + return ""; + + } else { + print "OK\n"; + return "" if $_ eq "/usr/lib" or $_ eq "/usr/include"; + return $_; + } + } + } + + for (@_) { + next unless -d $_; + opendir D, $_ or warn "$_: $!\n"; + my @dir = readdir(D); + closedir D; + + for my $dir ( @dir ) { + next if $dir =~ /^\.+$/ or !-d "$_/$dir"; + my $path = &check($deep-1, $check, "$_/$dir", "subsearch"); + return $path if $path ne ""; + } + } + + if ($print) { + print "NOT OK\n"; + print "Please make sure that you have the needed software installed!\n"; + print "If you have a special path for your includes then edit src/configure!\n"; + print "Or set the environment variables YHTTPDHEADERPATHS and YHTTPDLIBPATHS.\n"; + print " Example: setenv YHTTPDHEADERPATHS \"/your/header/includes:/a/includes\"\n"; + print "(The environment variables have to be seperated by an :)\n"; + print "PS: You may use the locate and/or find command to search for files.\n"; + exit(1); + } + + `touch ../err`; + return ""; + } + + sub make_add { + my $flag = shift; + my $add = shift; + my $ret = ""; + + for (reverse keys %$add) { + next unless /.+/; + $ret .= "$flag$_ "; + } + return $ret; + } + + sub test_include { + my $shift = shift; + my $check = shift; + my $return = 0; + print "Testing $shift/$check..."; + + `echo "\#include \\"maps/hashmap.h\\"" > __test.cpp`; + `echo "int main(void){return 0;}" >> __test.cpp`; + my $cmd = "`tail -n 1 ../g++.version` -I$shift __test.cpp -o /dev/null 2>/dev/null"; + system $cmd; + + unless ($?) { + print "OK\n"; + $return = 1; + + } else { + print "Not OK\n"; + } + + unlink "__test.cpp" if -f "__test.cpp"; + return $return; + } + + exit(0); +' `echo "$*" | sed "s/-//g"` diff --git a/yhttpd/src/contrib/README b/yhttpd/src/contrib/README new file mode 100644 index 0000000..105f25d --- /dev/null +++ b/yhttpd/src/contrib/README @@ -0,0 +1,5 @@ +This directory includes source code which has been included directly into yhttpd but is not +programmed by the yhttpd project explicitly which means the source code here is from extern. + +Used versions: +tinyxml 2.3.2 diff --git a/yhttpd/src/contrib/xml/README b/yhttpd/src/contrib/xml/README new file mode 100644 index 0000000..7da9dfd --- /dev/null +++ b/yhttpd/src/contrib/xml/README @@ -0,0 +1,504 @@ +ATTENTION: + +This version of TinyXML has ben very little modified by +Paul C. Buetow in 2004 to fit the yhttpd project. + +To get the original source go to +http://www.sourceforge.net/projects/tinyxml + +/** @mainpage + +<h1> TinyXml </h1> + +TinyXml is a simple, small, C++ XML parser that can be easily +integrating into other programs. + + +<h2> What it does. </h2> + +In brief, TinyXml parses an XML document, and builds from that a +Document Object Model (DOM) that can be read, modified, and saved. + +XML stands for "eXtensible Markup Language." It allows you to create +your own document markups. Where HTML does a very good job of marking +documents for browsers, XML allows you to define any kind of document +markup, for example a document that describes a "to do" list for an +organizer application. XML is a very structured and convenient format. +All those random file formats created to store application data can +all be replaced with XML. One parser for everything. + +The best place for the complete, correct, and quite frankly hard to +read spec is at <a href="http://www.w3.org/TR/2004/REC-xml-20040204/"> +http://www.w3.org/TR/2004/REC-xml-20040204/</a>. An intro to XML +(that I really like) can be found at +<a href="http://skew.org/xml/tutorial/">http://skew.org/xml/tutorial</a>. + +There are different ways to access and interact with XML data. +TinyXml uses a Document Object Model (DOM), meaning the XML data is parsed +into a C++ objects that can be browsed and manipulated, and then +written to disk or another output stream. You can also construct an XML document from +scratch with C++ objects and write this to disk or another output +stream. + +TinyXml is designed to be easy and fast to learn. It is two headers +and four cpp files. Simply add these to your project and off you go. +There is an example file - xmltest.cpp - to get you started. + +TinyXml is released under the ZLib license, +so you can use it in open source or commercial code. The details +of the license are at the top of every source file. + +TinyXml attempts to be a flexible parser, but with truly correct and +compliant XML output. TinyXml should compile on any reasonably C++ +compliant system. It does not rely on exceptions or RTTI. It can be +compiled with or without STL support. TinyXml fully supports +the UTF-8 encoding, and the first 64k character entities. + + +<h2> What it doesn't do. </h2> + +It doesnt parse or use DTDs (Document Type Definitions) or XSLs +(eXtensible Stylesheet Language.) There are other parsers out there +(check out www.sourceforge.org, search for XML) that are much more fully +featured. But they are also much bigger, take longer to set up in +your project, have a higher learning curve, and often have a more +restrictive license. If you are working with browsers or have more +complete XML needs, TinyXml is not the parser for you. + +The following DTD syntax will not parse at this time in TinyXml: + +@verbatim + <!DOCTYPE Archiv [ + <!ELEMENT Comment (#PCDATA)> + ]> +@endverbatim + +because TinyXml sees this as a !DOCTYPE node with an illegally +embedded !ELEMENT node. This may be addressed in the future. + +<h2> Code Status. </h2> + +TinyXml is mature, tested code. It is very stable. If you find +bugs, please file a bug report is on the sourceforge web site +(www.sourceforge.net/projects/tinyxml). +We'll get them straightened out as soon as possible. + +There are some areas of improvement; please check sourceforge if you are +interested in working on TinyXml. + + +<h2> Features </h2> + +<h3> Using STL </h3> + +TinyXml can be compiled to use or not use STL. When using STL, TinyXml +uses the std::string class, and fully supports std::istream, std::ostream, +operator<<, and operator>>. Many API methods have both 'const char*' and +'const std::string&' forms. + +When STL support is compiled out, no STL files are included whatsover. All +the string classes are implemented by TinyXml itself. API methods +all use the 'const char*' form for input. + +Use the compile time #define: + + TIXML_USE_STL + +to compile one version or the other. This can be passed by the compiler, +or set as the first line of "tinyxml.h". + +Note: If compiling the test code in Linux, setting the environment +variable TINYXML_USE_STL=YES/NO will control STL compilation. In the +Windows project file, STL and non STL targets are provided. In your project, +its probably easiest to add the line "#define TIXML_USE_STL" as the first +line of tinyxml.h. + +<h3> UTF-8 </h3> + +TinyXml supports UTF-8 allowing to manipulate XML files in any language. TinyXml +also supports "legacy mode" - the encoding used before UTF-8 support and +probably best described as "extended ascii". + +Normally, TinyXml will try to detect the correct encoding and use it. However, +by setting the value of TIXML_DEFAULT_ENCODING in the header file, TinyXml +can be forced to always use one encoding. + +TinyXml will assume Legacy Mode until one of the following occurs: +<ol> + <li> If the non-standard but common "UTF-8 lead bytes" (0xef 0xbb 0xbf) + begin the file or data stream, TinyXml will read it as UTF-8. </li> + <li> If the declaration tag is read, and it has an encoding="UTF-8", then + TinyXml will read it as UTF-8. </li> + <li> If the declaration tag is read, and it has no encoding specified, then + TinyXml will read it as UTF-8. </li> + <li> If the declaration tag is read, and it has an encoding="something else", then + TinyXml will read it as Legacy Mode. In legacy mode, TinyXml will + work as it did before. It's not clear what that mode does exactly, but + old content should keep working.</li> + <li> Until one of the above criteria is met, TinyXml runs in Legacy Mode.</li> +</ol> + +What happens if the encoding is incorrectly set or detected? TinyXml will try +to read and pass through text seen as improperly encoded. You may get some strange +results or mangled characters. You may want to force TinyXml to the correct mode. + +<b> You may force TinyXml to Legacy Mode by using LoadFile( TIXML_ENCODING_LEGACY ) or +LoadFile( filename, TIXML_ENCODING_LEGACY ). You may force it to use legacy mode all +the time by setting TIXML_DEFAULT_ENCODING = TIXML_ENCODING_LEGACY. Likewise, you may +force it to TIXML_ENCODING_UTF8 with the same technique.</b> + +For English users, using English XML, UTF-8 is the same as low-ASCII. You +don't need to be aware of UTF-8 or change your code in any way. You can think +of UTF-8 as a "superset" of ASCII. + +UTF-8 is not a double byte format - but it is a standard encoding of Unicode! +TinyXml does not use or directly support wchar, TCHAR, or Microsofts _UNICODE at this time. +It is common to see the term "Unicode" improperly refer to UTF-16, a wide byte encoding +of unicode. This is a source of confusion. + +For "high-ascii" languages - everything not English, pretty much - TinyXml can +handle all languages, at the same time, as long as the XML is encoded +in UTF-8. That can be a little tricky, older programs and operating systems +tend to use the "default" or "traditional" code page. Many apps (and almost all +modern ones) can output UTF-8, but older or stubborn (or just broken) ones +still output text in the default code page. + +For example, Japanese systems traditionally use SHIFT-JIS encoding. +Text encoded as SHIFT-JIS can not be read by tinyxml. +A good text editor can import SHIFT-JIS and then save as UTF-8. + +The <a href="http://skew.org/xml/tutorial/">Skew.org link</a> does a great +job covering the encoding issue. + +The test file "utf8test.xml" is an XML containing English, Spanish, Russian, +and Simplified Chinese. (Hopefully they are translated correctly). The file +"utf8test.gif" is a screen capture of the XML file, rendered in IE. Note that +if you don't have the correct fonts (Simplified Chinese or Russian) on your +system, you won't see output that matches the GIF file even if you can parse +it correctly. Also note that (at least on my Windows machine) console output +is in a Western code page, so that Print() or printf() cannot correctly display +the file. This is not a bug in TinyXml - just an OS issue. No data is lost or +destroyed by TinyXml. The console just doesn't render UTF-8. + + +<h3> Entities </h3> +TinyXml recognizes the pre-defined "character entities", meaning special +characters. Namely: + +@verbatim + & & + < < + > > + " " + ' ' +@endverbatim + +These are recognized when the XML document is read, and translated to there +UTF-8 equivalents. For instance, text with the XML of: + +@verbatim + Far & Away +@endverbatim + +will have the Value() of "Far & Away" when queried from the TiXmlText object, +and will be written back to the XML stream/file as an ampersand. Older versions +of TinyXml "preserved" character entities, but the newer versions will translate +them into characters. + +Additionally, any character can be specified by its Unicode code point: +The syntax " " or " " are both to the non-breaking space characher. + + +<h3> Streams </h3> +With TIXML_USE_STL on, +TiXml has been modified to support both C (FILE) and C++ (operator <<,>>) +streams. There are some differences that you may need to be aware of. + +C style output: + - based on FILE* + - the Print() and SaveFile() methods + + Generates formatted output, with plenty of white space, intended to be as + human-readable as possible. They are very fast, and tolerant of ill formed + XML documents. For example, an XML document that contains 2 root elements + and 2 declarations, will still print. + +C style input: + - based on FILE* + - the Parse() and LoadFile() methods + + A fast, tolerant read. Use whenever you don't need the C++ streams. + +C++ style ouput: + - based on std::ostream + - operator<< + + Generates condensed output, intended for network transmission rather than + readability. Depending on your system's implementation of the ostream class, + these may be somewhat slower. (Or may not.) Not tolerant of ill formed XML: + a document should contain the correct one root element. Additional root level + elements will not be streamed out. + +C++ style input: + - based on std::istream + - operator>> + + Reads XML from a stream, making it useful for network transmission. The tricky + part is knowing when the XML document is complete, since there will almost + certainly be other data in the stream. TinyXml will assume the XML data is + complete after it reads the root element. Put another way, documents that + are ill-constructed with more than one root element will not read correctly. + Also note that operator>> is somewhat slower than Parse, due to both + implementation of the STL and limitations of TinyXml. + +<h3> White space </h3> +The world simply does not agree on whether white space should be kept, or condensed. +For example, pretend the '_' is a space, and look at "Hello____world". HTML, and +at least some XML parsers, will interpret this as "Hello_world". They condense white +space. Some XML parsers do not, and will leave it as "Hello____world". (Remember +to keep pretending the _ is a space.) Others suggest that __Hello___world__ should become +Hello___world. + +It's an issue that hasn't been resolved to my satisfaction. TinyXml supports the +first 2 approaches. Call TiXmlBase::SetCondenseWhiteSpace( bool ) to set the desired behavior. +The default is to condense white space. + +If you change the default, you should call TiXmlBase::SetCondenseWhiteSpace( bool ) +before making any calls to Parse XML data, and I don't recommend changing it after +it has been set. + + +<h3> Handles </h3> + +Where browsing an XML document in a robust way, it is important to check +for null returns from method calls. An error safe implementation can +generate a lot of code like: + +@verbatim +TiXmlElement* root = document.FirstChildElement( "Document" ); +if ( root ) +{ + TiXmlElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + TiXmlElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + TiXmlElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. +@endverbatim + +Handles have been introduced to clean this up. Using the TiXmlHandle class, +the previous code reduces to: + +@verbatim +TiXmlHandle docHandle( &document ); +TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).Element(); +if ( child2 ) +{ + // do something useful +@endverbatim + +Which is much easier to deal with. See TiXmlHandle for more information. + + +<h3> Row and Column tracking </h3> +Being able to track nodes and attributes back to their origin location +in source files can be very important for some applications. Additionally, +knowing where parsing errors occured in the original source can be very +time saving. + +TinyXml can tracks the row and column origin of all nodes and attributes +in a text file. The TiXmlBase::Row() and TiXmlBase::Column() methods return +the origin of the node in the source text. The correct tabs can be +configured in TiXmlDocument::SetTabSize(). + + +<h2> Using and Installing </h2> + +To Compile and Run xmltest: + +A Linux Makefile and a Windows Visual C++ .dsw file is provided. +Simply compile and run. It will write the file demotest.xml to your +disk and generate output on the screen. It also tests walking the +DOM by printing out the number of nodes found using different +techniques. + +The Linux makefile is very generic and will +probably run on other systems, but is only tested on Linux. You no +longer need to run 'make depend'. The dependecies have been +hard coded. + +<h3>Windows project file for VC6</h3> +<ul> +<li>tinyxml: tinyxml library, non-STL </li> +<li>tinyxmlSTL: tinyxml library, STL </li> +<li>tinyXmlTest: test app, non-STL </li> +<li>tinyXmlTestSTL: test app, STL </li> +</ul> + +<h3>Linux Make file</h3> +At the top of the makefile you can set: + +PROFILE, DEBUG, and TINYXML_USE_STL. Details (such that they are) are in +the makefile. + +In the tinyxml directory, type "make clean" then "make". The executable +file 'xmltest' will be created. + + + +<h3>To Use in an Application:</h3> + +Add tinyxml.cpp, tinyxml.h, tinyxmlerror.cpp, tinyxmlparser.cpp, and tinystr.cpp to your +project or make file. That's it! It should compile on any reasonably +compliant C++ system. You do not need to enable exceptions or +RTTI for TinyXml. + + +<h2> How TinyXml works. </h2> + +An example is probably the best way to go. Take: +@verbatim + <?xml version="1.0" standalone=no> + <!-- Our to do list data --> + <ToDo> + <Item priority="1"> Go to the <bold>Toy store!</bold></Item> + <Item priority="2"> Do bills</Item> + </ToDo> +@endverbatim + +Its not much of a To Do list, but it will do. To read this file +(say "demo.xml") you would create a document, and parse it in: +@verbatim + TiXmlDocument doc( "demo.xml" ); + doc.LoadFile(); +@endverbatim + +And its ready to go. Now lets look at some lines and how they +relate to the DOM. + +@verbatim +<?xml version="1.0" standalone=no> +@endverbatim + + The first line is a declaration, and gets turned into the + TiXmlDeclaration class. It will be the first child of the + document node. + + This is the only directive/special tag parsed by by TinyXml. + Generally directive targs are stored in TiXmlUnknown so the + commands wont be lost when it is saved back to disk. + +@verbatim +<!-- Our to do list data --> +@endverbatim + + A comment. Will become a TiXmlComment object. + +@verbatim +<ToDo> +@endverbatim + + The "ToDo" tag defines a TiXmlElement object. This one does not have + any attributes, but does contain 2 other elements. + +@verbatim +<Item priority="1"> +@endverbatim + + Creates another TiXmlElement which is a child of the "ToDo" element. + This element has 1 attribute, with the name "priority" and the value + "1". + +Go to the + + A TiXmlText. This is a leaf node and cannot contain other nodes. + It is a child of the "Item" TiXmlElement. + +@verbatim +<bold> +@endverbatim + + + Another TiXmlElement, this one a child of the "Item" element. + +Etc. + +Looking at the entire object tree, you end up with: +@verbatim +TiXmlDocument "demo.xml" + TiXmlDeclaration "version='1.0'" "standalone=no" + TiXmlComment " Our to do list data" + TiXmlElement "ToDo" + TiXmlElement "Item" Attribtutes: priority = 1 + TiXmlText "Go to the " + TiXmlElement "bold" + TiXmlText "Toy store!" + TiXmlElement "Item" Attributes: priority=2 + TiXmlText "bills" +@endverbatim + +<h2> Documentation </h2> + +The documentation is build with Doxygen, using the 'dox' +configuration file. + +<h2> License </h2> + +TinyXml is released under the zlib license: + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +<h2> References </h2> + +The World Wide Web Consortium is the definitive standard body for +XML, and there web pages contain huge amounts of information. + +The definitive spec: <a href="http://www.w3.org/TR/2004/REC-xml-20040204/"> +http://www.w3.org/TR/2004/REC-xml-20040204/</a> + +I also recommend "XML Pocket Reference" by Robert Eckstein and published by +OReilly...the book that got the whole thing started. + +<h2> Contributors, Contacts, and a Brief History </h2> + +Thanks very much to everyone who sends suggestions, bugs, ideas, and +encouragement. It all helps, and makes this project fun. A special thanks +to the contributors on the web pages that keep it lively. + +So many people have sent in bugs and ideas, that rather than list here +we try to give credit due in the "changes.txt" file. + +TinyXml was originally written be Lee Thomason. (Often the "I" still +in the documenation.) Lee reviews changes and releases new versions, +with the help of Yves Berquin and the tinyXml community. + +We appreciate your suggestions, and would love to know if you +use TinyXml. Hopefully you will enjoy it and find it useful. +Please post questions, comments, file bugs, or contact us at: + +www.sourceforge.net/projects/tinyxml + +Lee Thomason, +Yves Berquin +*/ diff --git a/yhttpd/src/contrib/xml/tinyxml.cpp b/yhttpd/src/contrib/xml/tinyxml.cpp new file mode 100644 index 0000000..73e57c2 --- /dev/null +++ b/yhttpd/src/contrib/xml/tinyxml.cpp @@ -0,0 +1,1428 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include <ctype.h> +#include "tinyxml.h" + +#ifdef TIXML_USE_STL +#include <sstream> +#endif + + +bool TiXmlBase::condenseWhiteSpace = true; + +void TiXmlBase::PutString( const TIXML_STRING& str, TIXML_OSTREAM* stream ) +{ + TIXML_STRING buffer; + PutString( str, &buffer ); + (*stream) << buffer; +} + +void TiXmlBase::PutString( const TIXML_STRING& str, TIXML_STRING* outString ) +{ + int i=0; + + while( i<(int)str.length() ) + { + unsigned char c = (unsigned char) str[i]; + + if ( c == '&' + && i < ( (int)str.length() - 2 ) + && str[i+1] == '#' + && str[i+2] == 'x' ) + { + // Hexadecimal character reference. + // Pass through unchanged. + // © -- copyright symbol, for example. + // + // The -1 is a bug fix from Rob Laveaux. It keeps + // an overflow from happening if there is no ';'. + // There are actually 2 ways to exit this loop - + // while fails (error case) and break (semicolon found). + // However, there is no mechanism (currently) for + // this function to return an error. + while ( i<(int)str.length()-1 ) + { + outString->append( str.c_str() + i, 1 ); + ++i; + if ( str[i] == ';' ) + break; + } + } + else if ( c == '&' ) + { + outString->append( entity[0].str, entity[0].strLength ); + ++i; + } + else if ( c == '<' ) + { + outString->append( entity[1].str, entity[1].strLength ); + ++i; + } + else if ( c == '>' ) + { + outString->append( entity[2].str, entity[2].strLength ); + ++i; + } + else if ( c == '\"' ) + { + outString->append( entity[3].str, entity[3].strLength ); + ++i; + } + else if ( c == '\'' ) + { + outString->append( entity[4].str, entity[4].strLength ); + ++i; + } + else if ( c < 32 ) + { + // Easy pass at non-alpha/numeric/symbol + // Below 32 is symbolic. + char buf[ 32 ]; + sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); + outString->append( buf, strlen( buf ) ); + ++i; + } + else + { + //char realc = (char) c; + //outString->append( &realc, 1 ); + *outString += (char) c; // somewhat more efficient function call. + ++i; + } + } +} + + +// <-- Strange class for a bug fix. Search for STL_STRING_BUG +TiXmlBase::StringToBuffer::StringToBuffer( const TIXML_STRING& str ) +{ + buffer = new char[ str.length()+1 ]; + if ( buffer ) + { + strcpy( buffer, str.c_str() ); + } +} + + +TiXmlBase::StringToBuffer::~StringToBuffer() +{ + delete [] buffer; +} +// End strange bug fix. --> + + +TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() +{ + parent = 0; + type = _type; + firstChild = 0; + lastChild = 0; + prev = 0; + next = 0; +} + + +TiXmlNode::~TiXmlNode() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } +} + + +void TiXmlNode::CopyTo( TiXmlNode* target ) const +{ + target->SetValue (value.c_str() ); + target->userData = userData; +} + + +void TiXmlNode::Clear() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } + + firstChild = 0; + lastChild = 0; +} + + +TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) +{ + node->parent = this; + + node->prev = lastChild; + node->next = 0; + + if ( lastChild ) + lastChild->next = node; + else + firstChild = node; // it was an empty list. + + lastChild = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) +{ + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + + return LinkEndChild( node ); +} + + +TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) +{ + if ( !beforeThis || beforeThis->parent != this ) + return 0; + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->next = beforeThis; + node->prev = beforeThis->prev; + if ( beforeThis->prev ) + { + beforeThis->prev->next = node; + } + else + { + assert( firstChild == beforeThis ); + firstChild = node; + } + beforeThis->prev = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) +{ + if ( !afterThis || afterThis->parent != this ) + return 0; + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->prev = afterThis; + node->next = afterThis->next; + if ( afterThis->next ) + { + afterThis->next->prev = node; + } + else + { + assert( lastChild == afterThis ); + lastChild = node; + } + afterThis->next = node; + return node; +} + + +TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) +{ + if ( replaceThis->parent != this ) + return 0; + + TiXmlNode* node = withThis.Clone(); + if ( !node ) + return 0; + + node->next = replaceThis->next; + node->prev = replaceThis->prev; + + if ( replaceThis->next ) + replaceThis->next->prev = node; + else + lastChild = node; + + if ( replaceThis->prev ) + replaceThis->prev->next = node; + else + firstChild = node; + + delete replaceThis; + node->parent = this; + return node; +} + + +bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) +{ + if ( removeThis->parent != this ) + { + assert( 0 ); + return false; + } + + if ( removeThis->next ) + removeThis->next->prev = removeThis->prev; + else + lastChild = removeThis->prev; + + if ( removeThis->prev ) + removeThis->prev->next = removeThis->next; + else + firstChild = removeThis->next; + + delete removeThis; + return true; +} + +TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const +{ + TiXmlNode* node; + for ( node = firstChild; node; node = node->next ) + { + if ( node->SValue() == TIXML_STRING( _value )) + return node; + } + return 0; +} + +TiXmlNode* TiXmlNode::LastChild( const char * _value ) const +{ + TiXmlNode* node; + for ( node = lastChild; node; node = node->prev ) + { + if ( node->SValue() == TIXML_STRING (_value)) + return node; + } + return 0; +} + +TiXmlNode* TiXmlNode::IterateChildren( TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild(); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling(); + } +} + +TiXmlNode* TiXmlNode::IterateChildren( const char * val, TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild( val ); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling( val ); + } +} + +TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const +{ + TiXmlNode* node; + for ( node = next; node; node = node->next ) + { + if ( node->SValue() == TIXML_STRING (_value)) + return node; + } + return 0; +} + + +TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const +{ + TiXmlNode* node; + for ( node = prev; node; node = node->prev ) + { + if ( node->SValue() == TIXML_STRING (_value)) + return node; + } + return 0; +} + +void TiXmlElement::RemoveAttribute( const char * name ) +{ + TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + { + attributeSet.Remove( node ); + delete node; + } +} + +TiXmlElement* TiXmlNode::FirstChildElement() const +{ + TiXmlNode* node; + + for ( node = FirstChild(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + +TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const +{ + TiXmlNode* node; + + for ( node = FirstChild( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +TiXmlElement* TiXmlNode::NextSiblingElement() const +{ + TiXmlNode* node; + + for ( node = NextSibling(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + +TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const +{ + TiXmlNode* node; + + for ( node = NextSibling( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + + +TiXmlDocument* TiXmlNode::GetDocument() const +{ + const TiXmlNode* node; + + for( node = this; node; node = node->parent ) + { + if ( node->ToDocument() ) + return node->ToDocument(); + } + return 0; +} + + +TiXmlElement::TiXmlElement (const char * _value) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} + + +#ifdef TIXML_USE_STL +TiXmlElement::TiXmlElement( const std::string& _value ) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} +#endif + + +TiXmlElement::TiXmlElement( const TiXmlElement& copy) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + copy.CopyTo( this ); +} + + +void TiXmlElement::operator=( const TiXmlElement& base ) +{ + ClearThis(); + base.CopyTo( this ); +} + + +TiXmlElement::~TiXmlElement() +{ + ClearThis(); +} + + +void TiXmlElement::ClearThis() +{ + Clear(); + while( attributeSet.First() ) + { + TiXmlAttribute* node = attributeSet.First(); + attributeSet.Remove( node ); + delete node; + } +} + + +const char * TiXmlElement::Attribute( const char * name ) const +{ + TiXmlAttribute* node = attributeSet.Find( name ); + + if ( node ) + return node->Value(); + + return 0; +} + + +const char * TiXmlElement::Attribute( const char * name, int* i ) const +{ + const char * s = Attribute( name ); + if ( i ) + { + if ( s ) + *i = atoi( s ); + else + *i = 0; + } + return s; +} + + +const char * TiXmlElement::Attribute( const char * name, double* d ) const +{ + const char * s = Attribute( name ); + if ( d ) + { + if ( s ) + *d = atof( s ); + else + *d = 0; + } + return s; +} + + +int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const +{ + TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + return node->QueryIntValue( ival ); +} + + +int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const +{ + TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + return node->QueryDoubleValue( dval ); +} + + +void TiXmlElement::SetAttribute( const char * name, int val ) +{ + char buf[64]; + sprintf( buf, "%d", val ); + SetAttribute( name, buf ); +} + + +void TiXmlElement::SetDoubleAttribute( const char * name, double val ) +{ + char buf[128]; + sprintf( buf, "%f", val ); + SetAttribute( name, buf ); +} + + +void TiXmlElement::SetAttribute( const char * name, const char * _value ) +{ + TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + { + node->SetValue( _value ); + return; + } + + TiXmlAttribute* attrib = new TiXmlAttribute( name, _value ); + if ( attrib ) + { + attributeSet.Add( attrib ); + } + else + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } +} + +void TiXmlElement::Print( FILE* cfile, int depth ) const +{ + int i; + for ( i=0; i<depth; i++ ) + { + fprintf( cfile, " " ); + } + + fprintf( cfile, "<%s", value.c_str() ); + + TiXmlAttribute* attrib; + for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() ) + { + fprintf( cfile, " " ); + attrib->Print( cfile, depth ); + } + + // There are 3 different formatting approaches: + // 1) An element without children is printed as a <foo /> node + // 2) An element with only a text child is printed as <foo> text </foo> + // 3) An element with children is printed on multiple lines. + TiXmlNode* node; + if ( !firstChild ) + { + fprintf( cfile, " />" ); + } + else if ( firstChild == lastChild && firstChild->ToText() ) + { + fprintf( cfile, ">" ); + firstChild->Print( cfile, depth + 1 ); + fprintf( cfile, "</%s>", value.c_str() ); + } + else + { + fprintf( cfile, ">" ); + + for ( node = firstChild; node; node=node->NextSibling() ) + { + if ( !node->ToText() ) + { + fprintf( cfile, "\n" ); + } + node->Print( cfile, depth+1 ); + } + fprintf( cfile, "\n" ); + for( i=0; i<depth; ++i ) + fprintf( cfile, " " ); + fprintf( cfile, "</%s>", value.c_str() ); + } +} + +void TiXmlElement::StreamOut( TIXML_OSTREAM * stream ) const +{ + (*stream) << "<" << value; + + TiXmlAttribute* attrib; + for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() ) + { + (*stream) << " "; + attrib->StreamOut( stream ); + } + + // If this node has children, give it a closing tag. Else + // make it an empty tag. + TiXmlNode* node; + if ( firstChild ) + { + (*stream) << ">"; + + for ( node = firstChild; node; node=node->NextSibling() ) + { + node->StreamOut( stream ); + } + (*stream) << "</" << value << ">"; + } + else + { + (*stream) << " />"; + } +} + + +void TiXmlElement::CopyTo( TiXmlElement* target ) const +{ + // superclass: + TiXmlNode::CopyTo( target ); + + // Element class: + // Clone the attributes, then clone the children. + TiXmlAttribute* attribute = 0; + for( attribute = attributeSet.First(); + attribute; + attribute = attribute->Next() ) + { + target->SetAttribute( attribute->Name(), attribute->Value() ); + } + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + + +TiXmlNode* TiXmlElement::Clone() const +{ + TiXmlElement* clone = new TiXmlElement( Value() ); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + ClearError(); +} + +TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + value = documentName; + ClearError(); +} + + +#ifdef TIXML_USE_STL +TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + value = documentName; + ClearError(); +} +#endif + + +TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDocument::operator=( const TiXmlDocument& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) +{ + // See STL_STRING_BUG below. + StringToBuffer buf( value ); + + if ( buf.buffer && LoadFile( buf.buffer, encoding ) ) + return true; + + return false; +} + + +bool TiXmlDocument::SaveFile() const +{ + // See STL_STRING_BUG below. + StringToBuffer buf( value ); + + if ( buf.buffer && SaveFile( buf.buffer ) ) + return true; + + return false; +} + +bool TiXmlDocument::LoadFile( const char* filename, TiXmlEncoding encoding ) +{ + // Delete the existing data: + Clear(); + location.Clear(); + + // There was a really terrifying little bug here. The code: + // value = filename + // in the STL case, cause the assignment method of the std::string to + // be called. What is strange, is that the std::string had the same + // address as it's c_str() method, and so bad things happen. Looks + // like a bug in the Microsoft STL implementation. + // See STL_STRING_BUG above. + // Fixed with the StringToBuffer class. + value = filename; + + FILE* file = fopen( value.c_str (), "r" ); + + if ( file ) + { + // Get the file size, so we can pre-allocate the string. HUGE speed impact. + long length = 0; + fseek( file, 0, SEEK_END ); + length = ftell( file ); + fseek( file, 0, SEEK_SET ); + + // Strange case, but good to handle up front. + if ( length == 0 ) + { + fclose( file ); + return false; + } + + // If we have a file, assume it is all one big XML file, and read it in. + // The document parser may decide the document ends sooner than the entire file, however. + TIXML_STRING data; + data.reserve( length ); + + const int BUF_SIZE = 2048; + char buf[BUF_SIZE]; + + while( fgets( buf, BUF_SIZE, file ) ) + { + data += buf; + } + fclose( file ); + + Parse( data.c_str(), 0, encoding ); + + if ( Error() ) + return false; + else + return true; + } + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; +} + +bool TiXmlDocument::SaveFile( const char * filename ) const +{ + // The old c stuff lives on... + FILE* fp = fopen( filename, "w" ); + if ( fp ) + { + Print( fp, 0 ); + fclose( fp ); + return true; + } + return false; +} + + +void TiXmlDocument::CopyTo( TiXmlDocument* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->error = error; + target->errorDesc = errorDesc.c_str (); + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + + +TiXmlNode* TiXmlDocument::Clone() const +{ + TiXmlDocument* clone = new TiXmlDocument(); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlDocument::Print( FILE* cfile, int depth ) const +{ + TiXmlNode* node; + for ( node=FirstChild(); node; node=node->NextSibling() ) + { + node->Print( cfile, depth ); + fprintf( cfile, "\n" ); + } +} + +void TiXmlDocument::StreamOut( TIXML_OSTREAM * out ) const +{ + TiXmlNode* node; + for ( node=FirstChild(); node; node=node->NextSibling() ) + { + node->StreamOut( out ); + + // Special rule for streams: stop after the root element. + // The stream in code will only read one element, so don't + // write more than one. + if ( node->ToElement() ) + break; + } +} + + +TiXmlAttribute* TiXmlAttribute::Next() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} + + +TiXmlAttribute* TiXmlAttribute::Previous() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} + + +void TiXmlAttribute::Print( FILE* cfile, int /*depth*/ ) const +{ + TIXML_STRING n, v; + + PutString( name, &n ); + PutString( value, &v ); + + if (value.find ('\"') == TIXML_STRING::npos) + fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); + else + fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); +} + + +void TiXmlAttribute::StreamOut( TIXML_OSTREAM * stream ) const +{ + if (value.find( '\"' ) != TIXML_STRING::npos) + { + PutString( name, stream ); + (*stream) << "=" << "'"; + PutString( value, stream ); + (*stream) << "'"; + } + else + { + PutString( name, stream ); + (*stream) << "=" << "\""; + PutString( value, stream ); + (*stream) << "\""; + } +} + +int TiXmlAttribute::QueryIntValue( int* ival ) const +{ + if ( sscanf( value.c_str(), "%d", ival ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +int TiXmlAttribute::QueryDoubleValue( double* dval ) const +{ + if ( sscanf( value.c_str(), "%lf", dval ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +void TiXmlAttribute::SetIntValue( int _value ) +{ + char buf [64]; + sprintf (buf, "%d", _value); + SetValue (buf); +} + +void TiXmlAttribute::SetDoubleValue( double _value ) +{ + char buf [64]; + sprintf (buf, "%lf", _value); + SetValue (buf); +} + +const int TiXmlAttribute::IntValue() const +{ + return atoi (value.c_str ()); +} + +const double TiXmlAttribute::DoubleValue() const +{ + return atof (value.c_str ()); +} + + +TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::COMMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlComment::operator=( const TiXmlComment& base ) +{ + Clear(); + base.CopyTo( this ); +} + + +void TiXmlComment::Print( FILE* cfile, int depth ) const +{ + for ( int i=0; i<depth; i++ ) + { + fputs( " ", cfile ); + } + fprintf( cfile, "<!--%s-->", value.c_str() ); +} + +void TiXmlComment::StreamOut( TIXML_OSTREAM * stream ) const +{ + (*stream) << "<!--"; + //PutString( value, stream ); + (*stream) << value; + (*stream) << "-->"; +} + + +void TiXmlComment::CopyTo( TiXmlComment* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +TiXmlNode* TiXmlComment::Clone() const +{ + TiXmlComment* clone = new TiXmlComment(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlText::Print( FILE* cfile, int /*depth*/ ) const +{ + TIXML_STRING buffer; + PutString( value, &buffer ); + fprintf( cfile, "%s", buffer.c_str() ); +} + + +void TiXmlText::StreamOut( TIXML_OSTREAM * stream ) const +{ + PutString( value, stream ); +} + + +void TiXmlText::CopyTo( TiXmlText* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +TiXmlNode* TiXmlText::Clone() const +{ + TiXmlText* clone = 0; + clone = new TiXmlText( "" ); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlDeclaration::TiXmlDeclaration( const char * _version, + const char * _encoding, + const char * _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} + + +#ifdef TIXML_USE_STL +TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} +#endif + + +TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/ ) const +{ + fprintf (cfile, "<?xml "); + + if ( !version.empty() ) + fprintf (cfile, "version=\"%s\" ", version.c_str ()); + if ( !encoding.empty() ) + fprintf (cfile, "encoding=\"%s\" ", encoding.c_str ()); + if ( !standalone.empty() ) + fprintf (cfile, "standalone=\"%s\" ", standalone.c_str ()); + fprintf (cfile, "?>"); +} + +void TiXmlDeclaration::StreamOut( TIXML_OSTREAM * stream ) const +{ + (*stream) << "<?xml "; + + if ( !version.empty() ) + { + (*stream) << "version=\""; + PutString( version, stream ); + (*stream) << "\" "; + } + if ( !encoding.empty() ) + { + (*stream) << "encoding=\""; + PutString( encoding, stream ); + (*stream ) << "\" "; + } + if ( !standalone.empty() ) + { + (*stream) << "standalone=\""; + PutString( standalone, stream ); + (*stream) << "\" "; + } + (*stream) << "?>"; +} + + +void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->version = version; + target->encoding = encoding; + target->standalone = standalone; +} + + +TiXmlNode* TiXmlDeclaration::Clone() const +{ + TiXmlDeclaration* clone = new TiXmlDeclaration(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlUnknown::Print( FILE* cfile, int depth ) const +{ + for ( int i=0; i<depth; i++ ) + fprintf( cfile, " " ); + fprintf( cfile, "<%s>", value.c_str() ); +} + + +void TiXmlUnknown::StreamOut( TIXML_OSTREAM * stream ) const +{ + (*stream) << "<" << value << ">"; // Don't use entities here! It is unknown. +} + + +void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +TiXmlNode* TiXmlUnknown::Clone() const +{ + TiXmlUnknown* clone = new TiXmlUnknown(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlAttributeSet::TiXmlAttributeSet() +{ + sentinel.next = &sentinel; + sentinel.prev = &sentinel; +} + + +TiXmlAttributeSet::~TiXmlAttributeSet() +{ + assert( sentinel.next == &sentinel ); + assert( sentinel.prev == &sentinel ); +} + + +void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) +{ + assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. + + addMe->next = &sentinel; + addMe->prev = sentinel.prev; + + sentinel.prev->next = addMe; + sentinel.prev = addMe; +} + +void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) +{ + TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node == removeMe ) + { + node->prev->next = node->next; + node->next->prev = node->prev; + node->next = 0; + node->prev = 0; + return; + } + } + assert( 0 ); // we tried to remove a non-linked attribute. +} + +TiXmlAttribute* TiXmlAttributeSet::Find( const char * name ) const +{ + TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} + + +#ifdef TIXML_USE_STL +TIXML_ISTREAM & operator >> (TIXML_ISTREAM & in, TiXmlNode & base) +{ + TIXML_STRING tag; + tag.reserve( 8 * 1000 ); + base.StreamIn( &in, &tag ); + + base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); + return in; +} +#endif + + +TIXML_OSTREAM & operator<< (TIXML_OSTREAM & out, const TiXmlNode & base) +{ + base.StreamOut (& out); + return out; +} + + +#ifdef TIXML_USE_STL +std::string & operator<< (std::string& out, const TiXmlNode& base ) +{ + std::ostringstream os_stream( std::ostringstream::out ); + base.StreamOut( &os_stream ); + + out.append( os_stream.str() ); + return out; +} +#endif + + +TiXmlHandle TiXmlHandle::FirstChild() const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement() const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild(); + for ( i=0; + child && i<count; + child = child->NextSibling(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild( value ); + for ( i=0; + child && i<count; + child = child->NextSibling( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement(); + for ( i=0; + child && i<count; + child = child->NextSiblingElement(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement( value ); + for ( i=0; + child && i<count; + child = child->NextSiblingElement( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} diff --git a/yhttpd/src/contrib/xml/tinyxml.h b/yhttpd/src/contrib/xml/tinyxml.h new file mode 100644 index 0000000..a9ece4b --- /dev/null +++ b/yhttpd/src/contrib/xml/tinyxml.h @@ -0,0 +1,1630 @@ +#include "../../incl.h" + + +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#ifndef TINYXML_INCLUDED +#define TINYXML_INCLUDED + +#ifdef _MSC_VER +#pragma warning( disable : 4530 ) +#pragma warning( disable : 4786 ) +#endif + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +// Help out windows: +#if defined( _DEBUG ) && !defined( DEBUG ) +#define DEBUG +#endif + +#if defined( DEBUG ) && defined( _MSC_VER ) +#include <windows.h> +#define TIXML_LOG OutputDebugString +#else +#define TIXML_LOG printf +#endif + +#include <string> +#include <iostream> +#define TIXML_STRING std::string +#define TIXML_ISTREAM std::istream +#define TIXML_OSTREAM std::ostream + +class TiXmlDocument; +class TiXmlElement; +class TiXmlComment; +class TiXmlUnknown; +class TiXmlAttribute; +class TiXmlText; +class TiXmlDeclaration; +class TiXmlParsingData; + +const int TIXML_MAJOR_VERSION = 2; +const int TIXML_MINOR_VERSION = 3; +const int TIXML_PATCH_VERSION = 2; + +/* Internal structure for tracking location of items + in the XML file. +*/ +struct TiXmlCursor +{ + TiXmlCursor() + { + Clear(); + } + void Clear() + { + row = col = -1; + } + + int row; // 0 based. + int col; // 0 based. +}; + + +// Only used by Attribute::Query functions +enum +{ + TIXML_SUCCESS, + TIXML_NO_ATTRIBUTE, + TIXML_WRONG_TYPE +}; + + +// Used by the parsing routines. +enum TiXmlEncoding +{ + TIXML_ENCODING_UNKNOWN, + TIXML_ENCODING_UTF8, + TIXML_ENCODING_LEGACY +}; + +const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN; + +/** TiXmlBase is a base class for every class in TinyXml. + It does little except to establish that TinyXml classes + can be printed and provide some utility functions. + + In XML, the document and elements can contain + other elements and other types of nodes. + + @verbatim + A Document can contain: Element (container or leaf) + Comment (leaf) + Unknown (leaf) + Declaration( leaf ) + + An Element can contain: Element (container or leaf) + Text (leaf) + Attributes (not on tree) + Comment (leaf) + Unknown (leaf) + + A Decleration contains: Attributes (not on tree) + @endverbatim +*/ +class TiXmlBase +{ + friend class TiXmlNode; + friend class TiXmlElement; + friend class TiXmlDocument; + +public: + TiXmlBase() : userData(0) + {} + virtual ~TiXmlBase() + {} + + /** All TinyXml classes can print themselves to a filestream. + This is a formatted print, and will insert tabs and newlines. + + (For an unformatted stream, use the << operator.) + */ + virtual void Print( FILE* cfile, int depth ) const = 0; + + /** The world does not agree on whether white space should be kept or + not. In order to make everyone happy, these global, static functions + are provided to set whether or not TinyXml will condense all white space + into a single space or not. The default is to condense. Note changing this + values is not thread safe. + */ + static void SetCondenseWhiteSpace( bool condense ) + { + condenseWhiteSpace = condense; + } + + /// Return the current white space setting. + static bool IsWhiteSpaceCondensed() + { + return condenseWhiteSpace; + } + + /** Return the position, in the original source file, of this node or attribute. + The row and column are 1-based. (That is the first row and first column is + 1,1). If the returns values are 0 or less, then the parser does not have + a row and column value. + + Generally, the row and column value will be set when the TiXmlDocument::Load(), + TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set + when the DOM was created from operator>>. + + The values reflect the initial load. Once the DOM is modified programmatically + (by adding or changing nodes and attributes) the new values will NOT update to + reflect changes in the document. + + There is a minor performance cost to computing the row and column. Computation + can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value. + + @sa TiXmlDocument::SetTabSize() + */ + int Row() const + { + return location.row + 1; + } + int Column() const + { + return location.col + 1; + } ///< See Row() + + void SetUserData( void* user ) + { + userData = user; + } + void* GetUserData() + { + return userData; + } + + // Table that returs, for a given lead byte, the total number of bytes + // in the UTF-8 sequence. + static const int utf8ByteTable[256]; + + virtual const char* Parse( const char* p, + TiXmlParsingData* data, + TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0; + +protected: + + // See STL_STRING_BUG + // Utility class to overcome a bug. + class StringToBuffer + { + public: + StringToBuffer( const TIXML_STRING& str ); + ~StringToBuffer(); + char* buffer; + }; + + static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding ); + inline static bool IsWhiteSpace( char c ) + { + return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); + } + + virtual void StreamOut (TIXML_OSTREAM *) const = 0; + +#ifdef TIXML_USE_STL + + static bool StreamWhiteSpace( TIXML_ISTREAM * in, TIXML_STRING * tag ); + static bool StreamTo( TIXML_ISTREAM * in, int character, TIXML_STRING * tag ); +#endif + + /* Reads an XML name into the string provided. Returns + a pointer just past the last character of the name, + or 0 if the function has an error. + */ + static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding ); + + /* Reads text. Returns a pointer past the given end tag. + Wickedly complex options, but it keeps the (sensitive) code in one place. + */ + static const char* ReadText( const char* in, // where to start + TIXML_STRING* text, // the string read + bool ignoreWhiteSpace, // whether to keep the white space + const char* endTag, // what ends this text + bool ignoreCase, // whether to ignore case in the end tag + TiXmlEncoding encoding ); // the current encoding + + // If an entity has been found, transform it into a character. + static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding ); + + // Get a character, while interpreting entities. + // The length can be from 0 to 4 bytes. + inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding ) + { + assert( p ); + if ( encoding == TIXML_ENCODING_UTF8 ) + { + *length = utf8ByteTable[ *((unsigned char*)p) ]; + assert( *length >= 0 && *length < 5 ); + } + else + { + *length = 1; + } + + if ( *length == 1 ) + { + if ( *p == '&' ) + return GetEntity( p, _value, length, encoding ); + *_value = *p; + return p+1; + } + else if ( *length ) + { + strncpy( _value, p, *length ); + return p + (*length); + } + else + { + // Not valid text. + return 0; + } + } + + // Puts a string to a stream, expanding entities as it goes. + // Note this should not contian the '<', '>', etc, or they will be transformed into entities! + static void PutString( const TIXML_STRING& str, TIXML_OSTREAM* out ); + + static void PutString( const TIXML_STRING& str, TIXML_STRING* out ); + + // Return true if the next characters in the stream are any of the endTag sequences. + // Ignore case only works for english, and should only be relied on when comparing + // to Engilish words: StringEqual( p, "version", true ) is fine. + static bool StringEqual( const char* p, + const char* endTag, + bool ignoreCase, + TiXmlEncoding encoding ); + + + enum + { + TIXML_NO_ERROR = 0, + TIXML_ERROR, + TIXML_ERROR_OPENING_FILE, + TIXML_ERROR_OUT_OF_MEMORY, + TIXML_ERROR_PARSING_ELEMENT, + TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, + TIXML_ERROR_READING_ELEMENT_VALUE, + TIXML_ERROR_READING_ATTRIBUTES, + TIXML_ERROR_PARSING_EMPTY, + TIXML_ERROR_READING_END_TAG, + TIXML_ERROR_PARSING_UNKNOWN, + TIXML_ERROR_PARSING_COMMENT, + TIXML_ERROR_PARSING_DECLARATION, + TIXML_ERROR_DOCUMENT_EMPTY, + TIXML_ERROR_EMBEDDED_NULL, + + TIXML_ERROR_STRING_COUNT + }; + static const char* errorString[ TIXML_ERROR_STRING_COUNT ]; + + TiXmlCursor location; + + /// Field containing a generic user pointer + void* userData; + + // None of these methods are reliable for any language except English. + // Good for approximation, not great for accuracy. + static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding ); + static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding ); + inline static int ToLower( int v, TiXmlEncoding encoding ) + { + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( v < 128 ) + return tolower( v ); + return v; + } + else + { + return tolower( v ); + } + } + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + +private: + TiXmlBase( const TiXmlBase& ); // not implemented. + void operator=( const TiXmlBase& base ); // not allowed. + + struct Entity + { + const char* str; + unsigned int strLength; + char chr; + }; + enum + { + NUM_ENTITY = 5, + MAX_ENTITY_LENGTH = 6 + + }; + static Entity entity[ NUM_ENTITY ]; + static bool condenseWhiteSpace; +}; + + +/** The parent class for everything in the Document Object Model. + (Except for attributes). + Nodes have siblings, a parent, and children. A node can be + in a document, or stand on its own. The type of a TiXmlNode + can be queried, and it can be cast to its more defined type. +*/ +class TiXmlNode : public TiXmlBase +{ + friend class TiXmlDocument; + friend class TiXmlElement; + +public: +#ifdef TIXML_USE_STL + + /** An input stream operator, for every class. Tolerant of newlines and + formatting, but doesn't expect them. + */ + friend std::istream& operator >> (std::istream& in, TiXmlNode& base); + + /** An output stream operator, for every class. Note that this outputs + without any newlines or formatting, as opposed to Print(), which + includes tabs and new lines. + + The operator<< and operator>> are not completely symmetric. Writing + a node to a stream is very well defined. You'll get a nice stream + of output, without any extra whitespace or newlines. + + But reading is not as well defined. (As it always is.) If you create + a TiXmlElement (for example) and read that from an input stream, + the text needs to define an element or junk will result. This is + true of all input streams, but it's worth keeping in mind. + + A TiXmlDocument will read nodes until it reads a root element, and + all the children of that root element. + */ + friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base); + + /// Appends the XML node or attribute to a std::string. + friend std::string& operator<< (std::string& out, const TiXmlNode& base ); + +#else + // Used internally, not part of the public API. + friend TIXML_OSTREAM& operator<< (TIXML_OSTREAM& out, const TiXmlNode& base); +#endif + + /** The types of XML nodes supported by TinyXml. (All the + unsupported types are picked up by UNKNOWN.) + */ + enum NodeType + { + DOCUMENT, + ELEMENT, + COMMENT, + UNKNOWN, + TEXT, + DECLARATION, + TYPECOUNT + }; + + virtual ~TiXmlNode(); + + /** The meaning of 'value' changes for the specific type of + TiXmlNode. + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + + The subclasses will wrap this function. + */ + const char * Value() const + { + return value.c_str (); + } + + /** Changes the value of the node. Defined as: + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + */ + void SetValue(const char * _value) + { + value = _value; + } + +#ifdef TIXML_USE_STL + /// STL std::string form. + void SetValue( const std::string& _value ) + { + StringToBuffer buf( _value ); + SetValue( buf.buffer ? buf.buffer : "" ); + } +#endif + + /// Delete all the children of this node. Does not affect 'this'. + void Clear(); + + /// One step up the DOM. + TiXmlNode* Parent() const + { + return parent; + } + + TiXmlNode* FirstChild() const + { + return firstChild; + } ///< The first child of this node. Will be null if there are no children. + TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found. + + TiXmlNode* LastChild() const + { + return lastChild; + } /// The last child of this node. Will be null if there are no children. + TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children. + +#ifdef TIXML_USE_STL + + TiXmlNode* FirstChild( const std::string& _value ) const + { + return FirstChild (_value.c_str ()); + } ///< STL std::string form. + TiXmlNode* LastChild( const std::string& _value ) const + { + return LastChild (_value.c_str ()); + } ///< STL std::string form. +#endif + + /** An alternate way to walk the children of a node. + One way to iterate over nodes is: + @verbatim + for( child = parent->FirstChild(); child; child = child->NextSibling() ) + @endverbatim + + IterateChildren does the same thing with the syntax: + @verbatim + child = 0; + while( child = parent->IterateChildren( child ) ) + @endverbatim + + IterateChildren takes the previous child as input and finds + the next one. If the previous child is null, it returns the + first. IterateChildren will return null when done. + */ + TiXmlNode* IterateChildren( TiXmlNode* previous ) const; + + /// This flavor of IterateChildren searches for children with a particular 'value' + TiXmlNode* IterateChildren( const char * value, TiXmlNode* previous ) const; + +#ifdef TIXML_USE_STL + + TiXmlNode* IterateChildren( const std::string& _value, TiXmlNode* previous ) const + { + return IterateChildren (_value.c_str (), previous); + } ///< STL std::string form. +#endif + + /** Add a new node related to this. Adds a child past the LastChild. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertEndChild( const TiXmlNode& addThis ); + + + /** Add a new node related to this. Adds a child past the LastChild. + + NOTE: the node to be added is passed by pointer, and will be + henceforth owned (and deleted) by tinyXml. This method is efficient + and avoids an extra copy, but should be used with care as it + uses a different memory model than the other insert functions. + + @sa InsertEndChild + */ + TiXmlNode* LinkEndChild( TiXmlNode* addThis ); + + /** Add a new node related to this. Adds a child before the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ); + + /** Add a new node related to this. Adds a child after the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ); + + /** Replace a child of this node. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ); + + /// Delete a child of this node. + bool RemoveChild( TiXmlNode* removeThis ); + + /// Navigate to a sibling node. + TiXmlNode* PreviousSibling() const + { + return prev; + } + + /// Navigate to a sibling node. + TiXmlNode* PreviousSibling( const char * ) const; + +#ifdef TIXML_USE_STL + + TiXmlNode* PreviousSibling( const std::string& _value ) const + { + return PreviousSibling (_value.c_str ()); + } ///< STL std::string form. + TiXmlNode* NextSibling( const std::string& _value) const + { + return NextSibling (_value.c_str ()); + } ///< STL std::string form. +#endif + + /// Navigate to a sibling node. + TiXmlNode* NextSibling() const + { + return next; + } + + /// Navigate to a sibling node with the given 'value'. + TiXmlNode* NextSibling( const char * ) const; + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + TiXmlElement* NextSiblingElement() const; + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + TiXmlElement* NextSiblingElement( const char * ) const; + +#ifdef TIXML_USE_STL + + TiXmlElement* NextSiblingElement( const std::string& _value) const + { + return NextSiblingElement (_value.c_str ()); + } ///< STL std::string form. +#endif + + /// Convenience function to get through elements. + TiXmlElement* FirstChildElement() const; + + /// Convenience function to get through elements. + TiXmlElement* FirstChildElement( const char * value ) const; + +#ifdef TIXML_USE_STL + + TiXmlElement* FirstChildElement( const std::string& _value ) const + { + return FirstChildElement (_value.c_str ()); + } ///< STL std::string form. +#endif + + /** Query the type (as an enumerated value, above) of this node. + The possible types are: DOCUMENT, ELEMENT, COMMENT, + UNKNOWN, TEXT, and DECLARATION. + */ + virtual int Type() const + { + return type; + } + + /** Return a pointer to the Document this node lives in. + Returns null if not in a document. + */ + TiXmlDocument* GetDocument() const; + + /// Returns true if this node has no children. + bool NoChildren() const + { + return !firstChild; + } + + TiXmlDocument* ToDocument() const + { + return ( this && type == DOCUMENT ) ? (TiXmlDocument*) this : 0; + } ///< Cast to a more defined type. Will return null not of the requested type. + TiXmlElement* ToElement() const + { + return ( this && type == ELEMENT ) ? (TiXmlElement*) this : 0; + } ///< Cast to a more defined type. Will return null not of the requested type. + TiXmlComment* ToComment() const + { + return ( this && type == COMMENT ) ? (TiXmlComment*) this : 0; + } ///< Cast to a more defined type. Will return null not of the requested type. + TiXmlUnknown* ToUnknown() const + { + return ( this && type == UNKNOWN ) ? (TiXmlUnknown*) this : 0; + } ///< Cast to a more defined type. Will return null not of the requested type. + TiXmlText* ToText() const + { + return ( this && type == TEXT ) ? (TiXmlText*) this : 0; + } ///< Cast to a more defined type. Will return null not of the requested type. + TiXmlDeclaration* ToDeclaration() const + { + return ( this && type == DECLARATION ) ? (TiXmlDeclaration*) this : 0; + } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Create an exact duplicate of this node and return it. The memory must be deleted + by the caller. + */ + virtual TiXmlNode* Clone() const = 0; + +protected: + TiXmlNode( NodeType _type ); + + // Copy to the allocated object. Shared functionality between Clone, Copy constructor, + // and the assignment operator. + void CopyTo( TiXmlNode* target ) const; + +#ifdef TIXML_USE_STL + // The real work of the input operator. + virtual void StreamIn( TIXML_ISTREAM* in, TIXML_STRING* tag ) = 0; +#endif + + // Figure out what is at *p, and parse it. Returns null if it is not an xml node. + TiXmlNode* Identify( const char* start, TiXmlEncoding encoding ); + + // Internal Value function returning a TIXML_STRING + const TIXML_STRING& SValue() const + { + return value ; + } + + TiXmlNode* parent; + NodeType type; + + TiXmlNode* firstChild; + TiXmlNode* lastChild; + + TIXML_STRING value; + + TiXmlNode* prev; + TiXmlNode* next; + +private: + TiXmlNode( const TiXmlNode& ); // not implemented. + void operator=( const TiXmlNode& base ); // not allowed. +}; + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not TiXmlNodes, since they are not + part of the tinyXML document object model. There are other + suggested ways to look at this problem. +*/ +class TiXmlAttribute : public TiXmlBase +{ + friend class TiXmlAttributeSet; + +public: + /// Construct an empty attribute. + TiXmlAttribute() : TiXmlBase() + { + document = 0; + prev = next = 0; + } + +#ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlAttribute( const std::string& _name, const std::string& _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } +#endif + + /// Construct an attribute with a name and value. + TiXmlAttribute( const char * _name, const char * _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + + const char* Name() const + { + return name.c_str (); + } ///< Return the name of this attribute. + const char* Value() const + { + return value.c_str (); + } ///< Return the value of this attribute. + const int IntValue() const; ///< Return the value of this attribute, converted to an integer. + const double DoubleValue() const; ///< Return the value of this attribute, converted to a double. + + /** QueryIntValue examines the value string. It is an alternative to the + IntValue() method with richer error checking. + If the value is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. + + A specialized but useful call. Note that for success it returns 0, + which is the opposite of almost all other TinyXml calls. + */ + int QueryIntValue( int* value ) const; + /// QueryDoubleValue examines the value string. See QueryIntValue(). + int QueryDoubleValue( double* value ) const; + + void SetName( const char* _name ) + { + name = _name; + } ///< Set the name of this attribute. + void SetValue( const char* _value ) + { + value = _value; + } ///< Set the value. + + void SetIntValue( int value ); ///< Set the value from an integer. + void SetDoubleValue( double value ); ///< Set the value from a double. + +#ifdef TIXML_USE_STL + /// STL std::string form. + void SetName( const std::string& _name ) + { + StringToBuffer buf( _name ); + SetName ( buf.buffer ? buf.buffer : "error" ); + } + /// STL std::string form. + void SetValue( const std::string& _value ) + { + StringToBuffer buf( _value ); + SetValue( buf.buffer ? buf.buffer : "error" ); + } +#endif + + /// Get the next sibling attribute in the DOM. Returns null at end. + TiXmlAttribute* Next() const; + /// Get the previous sibling attribute in the DOM. Returns null at beginning. + TiXmlAttribute* Previous() const; + + bool operator==( const TiXmlAttribute& rhs ) const + { + return rhs.name == name; + } + bool operator<( const TiXmlAttribute& rhs ) const + { + return name < rhs.name; + } + bool operator>( const TiXmlAttribute& rhs ) const + { + return name > rhs.name; + } + + /* Attribute parsing starts: first letter of the name + returns: the next char after the value end quote + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + // Prints this Attribute to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual void StreamOut( TIXML_OSTREAM * out ) const; + // [internal use] + // Set the document pointer so the attribute can report errors. + void SetDocument( TiXmlDocument* doc ) + { + document = doc; + } + +private: + TiXmlAttribute( const TiXmlAttribute& ); // not implemented. + void operator=( const TiXmlAttribute& base ); // not allowed. + + TiXmlDocument* document; // A pointer back to a document, for error reporting. + TIXML_STRING name; + TIXML_STRING value; + TiXmlAttribute* prev; + TiXmlAttribute* next; +}; + + +/* A class used to manage a group of attributes. + It is only used internally, both by the ELEMENT and the DECLARATION. + + The set can be changed transparent to the Element and Declaration + classes that use it, but NOT transparent to the Attribute + which has to implement a next() and previous() method. Which makes + it a bit problematic and prevents the use of STL. + + This version is implemented with circular lists because: + - I like circular lists + - it demonstrates some independence from the (typical) doubly linked list. +*/ +class TiXmlAttributeSet +{ +public: + TiXmlAttributeSet(); + ~TiXmlAttributeSet(); + + void Add( TiXmlAttribute* attribute ); + void Remove( TiXmlAttribute* attribute ); + + TiXmlAttribute* First() const + { + return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; + } + TiXmlAttribute* Last() const + { + return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; + } + TiXmlAttribute* Find( const char * name ) const; + +private: + TiXmlAttribute sentinel; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TiXmlElement : public TiXmlNode +{ +public: + /// Construct an element. + TiXmlElement (const char * in_value); + +#ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlElement( const std::string& _value ); +#endif + + TiXmlElement( const TiXmlElement& ); + + void operator=( const TiXmlElement& base ); + + virtual ~TiXmlElement(); + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + */ + const char* Attribute( const char* name ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an integer, + the integer value will be put in the return 'i', if 'i' + is non-null. + */ + const char* Attribute( const char* name, int* i ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an double, + the double value will be put in the return 'd', if 'd' + is non-null. + */ + const char* Attribute( const char* name, double* d ) const; + + /** QueryIntAttribute examines the attribute - it is an alternative to the + Attribute() method with richer error checking. + If the attribute is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. If the attribute + does not exist, then TIXML_NO_ATTRIBUTE is returned. + */ + int QueryIntAttribute( const char* name, int* value ) const; + /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute(). + int QueryDoubleAttribute( const char* name, double* value ) const; + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char* name, const char * value ); + +#ifdef TIXML_USE_STL + + const char* Attribute( const std::string& name ) const + { + return Attribute( name.c_str() ); + } + const char* Attribute( const std::string& name, int* i ) const + { + return Attribute( name.c_str(), i ); + } + const char* Attribute( const std::string& name, double* d ) const + { + return Attribute( name.c_str(), d ); + } + int QueryIntAttribute( const std::string& name, int* value ) const + { + return QueryIntAttribute( name.c_str(), value ); + } + int QueryDoubleAttribute( const std::string& name, double* value ) const + { + return QueryDoubleAttribute( name.c_str(), value ); + } + + /// STL std::string form. + void SetAttribute( const std::string& name, const std::string& _value ) + { + StringToBuffer n( name ); + StringToBuffer v( _value ); + if ( n.buffer && v.buffer ) + SetAttribute (n.buffer, v.buffer ); + } + ///< STL std::string form. + void SetAttribute( const std::string& name, int _value ) + { + StringToBuffer n( name ); + if ( n.buffer ) + SetAttribute (n.buffer, _value); + } +#endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char * name, int value ); + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetDoubleAttribute( const char * name, double value ); + + /** Deletes an attribute with the given name. + */ + void RemoveAttribute( const char * name ); +#ifdef TIXML_USE_STL + + void RemoveAttribute( const std::string& name ) + { + RemoveAttribute (name.c_str ()); + } ///< STL std::string form. +#endif + + TiXmlAttribute* FirstAttribute() const + { + return attributeSet.First(); + } ///< Access the first attribute in this element. + TiXmlAttribute* LastAttribute() const + { + return attributeSet.Last(); + } ///< Access the last attribute in this element. + + /// Creates a new Element and returns it - the returned element is a copy. + virtual TiXmlNode* Clone() const; + // Print the Element to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: next char past '<' + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + +protected: + + void CopyTo( TiXmlElement* target ) const; + void ClearThis(); // like clear, but initializes 'this' object as well + + // Used to be public [internal use] +#ifdef TIXML_USE_STL + + virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ); +#endif + + virtual void StreamOut( TIXML_OSTREAM * out ) const; + + /* [internal use] + Reads the "value" of the element -- another element, or text. + This should terminate with the current end tag. + */ + const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + +private: + + TiXmlAttributeSet attributeSet; +}; + + +/** An XML comment. +*/ +class TiXmlComment : public TiXmlNode +{ +public: + /// Constructs an empty comment. + TiXmlComment() : TiXmlNode( TiXmlNode::COMMENT ) + {} + TiXmlComment( const TiXmlComment& ); + void operator=( const TiXmlComment& base ); + + virtual ~TiXmlComment() + {} + + /// Returns a copy of this Comment. + virtual TiXmlNode* Clone() const; + /// Write this Comment to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: at the ! of the !-- + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + +protected: + void CopyTo( TiXmlComment* target ) const; + + // used to be public +#ifdef TIXML_USE_STL + + virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ); +#endif + + virtual void StreamOut( TIXML_OSTREAM * out ) const; + +private: + +}; + + +/** XML text. Contained in an element. +*/ +class TiXmlText : public TiXmlNode +{ + friend class TiXmlElement; +public: + /// Constructor. + TiXmlText (const char * initValue) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + } + virtual ~TiXmlText() + {} + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + } +#endif + + TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TEXT ) + { + copy.CopyTo( this ); + } + void operator=( const TiXmlText& base ) + { + base.CopyTo( this ); + } + + /// Write this text object to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + +protected : + /// [internal use] Creates a new Element and returns it. + virtual TiXmlNode* Clone() const; + void CopyTo( TiXmlText* target ) const; + + virtual void StreamOut ( TIXML_OSTREAM * out ) const; + bool Blank() const; // returns true if all white space and new lines + // [internal use] +#ifdef TIXML_USE_STL + + virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ); +#endif + +private: +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + <?xml version="1.0" standalone="yes"?> + @endverbatim + + TinyXml will happily read or write files without a declaration, + however. There are 3 possible attributes to the declaration: + version, encoding, and standalone. + + Note: In this version of the code, the attributes are + handled as special cases, not generic attributes, simply + because there can only be at most 3 and they are always the same. +*/ +class TiXmlDeclaration : public TiXmlNode +{ +public: + /// Construct an empty declaration. + TiXmlDeclaration() : TiXmlNode( TiXmlNode::DECLARATION ) + {} + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ); +#endif + + /// Construct. + TiXmlDeclaration( const char* _version, + const char* _encoding, + const char* _standalone ); + + TiXmlDeclaration( const TiXmlDeclaration& copy ); + void operator=( const TiXmlDeclaration& copy ); + + virtual ~TiXmlDeclaration() + {} + + /// Version. Will return an empty string if none was found. + const char *Version() const + { + return version.c_str (); + } + /// Encoding. Will return an empty string if none was found. + const char *Encoding() const + { + return encoding.c_str (); + } + /// Is this a standalone document? + const char *Standalone() const + { + return standalone.c_str (); + } + + /// Creates a copy of this Declaration and returns it. + virtual TiXmlNode* Clone() const; + /// Print this declaration to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + +protected: + void CopyTo( TiXmlDeclaration* target ) const; + // used to be public +#ifdef TIXML_USE_STL + + virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ); +#endif + + virtual void StreamOut ( TIXML_OSTREAM * out) const; + +private: + + TIXML_STRING version; + TIXML_STRING encoding; + TIXML_STRING standalone; +}; + + +/** Any tag that tinyXml doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into TiXmlUnknowns. +*/ +class TiXmlUnknown : public TiXmlNode +{ +public: + TiXmlUnknown() : TiXmlNode( TiXmlNode::UNKNOWN ) + {} + virtual ~TiXmlUnknown() + {} + + TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::UNKNOWN ) + { + copy.CopyTo( this ); + } + void operator=( const TiXmlUnknown& copy ) + { + copy.CopyTo( this ); + } + + /// Creates a copy of this Unknown and returns it. + virtual TiXmlNode* Clone() const; + /// Print this Unknown to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + +protected: + void CopyTo( TiXmlUnknown* target ) const; + +#ifdef TIXML_USE_STL + + virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ); +#endif + + virtual void StreamOut ( TIXML_OSTREAM * out ) const; + +private: + +}; + + +/** Always the top level node. A document binds together all the + XML pieces. It can be saved, loaded, and printed to the screen. + The 'value' of a document node is the xml file name. +*/ +class TiXmlDocument : public TiXmlNode +{ +public: + /// Create an empty document, that has no name. + TiXmlDocument(); + /// Create a document with a name. The name of the document is also the filename of the xml. + TiXmlDocument( const char * documentName ); + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlDocument( const std::string& documentName ); +#endif + + TiXmlDocument( const TiXmlDocument& copy ); + void operator=( const TiXmlDocument& copy ); + + virtual ~TiXmlDocument() + {} + + /** Load a file using the current document value. + Returns true if successful. Will delete any existing + document data before loading. + */ + bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the current document value. Returns true if successful. + bool SaveFile() const; + /// Load a file using the given filename. Returns true if successful. + bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given filename. Returns true if successful. + bool SaveFile( const char * filename ) const; + +#ifdef TIXML_USE_STL + + bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. + { + StringToBuffer f( filename ); + return ( f.buffer && LoadFile( f.buffer, encoding )); + } + bool SaveFile( const std::string& filename ) const ///< STL std::string version. + { + StringToBuffer f( filename ); + return ( f.buffer && SaveFile( f.buffer )); + } +#endif + + /** Parse the given null terminated block of xml data. Passing in an encoding to this + method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml + to use that encoding, regardless of what TinyXml might otherwise try to detect. + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + + /** Get the root element -- the only top level element -- of the document. + In well formed XML, there should only be one. TinyXml is tolerant of + multiple elements at the document level. + */ + TiXmlElement* RootElement() const + { + return FirstChildElement(); + } + + /** If an error occurs, Error will be set to true. Also, + - The ErrorId() will contain the integer identifier of the error (not generally useful) + - The ErrorDesc() method will return the name of the error. (very useful) + - The ErrorRow() and ErrorCol() will return the location of the error (if known) + */ + bool Error() const + { + return error; + } + + /// Contains a textual (english) description of the error if one occurs. + const char * ErrorDesc() const + { + return errorDesc.c_str (); + } + + /** Generally, you probably want the error string ( ErrorDesc() ). But if you + prefer the ErrorId, this function will fetch it. + */ + const int ErrorId() const + { + return errorId; + } + + /** Returns the location (if known) of the error. The first column is column 1, + and the first row is row 1. A value of 0 means the row and column wasn't applicable + (memory errors, for example, have no row/column) or the parser lost the error. (An + error in the error reporting, in that case.) + + @sa SetTabSize, Row, Column + */ + int ErrorRow() + { + return errorLocation.row+1; + } + int ErrorCol() + { + return errorLocation.col+1; + } ///< The column where the error occured. See ErrorRow() + + /** By calling this method, with a tab size + greater than 0, the row and column of each node and attribute is stored + when the file is loaded. Very useful for tracking the DOM back in to + the source file. + + The tab size is required for calculating the location of nodes. If not + set, the default of 4 is used. The tabsize is set per document. Setting + the tabsize to 0 disables row/column tracking. + + Note that row and column tracking is not supported when using operator>>. + + The tab size needs to be enabled before the parse or load. Correct usage: + @verbatim + TiXmlDocument doc; + doc.SetTabSize( 8 ); + doc.Load( "myfile.xml" ); + @endverbatim + + @sa Row, Column + */ + void SetTabSize( int _tabsize ) + { + tabsize = _tabsize; + } + + int TabSize() const + { + return tabsize; + } + + /** If you have handled the error, it can be reset with this call. The error + state is automatically cleared if you Parse a new XML block. + */ + void ClearError() + { + error = false; + errorId = 0; + errorDesc = ""; + errorLocation.row = errorLocation.col = 0; + //errorLocation.last = 0; + } + + /** Dump the document to standard out. */ + void Print() const + { + Print( stdout, 0 ); + } + + /// Print this Document to a FILE stream. + virtual void Print( FILE* cfile, int depth = 0 ) const; + // [internal use] + void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + +protected : + virtual void StreamOut ( TIXML_OSTREAM * out) const; + // [internal use] + virtual TiXmlNode* Clone() const; +#ifdef TIXML_USE_STL + + virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ); +#endif + +private: + void CopyTo( TiXmlDocument* target ) const; + + bool error; + int errorId; + TIXML_STRING errorDesc; + int tabsize; + TiXmlCursor errorLocation; +}; + + +/** + A TiXmlHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + <Document> + <Element attributeA = "valueA"> + <Child attributeB = "value1" /> + <Child attributeB = "value2" /> + </Element> + <Document> + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + TiXmlElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + TiXmlElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + TiXmlElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + TiXmlElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity + of such code. A TiXmlHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + TiXmlHandle docHandle( &document ); + TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).Element(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + TiXmlHandle handleCopy = handle; + @endverbatim + + What they should not be used for is iteration: + + @verbatim + int i=0; + while ( true ) + { + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).Element(); + if ( !child ) + break; + // do something + ++i; + } + @endverbatim + + It seems reasonable, but it is in fact two embedded while loops. The Child method is + a linear walk to find the element, so this code would iterate much more than it needs + to. Instead, prefer: + + @verbatim + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).Element(); + + for( child; child; child=child->NextSiblingElement() ) + { + // do something + } + @endverbatim +*/ +class TiXmlHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + TiXmlHandle( TiXmlNode* node ) + { + this->node = node; + } + /// Copy constructor + TiXmlHandle( const TiXmlHandle& ref ) + { + this->node = ref.node; + } + TiXmlHandle operator=( const TiXmlHandle& ref ) + { + this->node = ref.node; + return *this; + } + + /// Return a handle to the first child node. + TiXmlHandle FirstChild() const; + /// Return a handle to the first child node with the given name. + TiXmlHandle FirstChild( const char * value ) const; + /// Return a handle to the first child element. + TiXmlHandle FirstChildElement() const; + /// Return a handle to the first child element with the given name. + TiXmlHandle FirstChildElement( const char * value ) const; + + /** Return a handle to the "index" child with the given name. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( const char* value, int index ) const; + /** Return a handle to the "index" child. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( int index ) const; + /** Return a handle to the "index" child element with the given name. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( const char* value, int index ) const; + /** Return a handle to the "index" child element. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( int index ) const; + +#ifdef TIXML_USE_STL + + TiXmlHandle FirstChild( const std::string& _value ) const + { + return FirstChild( _value.c_str() ); + } + TiXmlHandle FirstChildElement( const std::string& _value ) const + { + return FirstChildElement( _value.c_str() ); + } + + TiXmlHandle Child( const std::string& _value, int index ) const + { + return Child( _value.c_str(), index ); + } + TiXmlHandle ChildElement( const std::string& _value, int index ) const + { + return ChildElement( _value.c_str(), index ); + } +#endif + + /// Return the handle as a TiXmlNode. This may return null. + TiXmlNode* Node() const + { + return node; + } + /// Return the handle as a TiXmlElement. This may return null. + TiXmlElement* Element() const + { + return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); + } + /// Return the handle as a TiXmlText. This may return null. + TiXmlText* Text() const + { + return ( ( node && node->ToText() ) ? node->ToText() : 0 ); + } + /// Return the handle as a TiXmlUnknown. This may return null; + TiXmlUnknown* Unknown() const + { + return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); + } + +private: + TiXmlNode* node; +}; + + +#endif + diff --git a/yhttpd/src/contrib/xml/tinyxmlerror.cpp b/yhttpd/src/contrib/xml/tinyxmlerror.cpp new file mode 100644 index 0000000..9243e89 --- /dev/null +++ b/yhttpd/src/contrib/xml/tinyxmlerror.cpp @@ -0,0 +1,51 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "tinyxml.h" + +// The goal of the seperate error file is to make the first +// step towards localization. tinyxml (currently) only supports +// latin-1, but at least the error messages could now be translated. +// +// It also cleans up the code a bit. +// + +const char* TiXmlBase::errorString[ TIXML_ERROR_STRING_COUNT ] = + { + "No error", + "Error", + "Failed to open file", + "Memory allocation failed.", + "Error parsing Element.", + "Failed to read Element name", + "Error reading Element value.", + "Error reading Attributes.", + "Error: empty tag.", + "Error reading end tag.", + "Error parsing Unknown.", + "Error parsing Comment.", + "Error parsing Declaration.", + "Error document empty.", + "Error null (0) or unexpected EOF found in input stream.", + }; diff --git a/yhttpd/src/contrib/xml/tinyxmlparser.cpp b/yhttpd/src/contrib/xml/tinyxmlparser.cpp new file mode 100644 index 0000000..9c696b6 --- /dev/null +++ b/yhttpd/src/contrib/xml/tinyxmlparser.cpp @@ -0,0 +1,1553 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "tinyxml.h" +#include <ctype.h> + +//#define DEBUG_PARSER + +// Note tha "PutString" hardcodes the same list. This +// is less flexible than it appears. Changing the entries +// or order will break putstring. +TiXmlBase::Entity TiXmlBase::entity[ NUM_ENTITY ] = + { + { "&", 5, '&' }, + { "<", 4, '<' }, + { ">", 4, '>' }, + { """, 6, '\"' }, + { "'", 6, '\'' } + }; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// Including the basic of this table, which determines the #bytes in the +// sequence from the lead byte. 1 placed for invalid sequences -- +// although the result will be junk, pass it through as much as possible. +// Beware of the non-characters in UTF-8: +// ef bb bf (Microsoft "lead bytes") +// ef bf be +// ef bf bf + + + +const int TiXmlBase::utf8ByteTable[256] = + { + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte + 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid + }; + + +void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = + { + 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC + }; + + if (input < 0x80) + *length = 1; + else if ( input < 0x800 ) + *length = 2; + else if ( input < 0x10000 ) + *length = 3; + else if ( input < 0x200000 ) + *length = 4; + else + { + *length = 0; + return; + } // This code won't covert this correctly anyway. + + output += *length; + + // Scary scary fall throughs. + switch (*length) + { + case 4: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 3: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 2: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 1: + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); + } +} + + +/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding encoding ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + + // if ( encoding == TIXML_ENCODING_UTF8 ) + // { + if ( anyByte < 127 ) + return isalpha( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. + // } + // else + // { + // return isalpha( anyByte ); + // } +} + + +/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + + // if ( encoding == TIXML_ENCODING_UTF8 ) + // { + if ( anyByte < 127 ) + return isalnum( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. + // } + // else + // { + // return isalnum( anyByte ); + // } +} + + +class TiXmlParsingData +{ + friend class TiXmlDocument; +public: + void Stamp( const char* now, TiXmlEncoding encoding ); + + const TiXmlCursor& Cursor() + { + return cursor; + } + +private: + // Only used by the document! + TiXmlParsingData( const char* start, int _tabsize, int row, int col ) + { + assert( start ); + stamp = start; + tabsize = _tabsize; + cursor.row = row; + cursor.col = col; + } + + TiXmlCursor cursor; + const char* stamp; + int tabsize; +}; + + +void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) +{ + assert( now ); + + // Do nothing if the tabsize is 0. + if ( tabsize < 1 ) + { + return; + } + + // Get the current row, column. + int row = cursor.row; + int col = cursor.col; + const char* p = stamp; + assert( p ); + + while ( p < now ) + { + // Code contributed by Fletcher Dunn: (modified by lee) + switch (*p) + { + case 0: + // We *should* never get here, but in case we do, don't + // advance past the terminating null character, ever + return; + + case '\r': + // bump down to the next line + ++row; + col = 0; + // Eat the character + ++p; + + // Check for \r\n sequence, and treat this as a single character + if (*p == '\n') + { + ++p; + } + break; + + case '\n': + // bump down to the next line + ++row; + col = 0; + + // Eat the character + ++p; + + // Check for \n\r sequence, and treat this as a single + // character. (Yes, this bizarre thing does occur still + // on some arcane platforms...) + if (*p == '\r') + { + ++p; + } + break; + + case '\t': + // Eat the character + ++p; + + // Skip to next tab stop + col = (col / tabsize + 1) * tabsize; + break; + + case (char)(0xef): + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( *(p+1) && *(p+2) ) + { + // In these cases, don't advance the column. These are + // 0-width spaces. + if ( *(p+1)==(char)(0xbb) && *(p+2)==(char)(0xbf) ) + p += 3; + else if ( *(p+1)==(char)(0xbf) && *(p+2)==(char)(0xbe) ) + p += 3; + else if ( *(p+1)==(char)(0xbf) && *(p+2)==(char)(0xbf) ) + p += 3; + else + { + p +=3; + ++col; + } // A normal character. + } + } + else + { + ++p; + ++col; + } + break; + + default: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // Eat the 1 to 4 byte utf8 character. + int step = TiXmlBase::utf8ByteTable[*((unsigned char*)p)]; + if ( step == 0 ) + step = 1; // Error case from bad encoding, but handle gracefully. + p += step; + + // Just advance one column, of course. + ++col; + } + else + { + ++p; + ++col; + } + break; + } + } + cursor.row = row; + cursor.col = col; + assert( cursor.row >= -1 ); + assert( cursor.col >= -1 ); + stamp = p; + assert( stamp ); +} + + +const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) +{ + if ( !p || !*p ) + { + return 0; + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + while ( *p ) + { + // Skip the stupid Microsoft UTF-8 Byte order marks + if ( *(p+0)==(char) 0xef + && *(p+1)==(char) 0xbb + && *(p+2)==(char) 0xbf ) + { + p += 3; + continue; + } + else if(*(p+0)==(char) 0xef + && *(p+1)==(char) 0xbf + && *(p+2)==(char) 0xbe ) + { + p += 3; + continue; + } + else if(*(p+0)==(char) 0xef + && *(p+1)==(char) 0xbf + && *(p+2)==(char) 0xbf ) + { + p += 3; + continue; + } + + if ( IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) // Still using old rules for white space. + ++p; + else + break; + } + } + else + { + while ( *p && IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) + ++p; + } + + return p; +} + +#ifdef TIXML_USE_STL +/*static*/ bool TiXmlBase::StreamWhiteSpace( TIXML_ISTREAM * in, TIXML_STRING * tag ) +{ + for( ;; ) + { + if ( !in->good() ) + return false; + + int c = in->peek(); + // At this scope, we can't get to a document. So fail silently. + if ( !IsWhiteSpace( c ) || c <= 0 ) + return true; + + *tag += (char) in->get + (); + } +} + +/*static*/ bool TiXmlBase::StreamTo( TIXML_ISTREAM * in, int character, TIXML_STRING * tag ) +{ + //assert( character > 0 && character < 128 ); // else it won't work in utf-8 + while ( in->good() ) + { + int c = in->peek(); + if ( c == character ) + return true; + if ( c <= 0 ) // Silent failure: can't get document at this scope + return false; + + in->get + (); + *tag += (char) c; + } + return false; +} +#endif + +const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) +{ + *name = ""; + assert( p ); + + // Names start with letters or underscores. + // Of course, in unicode, tinyxml has no idea what a letter *is*. The + // algorithm is generous. + // + // After that, they can be letters, underscores, numbers, + // hyphens, or colons. (Colons are valid ony for namespaces, + // but tinyxml can't tell namespaces from names.) + if ( p && *p + && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) ) + { + while( p && *p + && ( IsAlphaNum( (unsigned char ) *p, encoding ) + || *p == '_' + || *p == '-' + || *p == '.' + || *p == ':' ) ) + { + (*name) += *p; + ++p; + } + return p; + } + return 0; +} + +const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) +{ + // Presume an entity, and pull it out. + TIXML_STRING ent; + int i; + *length = 0; + + if ( *(p+1) && *(p+1) == '#' && *(p+2) ) + { + unsigned long ucs = 0; + unsigned delta = 0; + unsigned mult = 1; + + if ( *(p+2) == 'x' ) + { + // Hexadecimal. + if ( !*(p+3) ) + return 0; + + const char* q = p+3; + q = strchr( q, ';' ); + + if ( !q || !*q ) + return 0; + + delta = q-p; + --q; + + while ( *q != 'x' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else if ( *q >= 'a' && *q <= 'f' ) + ucs += mult * (*q - 'a' + 10); + else if ( *q >= 'A' && *q <= 'F' ) + ucs += mult * (*q - 'A' + 10 ); + else + return 0; + mult *= 16; + --q; + } + } + else + { + // Decimal. + if ( !*(p+2) ) + return 0; + + const char* q = p+2; + q = strchr( q, ';' ); + + if ( !q || !*q ) + return 0; + + delta = q-p; + --q; + + while ( *q != '#' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else + return 0; + mult *= 10; + --q; + } + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + } + else + { + *value = (char)ucs; + *length = 1; + } + return p + delta + 1; + } + + // Now try to match it. + for( i=0; i<NUM_ENTITY; ++i ) + { + if ( strncmp( entity[i].str, p, entity[i].strLength ) == 0 ) + { + assert( strlen( entity[i].str ) == entity[i].strLength ); + *value = entity[i].chr; + *length = 1; + return ( p + entity[i].strLength ); + } + } + + // So it wasn't an entity, its unrecognized, or something like that. + *value = *p; // Don't put back the last one, since we return it! + return p+1; +} + + +bool TiXmlBase::StringEqual( const char* p, + const char* tag, + bool ignoreCase, + TiXmlEncoding encoding ) +{ + assert( p ); + assert( tag ); + if ( !p || !*p ) + { + assert( 0 ); + return false; + } + + const char* q = p; + + if ( ignoreCase ) + { + while ( *q && *tag && ToLower( *q, encoding ) == ToLower( *tag, encoding ) ) + { + ++q; + ++tag; + } + + if ( *tag == 0 ) + return true; + } + else + { + while ( *q && *tag && *q == *tag ) + { + ++q; + ++tag; + } + + if ( *tag == 0 ) // Have we found the end of the tag, and everything equal? + return true; + } + return false; +} + +const char* TiXmlBase::ReadText( const char* p, + TIXML_STRING * text, + bool trimWhiteSpace, + const char* endTag, + bool caseInsensitive, + TiXmlEncoding encoding ) +{ + *text = ""; + if ( !trimWhiteSpace // certain tags always keep whitespace + || !condenseWhiteSpace ) // if true, whitespace is always kept + { + // Keep all the white space. + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) + ) + { + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + text->append( cArr, len ); + } + } + else + { + bool whitespace = false; + + // Remove leading white space: + p = SkipWhiteSpace( p, encoding ); + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) ) + { + if ( *p == '\r' || *p == '\n' ) + { + whitespace = true; + ++p; + } + else if ( IsWhiteSpace( *p ) ) + { + whitespace = true; + ++p; + } + else + { + // If we've found whitespace, add it before the + // new character. Any whitespace just becomes a space. + if ( whitespace ) + { + (*text) += ' '; + whitespace = false; + } + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + if ( len == 1 ) + (*text) += cArr[0]; // more efficient + else + text->append( cArr, len ); + } + } + } + return p + strlen( endTag ); +} + +#ifdef TIXML_USE_STL + +void TiXmlDocument::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ) +{ + // The basic issue with a document is that we don't know what we're + // streaming. Read something presumed to be a tag (and hope), then + // identify it, and call the appropriate stream method on the tag. + // + // This "pre-streaming" will never read the closing ">" so the + // sub-tag can orient itself. + + if ( !StreamTo( in, '<', tag ) ) + { + SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + while ( in->good() ) + { + int tagIndex = (int) tag->length(); + while ( in->good() && in->peek() != '>' ) + { + int c = in->get + (); + if ( c <= 0 ) + { + SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + break; + } + (*tag) += (char) c; + } + + if ( in->good() ) + { + // We now have something we presume to be a node of + // some sort. Identify it, and call the node to + // continue streaming. + TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); + + if ( node ) + { + node->StreamIn( in, tag ); + bool isElement = node->ToElement() != 0; + delete node; + node = 0; + + // If this is the root element, we're done. Parsing will be + // done by the >> operator. + if ( isElement ) + { + return; + } + } + else + { + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + } + } + // We should have returned sooner. + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); +} + +#endif + +const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) +{ + ClearError(); + + // Parse away, at the document level. Since a document + // contains nothing but other tags, most of what happens + // here is skipping white space. + if ( !p || !*p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + // Note that, for a document, this needs to come + // before the while space skip, so that parsing + // starts from the pointer we are given. + location.Clear(); + if ( prevData ) + { + location.row = prevData->cursor.row; + location.col = prevData->cursor.col; + } + else + { + location.row = 0; + location.col = 0; + } + TiXmlParsingData data( p, TabSize(), location.row, location.col ); + location = data.Cursor(); + + if ( encoding == TIXML_ENCODING_UNKNOWN ) + { + // Check for the Microsoft UTF-8 lead bytes. + if ( *(p+0) && *(p+0) == (char)(0xef) + && *(p+1) && *(p+1) == (char)(0xbb) + && *(p+2) && *(p+2) == (char)(0xbf) ) + { + encoding = TIXML_ENCODING_UTF8; + } + } + + p = SkipWhiteSpace( p, encoding ); + if ( !p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + while ( p && *p ) + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, &data, encoding ); + LinkEndChild( node ); + } + else + { + break; + } + + // Did we get encoding info? + if ( encoding == TIXML_ENCODING_UNKNOWN + && node->ToDeclaration() ) + { + TiXmlDeclaration* dec = node->ToDeclaration(); + const char* enc = dec->Encoding(); + assert( enc ); + + if ( *enc == 0 ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice + else + encoding = TIXML_ENCODING_LEGACY; + } + + p = SkipWhiteSpace( p, encoding ); + } + + // All is well. + return p; +} + +void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + // The first error in a chain is more accurate - don't set again! + if ( error ) + return; + + assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); + error = true; + errorId = err; + errorDesc = errorString[ errorId ]; + + errorLocation.Clear(); + if ( pError && data ) + { + //TiXmlParsingData data( pError, prevData ); + data->Stamp( pError, encoding ); + errorLocation = data->Cursor(); + } +} + + +TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) +{ + TiXmlNode* returnNode = 0; + + p = SkipWhiteSpace( p, encoding ); + if( !p || !*p || *p != '<' ) + { + return 0; + } + + TiXmlDocument* doc = GetDocument(); + p = SkipWhiteSpace( p, encoding ); + + if ( !p || !*p ) + { + return 0; + } + + // What is this thing? + // - Elements start with a letter or underscore, but xml is reserved. + // - Comments: <!-- + // - Decleration: <?xml + // - Everthing else is unknown to tinyxml. + // + + const char* xmlHeader = + { "<?xml" + }; + const char* commentHeader = + { "<!--" + }; + const char* dtdHeader = + { "<!" + }; + + if ( StringEqual( p, xmlHeader, true, encoding ) ) + { +#ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Declaration\n" ); +#endif + + returnNode = new TiXmlDeclaration(); + } + else if ( StringEqual( p, commentHeader, false, encoding ) ) + { +#ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Comment\n" ); +#endif + + returnNode = new TiXmlComment(); + } + else if ( StringEqual( p, dtdHeader, false, encoding ) ) + { +#ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Unknown(1)\n" ); +#endif + + returnNode = new TiXmlUnknown(); + } + else if ( IsAlpha( *(p+1), encoding ) + || *(p+1) == '_' ) + { +#ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Element\n" ); +#endif + + returnNode = new TiXmlElement( "" ); + } + else + { +#ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Unknown(2)\n" ); +#endif + + returnNode = new TiXmlUnknown(); + } + + if ( returnNode ) + { + // Set the parent, so it can report errors + returnNode->parent = this; + } + else + { + if ( doc ) + doc->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } + return returnNode; +} + +#ifdef TIXML_USE_STL + +void TiXmlElement::StreamIn (TIXML_ISTREAM * in, TIXML_STRING * tag) +{ + // We're called with some amount of pre-parsing. That is, some of "this" + // element is in "tag". Go ahead and stream to the closing ">" + while( in->good() ) + { + int c = in->get + (); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c ; + + if ( c == '>' ) + break; + } + + if ( tag->length() < 3 ) + return; + + // Okay...if we are a "/>" tag, then we're done. We've read a complete tag. + // If not, identify and stream. + + if ( tag->at( tag->length() - 1 ) == '>' + && tag->at( tag->length() - 2 ) == '/' ) + { + // All good! + return; + } + else if ( tag->at( tag->length() - 1 ) == '>' ) + { + // There is more. Could be: + // text + // closing tag + // another node. + for ( ;; ) + { + StreamWhiteSpace( in, tag ); + + // Do we have text? + if ( in->good() && in->peek() != '<' ) + { + // Yep, text. + TiXmlText text( "" ); + text.StreamIn( in, tag ); + + // What follows text is a closing tag or another node. + // Go around again and figure it out. + continue; + } + + // We now have either a closing tag...or another node. + // We should be at a "<", regardless. + if ( !in->good() ) + return; + assert( in->peek() == '<' ); + int tagIndex = tag->length(); + + bool closingTag = false; + bool firstCharFound = false; + + for( ;; ) + { + if ( !in->good() ) + return; + + int c = in->peek(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + if ( c == '>' ) + break; + + *tag += (char) c; + in->get + (); + + if ( !firstCharFound && c != '<' && !IsWhiteSpace( c ) ) + { + firstCharFound = true; + if ( c == '/' ) + closingTag = true; + } + } + // If it was a closing tag, then read in the closing '>' to clean up the input stream. + // If it was not, the streaming will be done by the tag. + if ( closingTag ) + { + if ( !in->good() ) + return; + + int c = in->get + (); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + assert( c == '>' ); + *tag += (char) c; + + // We are done, once we've found our closing tag. + return; + } + else + { + // If not a closing tag, id it, and stream. + const char* tagloc = tag->c_str() + tagIndex; + TiXmlNode* node = Identify( tagloc, TIXML_DEFAULT_ENCODING ); + if ( !node ) + return; + node->StreamIn( in, tag ); + delete node; + node = 0; + + // No return: go around from the beginning: text, closing tag, or node. + } + } + } +} +#endif + +const char* TiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + TiXmlDocument* document = GetDocument(); + + if ( !p || !*p ) + { + if ( document ) + document->SetError( TIXML_ERROR_PARSING_ELEMENT, 0, 0, encoding ); + return 0; + } + + // TiXmlParsingData data( p, prevData ); + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + + if ( *p != '<' ) + { + if ( document ) + document->SetError( TIXML_ERROR_PARSING_ELEMENT, p, data, encoding ); + return 0; + } + + p = SkipWhiteSpace( p+1, encoding ); + + // Read the name. + const char* pErr = p; + + p = ReadName( p, &value, encoding ); + if ( !p || !*p ) + { + if ( document ) + document->SetError( TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, pErr, data, encoding ); + return 0; + } + + TIXML_STRING endTag ("</"); + endTag += value; + endTag += ">"; + + // Check for and read attributes. Also look for an empty + // tag or an end tag. + while ( p && *p ) + { + pErr = p; + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) + document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + if ( *p == '/' ) + { + ++p; + // Empty tag. + if ( *p != '>' ) + { + if ( document ) + document->SetError( TIXML_ERROR_PARSING_EMPTY, p, data, encoding ); + return 0; + } + return (p+1); + } + else if ( *p == '>' ) + { + // Done with attributes (if there were any.) + // Read the value -- which can include other + // elements -- read the end tag, and return. + ++p; + p = ReadValue( p, data, encoding ); // Note this is an Element method, and will set the error if one happens. + if ( !p || !*p ) + return 0; + + // We should find the end tag now + if ( StringEqual( p, endTag.c_str(), false, encoding ) ) + { + p += endTag.length(); + return p; + } + else + { + if ( document ) + document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding ); + return 0; + } + } + else + { + // Try to read an attribute: + TiXmlAttribute* attrib = new TiXmlAttribute(); + if ( !attrib ) + { + if ( document ) + document->SetError( TIXML_ERROR_OUT_OF_MEMORY, pErr, data, encoding ); + return 0; + } + + attrib->SetDocument( document ); + const char* pErr = p; + p = attrib->Parse( p, data, encoding ); + + if ( !p || !*p ) + { + if ( document ) + document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding ); + delete attrib; + return 0; + } + + // Handle the strange case of double attributes: + TiXmlAttribute* node = attributeSet.Find( attrib->Name() ); + if ( node ) + { + node->SetValue( attrib->Value() ); + delete attrib; + return 0; + } + + attributeSet.Add( attrib ); + } + } + return p; +} + + +const char* TiXmlElement::ReadValue( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + TiXmlDocument* document = GetDocument(); + + const char* pWithWhiteSpace = p; + // Read in text and elements in any order. + p = SkipWhiteSpace( p, encoding ); + while ( p && *p ) + { + if ( *p != '<' ) + { + // Take what we have, make a text element. + TiXmlText* textNode = new TiXmlText( "" ); + + if ( !textNode ) + { + if ( document ) + document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, encoding ); + return 0; + } + + if ( TiXmlBase::IsWhiteSpaceCondensed() ) + { + p = textNode->Parse( p, data, encoding ); + } + else + { + // Special case: we want to keep the white space + // so that leading spaces aren't removed. + p = textNode->Parse( pWithWhiteSpace, data, encoding ); + } + + if ( !textNode->Blank() ) + LinkEndChild( textNode ); + else + delete textNode; + } + else + { + // We hit a '<' + // Have we hit a new element or an end tag? + if ( StringEqual( p, "</", false, encoding ) ) + { + return p; + } + else + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, data, encoding ); + LinkEndChild( node ); + } + else + { + return 0; + } + } + } + p = SkipWhiteSpace( p, encoding ); + } + + if ( !p ) + { + if ( document ) + document->SetError( TIXML_ERROR_READING_ELEMENT_VALUE, 0, 0, encoding ); + } + return p; +} + + +#ifdef TIXML_USE_STL +void TiXmlUnknown::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get + (); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + + +const char* TiXmlUnknown::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + TiXmlDocument* document = GetDocument(); + p = SkipWhiteSpace( p, encoding ); + + // TiXmlParsingData data( p, prevData ); + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + if ( !p || !*p || *p != '<' ) + { + if ( document ) + document->SetError( TIXML_ERROR_PARSING_UNKNOWN, p, data, encoding ); + return 0; + } + ++p; + value = ""; + + while ( p && *p && *p != '>' ) + { + value += *p; + ++p; + } + + if ( !p ) + { + if ( document ) + document->SetError( TIXML_ERROR_PARSING_UNKNOWN, 0, 0, encoding ); + } + if ( *p == '>' ) + return p+1; + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlComment::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get + (); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + + if ( c == '>' + && tag->at( tag->length() - 2 ) == '-' + && tag->at( tag->length() - 3 ) == '-' ) + { + // All is well. + return; + } + } +} +#endif + + +const char* TiXmlComment::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + TiXmlDocument* document = GetDocument(); + value = ""; + + p = SkipWhiteSpace( p, encoding ); + + // TiXmlParsingData data( p, prevData ); + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + const char* startTag = "<!--"; + const char* endTag = "-->"; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + p = ReadText( p, &value, false, endTag, false, encoding ); + return p; +} + + +const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + return 0; + + int tabsize = 4; + if ( document ) + tabsize = document->TabSize(); + + // TiXmlParsingData data( p, prevData ); + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + // Read the name, the '=' and the value. + const char* pErr = p; + p = ReadName( p, &name, encoding ); + if ( !p || !*p ) + { + if ( document ) + document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p || *p != '=' ) + { + if ( document ) + document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + ++p; // skip '=' + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) + document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + const char* end; + + if ( *p == '\'' ) + { + ++p; + end = "\'"; + p = ReadText( p, &value, false, end, false, encoding ); + } + else if ( *p == '"' ) + { + ++p; + end = "\""; + p = ReadText( p, &value, false, end, false, encoding ); + } + else + { + // All attribute values should be in single or double quotes. + // But this is such a common error that the parser will try + // its best, even without them. + value = ""; + while ( p && *p // existence + && !IsWhiteSpace( *p ) && *p != '\n' && *p != '\r' // whitespace + && *p != '/' && *p != '>' ) // tag end + { + value += *p; + ++p; + } + } + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlText::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->peek(); + if ( c == '<' ) + return; + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + in->get + (); + } +} +#endif + +const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + value = ""; + // TiXmlParsingData data( p, prevData ); + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + bool ignoreWhite = true; + + const char* end = "<"; + p = ReadText( p, &value, ignoreWhite, end, false, encoding ); + if ( p ) + return p-1; // don't truncate the '<' + return 0; +} + +#ifdef TIXML_USE_STL +void TiXmlDeclaration::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get + (); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + +const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) +{ + p = SkipWhiteSpace( p, _encoding ); + // Find the beginning, find the end, and look for + // the stuff in-between. + TiXmlDocument* document = GetDocument(); + if ( !p || !*p || !StringEqual( p, "<?xml", true, _encoding ) ) + { + if ( document ) + document->SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); + return 0; + } + // TiXmlParsingData data( p, prevData ); + if ( data ) + { + data->Stamp( p, _encoding ); + location = data->Cursor(); + } + p += 5; + + version = ""; + encoding = ""; + standalone = ""; + + while ( p && *p ) + { + if ( *p == '>' ) + { + ++p; + return p; + } + + p = SkipWhiteSpace( p, _encoding ); + if ( StringEqual( p, "version", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + version = attrib.Value(); + } + else if ( StringEqual( p, "encoding", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + encoding = attrib.Value(); + } + else if ( StringEqual( p, "standalone", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + standalone = attrib.Value(); + } + else + { + // Read over whatever it is. + while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) + ++p; + } + } + return 0; +} + +bool TiXmlText::Blank() const +{ + for ( unsigned i=0; i<value.length(); i++ ) + if ( !IsWhiteSpace( value[i] ) ) + return false; + return true; +} + diff --git a/yhttpd/src/glob.h b/yhttpd/src/glob.h new file mode 100644 index 0000000..a2c1334 --- /dev/null +++ b/yhttpd/src/glob.h @@ -0,0 +1,198 @@ +/* + Notice: + + All #defines which start with an CONFIG can be edited through + gmake config in the main directory! +*/ + +#include "maps/hashmap.h" + +// global variables. +#ifndef GLOB_H +#define GLOB_H + +// Definition of boolean values. +#define true 1 +#define false 0 + + +/* - CONFIG - + Should yhttpd get compiled with OpenSSL support? +*/ +//#define OPENSSL + +/* - CONFIG - + Should yhttpd get compiled with comand line interface support? +*/ +#define CLI + +/* - CONFIG - + What should be the name of the config file? +*/ +#define CONFILE "yhttpd.conf" + +/* - DISABLED - + Enable debugging options. +*/ +//#define DEBUG + +/* - DISABLED - + If you want to enable EXPERIMENTAL features, then set this val- + ue to true. Else use false which is recommended! All experimen- + al features are marked inside of the running yhttpd! +*/ +//#define EXPERIM + +/* - CONFIG - + Should yhttpd get compiled with logging support? +*/ +#define LOGGING + +/* - CONFIG - + Please enter the highest networking port which is allowed to be + used. If yhttpd is unable to create the server socket on a cert- + ain port, it will increment the port number and retries to cre- + ate another socket on the incremented port number. This proced- + ure will continue until MAXPORT has been reached. +*/ +#define MAXPORT 65535 + +/* Specifies the max amount of lines to read from a HTTP request + header +*/ +#define MAXLINES 30 + +/* Specifies the max length of a lines to read from a HTTP request + header +*/ +#define MAXLENGTH 1024 + +/* - CONFIG - + Should yhttpd get compiled with ncurses support? +*/ +#define NCURSES + +/* - CONFIG - + Please specify the maximum length of a HTTP post request. +*/ +#define POSTBUF 512 + +/* - CONFIG - + Please specify the size of a temporary buffer. (Will be used f- + or different tasks) +*/ +#define READBUF 2048 + +/* - CONFIG - + Please specify the maximum length of a line read from a socket + or a file. ( config-file, html-template ) +*/ +#define READSOCK 2048 + +/* - CONFIG - + In which prefix should yhttpd be installed if typing gmake inst- + all? +*/ +#define PREFIX "/usr/local" + +/* - CONFIG - + DO NOT USE TOGETHER WITH NCURSES! Displays important server mes- + ages. This one will print all messages to stdout if no NCURSES + is defined. Don't use this until NCURSES is defined! all messag- + es will appear in the ncurses interface anyways. +*/ +//#define SERVMSG + +/* - CONFIG - + Set to true if you want yhttpd to catch the SIGSEGV signal. yhttpd + will print a warning message into the system messages and will + not core dump if an error occurs. +*/ +//#define CTCSEGV + +/* - CONFIG - + Please chose if you want to use verbose server outputs or not. + The verbose messages will appear in the ncurses menu if ncurses + is enabled or in the server-window if yhttpd has been compiled + without ncurses support. This option shows you all incoming + requests with the client IP and port numbers. You probably want + this to be turned off if you have heavy server load. +*/ +//#define VERBOSE + + + + +// The following values define the positions of the data stats in the NCURSES interface. +#ifdef NCURSES +#define NCUR_SERVER_HEADER_X 21 +#define NCUR_SERVER_HEADER_Y 2 +#define NCUR_PORT_X 22 +#define NCUR_PORT_Y 2 +#define NCUR_HITS_X 23 +#define NCUR_HITS_Y 2 + +#define NCUR_POOL_HEADER_X 21 +#define NCUR_POOL_HEADER_Y 16 +#define NCUR_POOL_WAIT_X 22 +#define NCUR_POOL_WAIT_Y 16 +#define NCUR_POOL_RUNNING_X 23 +#define NCUR_POOL_RUNNING_Y 16 + +#define NCUR_DATA_HEADER_X 21 +#define NCUR_DATA_HEADER_Y 35 +#define NCUR_GARBAGE_X 22 +#define NCUR_GARBAGE_Y 35 +#define NCUR_CON_QUEUE_X 23 +#define NCUR_CON_QUEUE_Y 35 + +#define NCUR_HTTPD_HEADER_X 21 +#define NCUR_HTTPD_HEADER_Y 52 +#define NCUR_NUM_ROOMS_X 22 +#define NCUR_NUM_ROOMS_Y 52 +#define NCUR_SESSION_X 23 +#define NCUR_SESSION_Y 52 + +#define NCUR_CACHED_HEADER_X 21 +#define NCUR_CACHED_HEADER_Y 68 +#define NCUR_CACHED_DOCS_X 22 +#define NCUR_CACHED_DOCS_Y 68 +#define NCUR_CACHED_MODS_X 23 +#define NCUR_CACHED_MODS_Y 68 + +#define NCUR_MENU_CHAR_X 0 +#define NCUR_MENU_CHAR_Y 33 +#define NCUR_UPTIME_X 0 +#define NCUR_UPTIME_Y 44 +#define NCUR_TIME_X 0 +#define NCUR_TIME_Y 64 + +#endif + +////////////////////////////////////////////////////////////////////////////////////////// +// DO NOT CHANGE ANYTHING BEHIND THIS LINE! +////////////////////////////////////////////////////////////////////////////////////////// + +using namespace std; + +typedef int function( void *v_arg ); + +struct container +{ + void* elem[4]; +}; + +struct dynmod +{ + function *the_func ; + void *the_module; +}; + +typedef enum method_ { + METH_NCURSES, + METH_RETSTRING +} method; + +// Define external executables: +#define GMAKE "/usr/local/bin/gmake \0" +#endif diff --git a/yhttpd/src/html.cpp b/yhttpd/src/html.cpp new file mode 100644 index 0000000..ace041a --- /dev/null +++ b/yhttpd/src/html.cpp @@ -0,0 +1,133 @@ +#ifndef HTML_CPP +#define HTML_CPP + +#include <fstream> +#include "html.h" + +using namespace std; + +html::html( ) +{ + set_name( wrap::CONF->get_elem( "httpd.templatedir" ) ); +} + +html::~html( ) +{} + +void +html::clear_cache( ) +{ + clear(); + wrap::system_message( CLRHTML ); + +#ifdef NCURSES + + print_cached( 0 ); +#endif +} + +string +html::parse( map<string,string> &map_params ) +{ + string s_file = map_params["request"]; + + // check if s_file is in the container. + string s_templ; + + // if not, read file. + if ( ! shashmap<string>::exists( s_file ) ) + { + string s_path = get_name(); + ifstream if_templ( s_path.append( s_file ).c_str(), ios::binary ); + + if ( ! if_templ ) + { + wrap::system_message( OFFFOUND + s_path ); + if(map_params["request"] == wrap::CONF->get_elem( "httpd.html.notfound" )) + return ""; + + map_params["request"] = wrap::CONF->get_elem( "httpd.html.notfound" ); + return parse( map_params ); + } + + char c_buf; + while( !if_templ.eof() ) + { + if_templ.get( c_buf ); + s_templ += c_buf; + } + + if ( map_params["content-type"].compare(0,5,"text/") == 0 ) + s_templ.erase(s_templ.end()-1); + + if_templ.close(); + + wrap::system_message( TECACHE + s_path ); + + // cache file. + shashmap<string>::add_elem(s_templ, s_file); +#ifdef NCURSES + + print_cached( shashmap<string>::size() ); +#endif + + } + else + { + s_templ = shashmap<string>::get_elem( s_file ); + } + + // find %%KEY%% token and substituate those. + unsigned pos[2]; + pos[0] = pos[1] = 0; + + for(;;) + { + pos[0] = s_templ.find( "%%", pos[1] ); + + if ( pos[0] == string::npos ) + break; + + pos[0] += 2; + pos[1] = s_templ.find( "%%", pos[0] ); + + if ( pos[0] == string::npos ) + break; + + // get key and val. + string s_key = s_templ.substr( pos[0], pos[1]-pos[0] ); + string s_val = wrap::CONF->get_elem( s_key ); + + // if s_val is empty use map_params. + if ( s_val.empty() ) + s_val = map_params[ s_key ]; + + // substituate key with val. + s_templ.replace( pos[0]-2, pos[1]-pos[0]+4, s_val ); + + // calculate the string displacement. + int i_diff = s_val.length() - ( pos[1] - pos[0] + 4); + + pos[1] += 2 + i_diff; + + }; + + return s_templ; +} + + +#ifdef NCURSES +void +html::print_cached( int i_docs ) +{ + if ( !wrap::NCUR->is_ready() ) + return; + + mvprintw( NCUR_CACHED_DOCS_X, NCUR_CACHED_DOCS_Y, "Docs: %d ", i_docs); + refresh(); +} + +#endif + +#endif + diff --git a/yhttpd/src/html.h b/yhttpd/src/html.h new file mode 100644 index 0000000..14a7161 --- /dev/null +++ b/yhttpd/src/html.h @@ -0,0 +1,38 @@ +// class html declaration. this class manages the html-template files. +#include "incl.h" + +#ifndef HTML_H +#define HTML_H + +#include "maps/shashmap.h" +#include "name.h" + +using namespace std; + +class html : public shashmap<string>, name +{ +public: + html( ); + ~html( ); + + // Clears the template cache so that new html templates will be read + // from hard disk. This method is needed after changeing s.t. on + // the html-template files. + void clear_cache( ); + + // Returns a parsed html-template. this method will check first if the + // required html-template exists inside the classes template cache. if not + // then the file will be read from file and added to the cache. + // afterwards the html-template will be parsed and returned. + // map_params contains the client request parameters which also will be + // used for string substituation. + string parse( map<string,string> &map_params ); + +#ifdef NCURSES + + void print_cached( int i_docs ); +#endif + +}; + +#endif diff --git a/yhttpd/src/incl.h b/yhttpd/src/incl.h new file mode 100644 index 0000000..31e220c --- /dev/null +++ b/yhttpd/src/incl.h @@ -0,0 +1,13 @@ +#include <pthread.h> +#include <iostream> +#include <string> +#include <map> + +#include "glob.h" + +#ifdef NCURSES +#include <ncurses.h> +#endif + +#include "msgs.h" +#include "wrap.h" diff --git a/yhttpd/src/logd.cpp b/yhttpd/src/logd.cpp new file mode 100644 index 0000000..664cd14 --- /dev/null +++ b/yhttpd/src/logd.cpp @@ -0,0 +1,175 @@ + +#ifndef LOGD_CPP +#define LOGD_CPP + +#include "logd.h" + +#ifdef LOGGING + +#include <fstream> + +logd::logd( string s_filename, string s_log_lines ) +{ + initialize( s_filename, tool::string2int(s_log_lines) ); +} + +logd::logd( string s_filename, int i_log_lines ) +{ + initialize( s_filename, i_log_lines ); +} + +logd::~logd() +{ + flush_logs(); + pthread_mutex_destroy( &mut_s_logging ); +} + +void +logd::initialize( string s_filename, int i_log_lines ) +{ + pthread_mutex_init( &mut_s_logging, NULL ); + + if( s_filename.empty() ) + { + wrap::system_message( LOGERR2 ); + exit(1); + } + + //if (wrap::NCUR->is_ready()) + // wrap::system_message(LOGGINI+s_filename); + + s_logfile = s_filename; + i_lines = i_log_lines; +} + +string +logd::get_time_string() +{ + struct tm *t_m; + time_t t_cur = time(NULL); + t_m = gmtime(&t_cur); + + char c_buf[100]; + c_buf[99] = '\0'; + strftime(c_buf, 100, "[%d/%b/%Y:%H:%M:%S %z]", t_m); + + return string(c_buf); +} + +void +logd::flush() +{ + ofstream of_output; + of_output.open(s_logfile.c_str(), ios::app); + + if( of_output == NULL ) + { + wrap::system_message( LOGERR1 + s_logfile ); + exit(1); + } + + while( ! s_queue.empty() ) + { + string s_l=s_queue.front(); + s_queue.pop(); + of_output.write( s_l.c_str(), s_l.size() ); + } + + of_output.close(); +} + +void +logd::log_access( map<string,string> &map_request ) +{ + //static int i_access_lines = wrap::CONF->get_elem("httpd.logging.access_lines"); + + string s_time = get_time_string(); + string s_logstr = map_request["REMOTE_ADDR"] + " - - "+s_time+" \"" + map_request["QUERY_STRING"]+"\" 200 0 \""+map_request["request"]+"\" \""+map_request["User-Agent"]+"\"\n"; + + pthread_mutex_lock ( &mut_s_logging ); + s_queue.push(s_logstr); + + if ( s_queue.size() > i_lines ) + flush(); + + pthread_mutex_unlock( &mut_s_logging ); +} + +void +logd::log_simple_line( string s_line ) +{ + // Dont log empty lines! + if (s_line.empty()) + return; + + string s_time = get_time_string(); + string s_logstr = s_time + " " + s_line; + + pthread_mutex_lock ( &mut_s_logging ); + s_queue.push(s_logstr); + + if ( s_queue.size() > i_lines ) + flush(); + + pthread_mutex_unlock( &mut_s_logging ); +} + +void +logd::set_logfile( string s_path, string s_filename ) +{ + // Remove "/" from filename! + unsigned i_pos = s_filename.find( "/" ); + while ( i_pos != string::npos ) + { + s_filename.replace( i_pos, 1, "SLASH" ); + i_pos = s_filename.find( "/" ); + } + + // Remove "\" from filename (for non unix systems)! + i_pos = s_filename.find( "\\" ); + while ( i_pos != string::npos ) + { + s_filename.replace( i_pos, 1, "BACKSLASH" ); + i_pos = s_filename.find( "\\" ); + } + + pthread_mutex_lock ( &mut_s_logging ); + this->s_logfile = s_path + s_filename; + pthread_mutex_unlock( &mut_s_logging ); +} + +void +logd::flush_logs() +{ + pthread_mutex_lock ( &mut_s_logging ); + flush(); + pthread_mutex_unlock( &mut_s_logging ); +} + +string +logd::remove_html_tags( string s_logs ) +{ + unsigned pos[2]; + + while ( (pos[0] = s_logs.find("<")) != string::npos ) + { + if ( (pos[1] = s_logs.find(">", pos[0])) != string::npos ) + s_logs.replace( pos[0], pos[1]-pos[0]+1, ""); + else + break; + } + + if ( s_logs == "\n" ) + return ""; + + return s_logs; +} + +void +logd::set_lines( const int i_lines ) +{ + this->i_lines = i_lines; +} + +#endif +#endif diff --git a/yhttpd/src/logd.h b/yhttpd/src/logd.h new file mode 100644 index 0000000..2d6270b --- /dev/null +++ b/yhttpd/src/logd.h @@ -0,0 +1,37 @@ +#include "incl.h" + +#ifdef LOGGING +#ifndef LOGD_H +#define LOGD_H + +#include <queue> +#include <time.h> + +class logd +{ +private: + string s_logfile; + queue<string> s_queue; + pthread_mutex_t mut_s_logging; + int i_lines; + + void initialize( string s_filename, int i_log_lines ); + void flush(); + void set_lines( const int i_lines ); + string get_time_string(); + +public: + logd( string s_filename, string s_log_lines ); + logd( string s_filename, int i_log_lines ); + ~logd(); + + void set_logfile( string s_path, string s_filename ); + void log_access( map<string,string> &map_request ); + void log_simple_line( string s_line ); + void flush_logs(); + static string remove_html_tags( string s_log ); +}; + +#endif +#endif + diff --git a/yhttpd/src/main.cpp b/yhttpd/src/main.cpp new file mode 100644 index 0000000..87f1cab --- /dev/null +++ b/yhttpd/src/main.cpp @@ -0,0 +1,101 @@ +/* + * yhttpd; Contact: www.yChat.org; Mail@yChat.org + * Copyright (C) 2003 Paul C. Buetow, Volker Richter + * Copyright (C) 2004 Paul C. Buetow + * Copyright (C) 2005 EXA Digital Solutions GbR + * ----------------------------------------------------------------- + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "incl.h" +#include "sign.h" + + +#include "maps/hashmap.h" + +using namespace std; + +map<string,string>* +parse_argc( int argc, char* argv[] ) +{ + map<string,string>* start_params = new map<string,string>; + + string s_output = ""; + + // Set to 1 if a config option key has to be read + // ( ./yhttpd -o key1 value1 -o key2 value2 ... ); + bool b_conf = 0; + + // Will store the key of an additional option value (see also b_conf) + string s_key; + + for (int i=1; argv[i] != 0; i++) + { + if ( !s_key.empty() ) + { + (*start_params)[s_key] = string(argv[i]); + s_key.clear(); + } + else if ( b_conf ) + { + s_key = string(argv[i]); + b_conf = 0; + } + else + { + if ( string(argv[i]).find("v") != string::npos ) + s_output.append(tool::yhttpd_version()+"\n"); + + if ( string(argv[i]).find("h") != string::npos ) + s_output.append( YCUSAGE ); + + if ( string(argv[i]).find("o") != string::npos ) + b_conf = 1; + } + } + + if ( !s_output.empty() ) + { + cout << s_output; + delete start_params; + exit(1); + } + + return start_params; +} + +int +main(int argc, char* argv[]) +{ + cout << tool::yhttpd_version() << endl + << DESCRIP << endl + << DESCRI2 << endl + << CONTACT << endl + << SEPERAT << endl; + + wrap::init_wrapper(parse_argc(argc, argv)); + + + sign::init_signal_handlers(); + + // start the socket manager. this one will listen for incoming http requests and will + // forward them to the specified routines which will generate a http response. + wrap::SOCK->start(); + + cout << DOWNMSG << endl; + return 0; +} diff --git a/yhttpd/src/maps/hashmap.h b/yhttpd/src/maps/hashmap.h new file mode 100644 index 0000000..b3a3212 --- /dev/null +++ b/yhttpd/src/maps/hashmap.h @@ -0,0 +1,53 @@ +#ifndef HASHMAP_H +#define HASHMAP_H + +#include <ext/hash_map> + +using namespace std; + +template<class key_type_> +struct compare_allocator +{ + inline bool operator()(key_type_ t_key_1, key_type_ t_key_2) const; +}; + +template<class key_type_> +struct equals_allocator +{ + inline bool operator()(key_type_ t_key_1, key_type_ t_key_2) const; +}; + +template<class key_type_> +struct size_hash +{ + inline int operator()(key_type_ t_key) const; +}; + +template<class key_type_> +struct self_hash +{ + inline int operator()(key_type_ t_key) const; +}; + +template +< +class obj_type, +class key_type_ = string, +class hash_type = size_hash<string>, +class alloc_type = compare_allocator<string> +> +struct hashmap : public __gnu_cxx::hash_map<key_type_, obj_type, hash_type, alloc_type> +{ + virtual inline void set_elem(obj_type t_obj, key_type_ t_key); + virtual inline obj_type get_elem(key_type_ t_key); + virtual inline obj_type get_set_elem(obj_type t_obj, key_type_ t_key); + virtual inline obj_type get_or_callback_set + (obj_type (*func)(void*), void* p_void, key_type_ t_key); + virtual inline vector<key_type_>* get_key_vector(); + virtual inline bool exists(key_type_ t_key); + virtual inline void run_func( void (*func)(obj_type) ); + virtual inline void run_func( void (*func)(obj_type, void*), void* v_arg ); +}; + +#include "hashmap.tmpl" +#endif diff --git a/yhttpd/src/maps/hashmap.tmpl b/yhttpd/src/maps/hashmap.tmpl new file mode 100644 index 0000000..9ee2f36 --- /dev/null +++ b/yhttpd/src/maps/hashmap.tmpl @@ -0,0 +1,126 @@ +template<class key_type_> +bool +compare_allocator<key_type_>::operator()(key_type_ t_key_1, key_type_ t_key_2) const +{ + return t_key_1.compare(t_key_2) == 0; +} + +template<class key_type_> +bool +equals_allocator<key_type_>::operator()(key_type_ t_key_1, key_type_ t_key_2) const +{ + return t_key_1 == t_key_2; +} + +template<class key_type_> +int +size_hash<key_type_>::operator()(key_type_ t_key) const +{ + int i_hash = 0; + int i_size = t_key.size(); + + for( size_t i = 0; i < i_size; ++i ) + i_hash = ( i_hash << 5 ) ^ t_key.at(i) ^ i_hash; + + return i_hash; +} + +template<class key_type_> +int +self_hash<key_type_>::operator()(key_type_ t_key) const +{ + return t_key; +} + + +template<class obj_type, class key_type_, class hash_type, class alloc_type> +obj_type +hashmap<obj_type, key_type_, hash_type, alloc_type>::get_set_elem(obj_type t_obj, key_type_ t_key) +{ + typename hashmap<obj_type, key_type_, hash_type, alloc_type>::iterator iter = this->find(t_key); + + if ( iter == this->end() ) + { + set_elem(t_obj, t_key); + return obj_type(); + } + + obj_type t_ret = iter->second; + iter->second = t_obj; + + return t_ret; +} + +template<class obj_type, class key_type_, class hash_type, class alloc_type> +obj_type +hashmap<obj_type, key_type_, hash_type, alloc_type>::get_or_callback_set +(obj_type (*func)(void*), void* p_void, key_type_ t_key) +{ + typename hashmap<obj_type, key_type_, hash_type, alloc_type>::iterator iter = this->find(t_key); + + if ( iter == this->end() ) + { + obj_type t_obj = (*func) (p_void); + set_elem(t_obj, t_key); + return t_obj; + } + + return iter->second; +} + +template<class obj_type, class key_type_, class hash_type, class alloc_type> +void +hashmap<obj_type, key_type_, hash_type, alloc_type>::set_elem(obj_type t_obj, key_type_ t_key) +{ + (*this)[t_key] = t_obj; +} + +template<class obj_type, class key_type_, class hash_type, class alloc_type> +obj_type +hashmap<obj_type, key_type_, hash_type, alloc_type>::get_elem(key_type_ t_key) +{ + typename hashmap<obj_type, key_type_, hash_type, alloc_type>::iterator iter = this->find(t_key); + + if ( iter != this->end() ) + return iter->second; + + return obj_type(); +} + +template<class obj_type, class key_type_, class hash_type, class alloc_type> +vector<key_type_>* +hashmap<obj_type, key_type_, hash_type, alloc_type>::get_key_vector() +{ + vector<key_type_>* p_vec = new vector<key_type_>; + typename hashmap<obj_type, key_type_, hash_type, alloc_type>::iterator iter; + + for ( iter = this->begin(); iter != this->end(); ++iter ) + p_vec->push_back(iter->first); + + return p_vec; +} + +template<class obj_type, class key_type_, class hash_type, class alloc_type> +bool +hashmap<obj_type, key_type_, hash_type, alloc_type>::exists(key_type_ t_key) +{ + return this->find(t_key) != this->end(); +} + +template<class obj_type, class key_type_, class hash_type, class alloc_type> +void +hashmap<obj_type, key_type_, hash_type, alloc_type>::run_func( void (*func)(obj_type) ) +{ + typename hashmap<obj_type, key_type_, hash_type, alloc_type>::iterator iter; + for ( iter = this->begin(); iter != this->end(); ++iter ) + ( *func ) ( iter->second ); +} + +template<class obj_type, class key_type_, class hash_type, class alloc_type> +void +hashmap<obj_type, key_type_, hash_type, alloc_type>::run_func( void (*func)(obj_type, void*), void* v_arg ) +{ + typename hashmap<obj_type, key_type_, hash_type, alloc_type>::iterator iter; + for ( iter = this->begin(); iter != this->end(); ++iter ) + ( *func ) ( iter->second, v_arg ); +} diff --git a/yhttpd/src/maps/mtools.h b/yhttpd/src/maps/mtools.h new file mode 100644 index 0000000..4ee45b8 --- /dev/null +++ b/yhttpd/src/maps/mtools.h @@ -0,0 +1,11 @@ +#ifndef MTOOLS_H +#define MTOOLS_H + +template <class type_> +struct mtools +{ + static void delete_obj(type_ type_obj); +}; + +#include "mtools.tmpl" +#endif diff --git a/yhttpd/src/maps/mtools.tmpl b/yhttpd/src/maps/mtools.tmpl new file mode 100644 index 0000000..6917131 --- /dev/null +++ b/yhttpd/src/maps/mtools.tmpl @@ -0,0 +1,12 @@ +#ifndef MTOOLS_TMPL +#define MTOOLS_TMPL + +template <class type_> +void +mtools<type_>::delete_obj( type_ type_obj ) +{ + if ( type_obj ) + delete type_obj; +} + +#endif diff --git a/yhttpd/src/maps/nhashmap.h b/yhttpd/src/maps/nhashmap.h new file mode 100644 index 0000000..f440230 --- /dev/null +++ b/yhttpd/src/maps/nhashmap.h @@ -0,0 +1,21 @@ +#ifndef NHASHMAP_H +#define NHASHMAP_H + +#include "shashmap.h" + +using namespace std; + +template +< +class obj_type, +class key_type_ = string, +class hash_type = size_hash<string>, +class alloc_type = compare_allocator<string> +> +struct nhashmap : public shashmap<obj_type, key_type_, hash_type, alloc_type> +{ + inline obj_type get_elem(key_type_ t_key); +}; + +#include "nhashmap.tmpl" +#endif diff --git a/yhttpd/src/maps/nhashmap.tmpl b/yhttpd/src/maps/nhashmap.tmpl new file mode 100644 index 0000000..3ea1934 --- /dev/null +++ b/yhttpd/src/maps/nhashmap.tmpl @@ -0,0 +1,11 @@ +template<class obj_type, class key_type_, class hash_type, class alloc_type> +obj_type +nhashmap<obj_type, key_type_, hash_type, alloc_type>::get_elem(key_type_ t_key) +{ + typename hashmap<obj_type, key_type_, hash_type, alloc_type>::iterator iter = this->find(t_key); + + if ( iter != this->end() ) + return iter->second; + + return NULL; +} diff --git a/yhttpd/src/maps/shashmap.h b/yhttpd/src/maps/shashmap.h new file mode 100644 index 0000000..f692e32 --- /dev/null +++ b/yhttpd/src/maps/shashmap.h @@ -0,0 +1,49 @@ +#ifndef SHASHMAP_H +#define SHASHMAP_H + +#include <pthread.h> +#include "hashmap.h" + +#include "../monitor/dump.h" + +using namespace std; + +template +< +class obj_type, +class key_type_ = string, +class hash_type = size_hash<string>, +class alloc_type = compare_allocator<string> +> +class shashmap : protected hashmap<obj_type, key_type_, hash_type, alloc_type>, + public dumpable +{ +private: + pthread_mutex_t mut_shashmap; + +protected: + virtual void dumpit(); + +public: + explicit shashmap(); + ~shashmap(); + virtual inline void set_elem(obj_type t_obj, key_type_ t_key); + virtual inline obj_type get_set_elem(obj_type t_obj, key_type_ t_key); + virtual inline obj_type get_or_callback_set + (obj_type (*func)(void*), void* p_void, key_type_ t_key); + virtual inline void add_elem(obj_type t_obj, key_type_ t_key); + virtual inline void add_elem_insecure(obj_type t_obj, key_type_ t_key); + virtual inline obj_type get_elem(key_type_ t_key); + virtual inline void del_elem(key_type_ t_key); + virtual inline void del_elem_insecure(key_type_ t_key); + virtual inline void clear(); + virtual inline int size(); + virtual inline bool exists(key_type_ t_key); + virtual inline vector<key_type_>* get_key_vector(); + virtual inline void run_func( void (*func)(obj_type) ); + virtual inline void run_func( void (*func)(obj_type, void*), void* v_arg ); + +}; + +#include "shashmap.tmpl" +#endif diff --git a/yhttpd/src/maps/shashmap.tmpl b/yhttpd/src/maps/shashmap.tmpl new file mode 100644 index 0000000..0bd5796 --- /dev/null +++ b/yhttpd/src/maps/shashmap.tmpl @@ -0,0 +1,157 @@ +template<class obj_type, class key_type_, class hash_type, class alloc_type> +shashmap<obj_type, key_type_, hash_type, alloc_type>::shashmap() +{ + pthread_mutex_init( &mut_shashmap, NULL ); +} + +template<class obj_type, class key_type_, class hash_type, class alloc_type> +shashmap<obj_type, key_type_, hash_type, alloc_type>::~shashmap() +{ + pthread_mutex_destroy( &mut_shashmap ); +} + +template<class obj_type, class key_type_, class hash_type, class alloc_type> +void +shashmap<obj_type, key_type_, hash_type, alloc_type>::add_elem(obj_type t_obj, key_type_ t_key) +{ + pthread_mutex_lock( &mut_shashmap ); + (*this)[t_key] = t_obj; + pthread_mutex_unlock( &mut_shashmap ); +} + +template<class obj_type, class key_type_, class hash_type, class alloc_type> +void +shashmap<obj_type, key_type_, hash_type, alloc_type>::add_elem_insecure(obj_type t_obj, key_type_ t_key) +{ + (*this)[t_key] = t_obj; +} + +template<class obj_type, class key_type_, class hash_type, class alloc_type> +obj_type +shashmap<obj_type, key_type_, hash_type, alloc_type>::get_set_elem(obj_type t_obj, key_type_ t_key) +{ + pthread_mutex_lock( &mut_shashmap ); + obj_type t_ret = hashmap<obj_type, key_type_, hash_type, alloc_type>::get_set_elem(t_obj, t_key); + pthread_mutex_unlock( &mut_shashmap ); + return t_ret; +} + +template<class obj_type, class key_type_, class hash_type, class alloc_type> +obj_type +shashmap<obj_type, key_type_, hash_type, alloc_type>::get_or_callback_set +(obj_type (*func)(void*), void* p_void, key_type_ t_key) +{ + pthread_mutex_lock( &mut_shashmap ); + obj_type t_ret = hashmap<obj_type, key_type_, hash_type, alloc_type>::get_or_callback_set + (func, p_void, t_key); + pthread_mutex_unlock( &mut_shashmap ); + return t_ret; +} + +template<class obj_type, class key_type_, class hash_type, class alloc_type> +void +shashmap<obj_type, key_type_, hash_type, alloc_type>::set_elem(obj_type t_obj, key_type_ t_key) +{ + pthread_mutex_lock( &mut_shashmap ); + (*this)[t_key] = t_obj; + pthread_mutex_unlock( &mut_shashmap ); +} + +template<class obj_type, class key_type_, class hash_type, class alloc_type> +obj_type +shashmap<obj_type, key_type_, hash_type, alloc_type>::get_elem(key_type_ t_key) +{ + pthread_mutex_lock( &mut_shashmap ); + obj_type t_ret = hashmap<obj_type, key_type_, hash_type, alloc_type>::get_elem(t_key); + pthread_mutex_unlock( &mut_shashmap ); + return t_ret; +} + +template<class obj_type, class key_type_, class hash_type, class alloc_type> +void +shashmap<obj_type, key_type_, hash_type, alloc_type>::del_elem(key_type_ t_key) +{ + pthread_mutex_lock( &mut_shashmap ); + hashmap<obj_type, key_type_, hash_type, alloc_type>::erase(t_key); + pthread_mutex_unlock( &mut_shashmap ); +} + +template<class obj_type, class key_type_, class hash_type, class alloc_type> +void +shashmap<obj_type, key_type_, hash_type, alloc_type>::del_elem_insecure(key_type_ t_key) +{ + hashmap<obj_type, key_type_, hash_type, alloc_type>::erase(t_key); +} + +template<class obj_type, class key_type_, class hash_type, class alloc_type> +vector<key_type_>* +shashmap<obj_type, key_type_, hash_type, alloc_type>::get_key_vector() +{ + pthread_mutex_lock( &mut_shashmap ); + vector<key_type_>* p_vec = hashmap<obj_type, key_type_, hash_type, alloc_type>::get_key_vector(); + pthread_mutex_unlock( &mut_shashmap ); + return p_vec; +} + +template<class obj_type, class key_type_, class hash_type, class alloc_type> +void +shashmap<obj_type, key_type_, hash_type, alloc_type>::clear() +{ + pthread_mutex_lock( &mut_shashmap ); + hashmap<obj_type, key_type_, hash_type, alloc_type>::clear(); + pthread_mutex_unlock( &mut_shashmap ); +} + +template<class obj_type, class key_type_, class hash_type, class alloc_type> +int +shashmap<obj_type, key_type_, hash_type, alloc_type>::size() +{ + pthread_mutex_lock( &mut_shashmap ); + int i_size = hashmap<obj_type, key_type_, hash_type, alloc_type>::size(); + pthread_mutex_unlock( &mut_shashmap ); + return i_size; +} + +template<class obj_type, class key_type_, class hash_type, class alloc_type> +bool +shashmap<obj_type, key_type_, hash_type, alloc_type>::exists(key_type_ t_key) +{ + pthread_mutex_lock( &mut_shashmap ); + bool b_ret = hashmap<obj_type, key_type_, hash_type, alloc_type>::exists(t_key); + pthread_mutex_unlock( &mut_shashmap ); + return b_ret; +} + +template<class obj_type, class key_type_, class hash_type, class alloc_type> +void +shashmap<obj_type, key_type_, hash_type, alloc_type>::run_func( void (*func)(obj_type) ) +{ + pthread_mutex_lock( &mut_shashmap ); + hashmap<obj_type, key_type_, hash_type, alloc_type>::run_func(func); + pthread_mutex_unlock( &mut_shashmap ); +} + +template<class obj_type, class key_type_, class hash_type, class alloc_type> +void +shashmap<obj_type, key_type_, hash_type, alloc_type>::run_func( void (*func)(obj_type, void*), void* v_arg ) +{ + pthread_mutex_lock( &mut_shashmap ); + hashmap<obj_type, key_type_, hash_type, alloc_type>::run_func(func, v_arg); + pthread_mutex_unlock( &mut_shashmap ); +} + +template<class obj_type, class key_type_, class hash_type, class alloc_type> +void +shashmap<obj_type, key_type_, hash_type, alloc_type>::dumpit() +{ + dumpable::add + ("[shashmap]"); + vector<key_type_>* p_vec = get_key_vector(); + + typename vector<key_type_>::iterator iter; + for (iter = p_vec->begin(); iter != p_vec->end(); ++iter) + dumpable::add + (*iter); + + delete p_vec; +} diff --git a/yhttpd/src/monitor/dump.cpp b/yhttpd/src/monitor/dump.cpp new file mode 100644 index 0000000..c5f51d0 --- /dev/null +++ b/yhttpd/src/monitor/dump.cpp @@ -0,0 +1,109 @@ +#ifndef DUMP_CPP +#define DUMP_CPP + +#include "dump.h" + +using namespace std; + +const string dumpable::s_sep = "->"; +const int dumpable::i_max_level = 100; + +dumpable::dumpable() +{ + initialize(0); +} + +void +dumpable::initialize(int i_level) +{ + this->i_level = i_level; + this->i_lined = i_level; + this->b_lined = false; + this->b_next_no_nl = false; + this->s_dump = ""; +} + +string +dumpable::dump() +{ + return dump(0); +} + +string +dumpable::dump(int i_level) +{ + initialize(i_level); + dumpit(); + return s_dump; +} + +void +dumpable::add + (string s_line) +{ + if ( i_lined > i_max_level ) + i_lined = i_max_level; + + if (!b_lined) + s_dump.append(s_sep); + + else + for ( int i = 0; i < i_lined; ++i ) + s_dump.append(" "); + + s_dump.append(s_line); + + if (b_next_no_nl) + b_next_no_nl = false; + + else + s_dump.append("\n"); + + if (!b_lined) + { + b_lined = true; + i_lined = i_level + s_sep.length(); + } +} + +int +dumpable::get_level() const +{ + return i_lined; +} + +dump::dump(vector<string> vec_params) +{ + if (vec_params.empty()) + { + cout << CLIPRMO << "all conf sock"; + cout << endl; + return; + } + + cout << run(vec_params); +} + +string +dump::run(vector<string> &vec_params) +{ + string s_ret(""); + string s_part; + + vector<string>::iterator iter; + for (iter = vec_params.begin(); iter != vec_params.end(); ++iter) + { + s_part = *iter; + + if (!s_part.compare("conf") || !s_part.compare("all")) + s_ret.append(wrap::CONF->dump()); + + if (!s_part.compare("sock") || !s_part.compare("all")) + s_ret.append(wrap::SOCK->dump()); + + } + + return s_ret; +} + +#endif diff --git a/yhttpd/src/monitor/dump.h b/yhttpd/src/monitor/dump.h new file mode 100644 index 0000000..ffb3529 --- /dev/null +++ b/yhttpd/src/monitor/dump.h @@ -0,0 +1,62 @@ +#include "../incl.h" + +#ifndef DUMP_H +#define DUMP_H + +using namespace std; + +class dumpable +{ +private: + int i_level; + int i_lined; + bool b_lined; + bool b_next_no_nl; + string s_dump; + + static const string s_sep; + static const int i_max_level; + + virtual void dumpit() = 0; + void initialize(int i_level); + void reset(); + +protected: + void add + (unsigned i_num) + { + add + ("<unsigned>"); + } + + void add + (int i_num) + { + add + ("<int>"); + } + + void add + (string s_line); + dumpable(); + + void next_no_newline() + { + b_next_no_nl = true; + } + +public: + string dump(); + string dump(int i_level); + int get_level() const; +}; + +class dump +{ +private: + string run(vector<string> &vec_params); +public: + dump(vector<string> vec_params); +}; + +#endif diff --git a/yhttpd/src/monitor/stats.cpp b/yhttpd/src/monitor/stats.cpp new file mode 100644 index 0000000..e0aa146 --- /dev/null +++ b/yhttpd/src/monitor/stats.cpp @@ -0,0 +1,102 @@ +#ifndef STATS_CPP +#define STATS_CPP + +#include "stats.h" + +using namespace std; + +stats::stats() +{ + i_rusage_vec_size = tool::string2int( + wrap::CONF->get_elem("httpd.stats.rusagehistory")); + + + pthread_mutex_init( &mut_vec_rusage, NULL ); + +} + +stats::~stats() +{ + pthread_mutex_destroy( &mut_vec_rusage ); +} + +void +stats::update_rusage_history() +{ + wrap::system_message(STATUPR); + + rusage* p_rusage = new rusage; + getrusage( RUSAGE_SELF, p_rusage ); + + map<string,long> map_rusage; + + map_rusage["ru_maxrss"] = p_rusage->ru_maxrss; + map_rusage["ru_ixrss"] = p_rusage->ru_ixrss; + map_rusage["ru_idrss"] = p_rusage->ru_idrss; + map_rusage["ru_isrss"] = p_rusage->ru_isrss; + map_rusage["ru_minflt"] = p_rusage->ru_minflt; + map_rusage["ru_majflt"] = p_rusage->ru_majflt; + map_rusage["ru_nswap"] = p_rusage->ru_nswap; + map_rusage["ru_inblock"] = p_rusage->ru_inblock; + map_rusage["ru_oublock"] = p_rusage->ru_oublock; + map_rusage["ru_msgsnd"] = p_rusage->ru_msgsnd; + map_rusage["ru_msgrcv"] = p_rusage->ru_msgrcv; + map_rusage["ru_nsignals"] = p_rusage->ru_nsignals; + map_rusage["ru_nvcsw"] = p_rusage->ru_nvcsw; + map_rusage["ru_nivcsw"] = p_rusage->ru_nivcsw; + + delete p_rusage; + + pthread_mutex_lock ( &mut_vec_rusage ); + + if ( vec_rusage_history.size() >= i_rusage_vec_size ) + vec_rusage_history.erase( vec_rusage_history.begin() ); + + vec_rusage_history.push_back(map_rusage); + + pthread_mutex_unlock( &mut_vec_rusage ); +} + +void +stats::set_rusage_vec_size( int i_rusage_vec_size ) +{ + pthread_mutex_lock ( &mut_vec_rusage ); + this->i_rusage_vec_size = i_rusage_vec_size; + pthread_mutex_unlock( &mut_vec_rusage ); +} + +long +stats::get_ru_maxrss() +{ + rusage* p_rusage = new rusage; + getrusage( RUSAGE_SELF, p_rusage ); + + long l_ret = p_rusage->ru_maxrss; + delete p_rusage; + + return l_ret; +} + +string +stats::get_rusage_history( string s_type, string s_seperator ) +{ + string s_ret; + int i_count = 0; + vector< map<string,long> >::iterator iter; + + pthread_mutex_lock ( &mut_vec_rusage ); + + for ( iter = vec_rusage_history.begin(); + iter != vec_rusage_history.end(); + iter++, i_count++ ) + s_ret.append(s_seperator + + tool::int2string(i_count) + ". " + iter->find(s_type)->first + " " + + tool::int2string( iter->find(s_type)->second) + "\n"); + + pthread_mutex_unlock( &mut_vec_rusage ); + + return s_ret; +} + + +#endif diff --git a/yhttpd/src/monitor/stats.h b/yhttpd/src/monitor/stats.h new file mode 100644 index 0000000..0478258 --- /dev/null +++ b/yhttpd/src/monitor/stats.h @@ -0,0 +1,36 @@ +#include "../incl.h" + +#ifndef STATS_H +#define STATS_H + +#include "../tool/tool.h" + +#include <stdio.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> + +using namespace std; + +class stats +{ +private: + // Specifies the max. amount of elements in vec_rusage_history; + int i_rusage_vec_size; + // History of the last i_rusage_vec_size rusage values. + vector< map<string,long> > vec_rusage_history; + pthread_mutex_t mut_vec_rusage; + + + void set_rusage_vec_size( int i_rusage_vec_size ); + +public: + stats( ); + ~stats( ); + + void update_rusage_history(); + string get_rusage_history( string s_type, string s_seperator ); + long get_ru_maxrss(); +}; + +#endif diff --git a/yhttpd/src/msgs.h b/yhttpd/src/msgs.h new file mode 100644 index 0000000..fdfb68c --- /dev/null +++ b/yhttpd/src/msgs.h @@ -0,0 +1,145 @@ +#ifndef MSGS_H +#define MSGS_H + +// alphabetical ordered: +#define ACCPERR "Sock: Accept error " +#define BINDERR "Sock: Bind error " +#define HTTPDREP "Chat: Using replacement strings" +#define HTTPDDOP "Chat: Default operator login " +#define HTTPDFLO "Chat: Flooding (" +#define CFILEOK "Parsing config file" +#define CFILEFA "Failed opening config file!" +#define CONTACT "Contact: http://www.yChat.org, Mail@yChat.org, ICQ: 11655527" +#define CLRHTML "HTML: Cleared the document cache " +#define CLIWELC "Command Line Interface (type help for a list of all commands)" +#define CLIPRMO ">> " +#define CLIPRMI "<< " +#define CLIHELP "Unknown command (use help)" +#define CLIMSQL "Spawing system mysql client (enter exit to return)" +#define CLISHEL "Spawing system shell (enter exit to return)" +#define DATAADD "Data: Adding used connection into the queue" +#define DATADIS "Data: Closing all connections" +#define DATADI2 "Data: Closing idle connection (" +#define DATAQUE "Data: " +#define DATAGET "Data: Using database connection queue " +#define DATAIN0 "Data: Initializing maxcon to " +#define DATAIN1 "Data: Initializing mincon to " +#define DATAMAX "Data: Max database connections reached " +#define DATAMA0 "Data: Max database connections " +#define DATANEW "Data: Creating new database connection " +#define DESCRIP "Copyright (C) 2003 Paul C. Buetow, Volker Richter" +#define DESCRI2 "Copyright (C) 2004, 2005 Paul C. Buetow" +#define DONEMSG "done" +#define DOWNMSG "Shutting down " +#define GARBAGE "Garbage: Initializing collector " +#define GARBACT "Garbage collector activated " +#define GAROFFNE "Garbage: No garbage to remove available " +#define GARROOM "Garbage: Added room " +#define GARUSER "Garbage: Added user " +#define GARUSE2 "Garbage: Recycle user " +#define LISTERR "Sock: Listen error " +#define LOGGINI "Logging: Init on " +#define LOGERR1 "Logging: Could not open logfile " +#define LOGERR2 "Logging: No filename specified " +#define LOGINER "Chat: Login failed (password), nick: " +#define LOGINE0 "Chat: Login failed (empty nick)" +#define LOGINE1 "Chat: Login failed (alpha nick), nick: " +#define LOGINE2 "Chat: Login failed (nick length), nick: " +#define LOGINE3 "Chat: Login failed (room length), nick/room: " +#define LOGINE4 "Chat: Login failed (guests disabled), nick: " +#define LOGINE5 "Chat: Login failed (not enough threads), nick: " +#define MODULEC "Modl: Caching " +#define MODULER "Modl: Requesting " +#define MODUNLO "Modl: Unloading all modules " +#define MODRELO "Modl: Reloading all modules " + +#ifdef DATABASE +#define MYSQLQU "MySQL: " +#define MYSQLQ2 "MySQL: Adding query " +#define MYSQLE1 "MySQL: Error running mysql_init " +#endif + +#ifdef CTCSEGV +#define SIGNSEG "Signal: Received SIGSEGV" +#endif + +#define NCURADM "ADMINISTRATOR's MAIN MENU" +#define NCURMSG "SERVER SYSTEM MESSAGES" +#define NCURSE0 "HTTP server: " +#define NCURSE1 "Thread pool: " +#define NCURSE2 "Data stats: " +#define NCURSE3 "Chat stats: " +#define NCURSE4 "Caching: " +#define NEWREQU "Sock: New request " +#define NEWROOM "Chat: New room " +#define NEWUSER "Chat: New user " +#define OFFFOUND "HTML: File not found " +#define PERMSTD "Reading standard command exec permissions" +#define POOLER1 "Pool: Did not allocate all threads (" +#define POOLER2 "Pool: Max pool size reached (" +#define POOLFLL "Pool: Allocating new threads (" +#define READERR "Sock: Read error " +#define REMROOM "Garbage: Removing room " +#define REMUSER "Garbage: Removing user " +#define REQUEST "Reqp: Request string " +#define SELCERR "Sock: Select error " +#define SEPERAT "-----------------------" +#define SESSION "Session: Count " +#define SESSDMP "Session: Dump of session" +#define SESSERR "Session: Could not find session " +#define SESSEXI "Session: New TempID already exists, recalc." +#define SHELLER "Shell: Could not execute command" +#define SHELLEX "Shell: Executing the following command:" +#define SIGSIGV "Signal: SIGV received!" +#define STATUPR "Stats: Updated rusage history" +#define STATRSS "Stats: Max resident set size " +#define REUROOM "Garbage: Reusing room object " +#define SOCKCLN "Sock: Initializing a client socket at " +#define SOCKCON "Sock: Connecting to " +#define SOCKCRT "Sock: Created socket on " +#define SOCKSRV "Sock: Initializing server socket " +#define SOCKERR "Sock: Can't create socket, trying next port " +#define SOCKER1 "Sock: Can't create socket, aborting" +#define SOCKER2 "Sock: Unknown hostname " +#define SOCKRDY "Sock: Server socket is ready " +#define SOCKCAC "Sock: Caching IP " +#define SOCKCA2 "Sock: Cleaning IP cache (" +#define SOCKUNS "Sock: Starting unsecure transport [HTTP]" +#ifdef OPENSSL +#define SSLERR1 "SSL: Can't create socket" +#define SSLERR2 "SSL: Private key does not match cert. file" +#define SSLERR3 "SSL: Can't create new SSL context" +#define SSLERR4 "SSL: Can't create new SSL socket via accept" +#define SOCKSEC "SSL: Starting secure transport [HTTPS]" +#endif +#define TECACHE "HTML: Caching document " +#define THRDSTR "Thread: Running" +#define TIMERAT "Timer: User autoaway timeout " +#define TIMERIN "Timer: Initializing " +#define TIMEROF "Timer: Setting offset to " +#define TIMERTH "Timer: Starting timer thread " +#define TIMERTO "Timer: User logout timeout " +#define TIMERUP "Timer: System uptime " +#define XMLREAD "XML: Reading " +#define XMLERR "XML Error: " +#define XMLER1 "XML Error: Unable to load file " +#define VERSION "0.8" +#define BRANCH "CURRENT" +#define BUILDNR 4003 +#define UNAME "FreeBSD 5.4-DEVEL-p3 i386" +#define COMPOPT "Using built-in specs.; Configured with: FreeBSD/i386 system compiler; Thread model: posix; gcc version 3.4 [FreeBSD] 20040728; 3.4; g++" +#define YCUSAGE "Usage: ./yhttpd {h|v}|{o confkey confvalue}\n" + +#define HEADER1 "HTTP/1.1 200 OK\r\n" +#define HEADER2 "Server: yhttpd/" VERSION "-" BRANCH "\r\n" +#define HEADER3 "Cache-Control: no-cache\r\n" +#define HEADER4 "Pragma: no-cache\r\n" +#define HEADER5 "Transfer-Encoding: chunked\r\n" +#define HEADER6 "Connection: keep-alive\r\n" +#define HEADER7 "Content-Length: "; +#define HEADER8 "Content-Type: "; +#define HEADER8b "; charset=ISO-8859-1\r\n"; +#define HEADER9 "Allow: GET\r\n"; +//#define MEMBERE "Memb: No such member " + +#endif diff --git a/yhttpd/src/name.cpp b/yhttpd/src/name.cpp new file mode 100644 index 0000000..79167f5 --- /dev/null +++ b/yhttpd/src/name.cpp @@ -0,0 +1,49 @@ +#ifndef NAME_CPP +#define NAME_CPP + +#include "name.h" +#include "tool/tool.h" + +using namespace std; + +name::name() +{ + pthread_mutex_init( &mut_s_name, NULL); +} + +name::name( string s_name ) +{ + pthread_mutex_init( &mut_s_name, NULL); + set_name( s_name ); +} + +name::~name() +{ + pthread_mutex_destroy( &mut_s_name ); +} + +string +name::get_name() +{ + string s_ret; + pthread_mutex_lock ( &mut_s_name ); + s_ret = s_name; + pthread_mutex_unlock( &mut_s_name ); + return s_ret; +} + +string +name::get_lowercase_name() +{ + return tool::to_lower( get_name() ); +} + +void +name::set_name( string s_name ) +{ + pthread_mutex_lock ( &mut_s_name ); + this->s_name = s_name; + pthread_mutex_unlock( &mut_s_name ); +} + +#endif diff --git a/yhttpd/src/name.h b/yhttpd/src/name.h new file mode 100644 index 0000000..0a62c1f --- /dev/null +++ b/yhttpd/src/name.h @@ -0,0 +1,24 @@ +#include "incl.h" + +#ifndef NAME_H +#define NAME_H + +using namespace std; + +class name +{ +protected: + string s_name; // object's name. + pthread_mutex_t mut_s_name; + +public: + virtual string get_name ( ); + virtual string get_lowercase_name ( ); + virtual void set_name ( string s_name ); + + name(); + name( string s_name ); // a standard constructor. + ~name(); +}; + +#endif diff --git a/yhttpd/src/ncur/menu.cpp b/yhttpd/src/ncur/menu.cpp new file mode 100644 index 0000000..dcacc9b --- /dev/null +++ b/yhttpd/src/ncur/menu.cpp @@ -0,0 +1,117 @@ + +#include "menu.h" + +#ifdef NCURSES + +#ifndef MENU_CPP +#define MENU_CPP + +using namespace std; + +menu::menu( int i_startx, int i_starty, int i_width, int i_height, char *c_header, char **choices, int i_numchoices, const chtype ch ) +{ + this->i_startx = i_startx; + this->i_starty = i_starty; + this->i_height = i_height; + this->i_width = i_width; + this->c_header = c_header; + this->choices = choices; + this->i_numchoices = i_numchoices; + + initialize( ch ); +} + +menu::~menu() +{ + /* + wborder(win, ' ', ' ', ' ',' ',' ',' ',' ',' '); + wrefresh(win); + delwin(win); + */ +} + +void +menu::initialize( const chtype ch ) +{ + this->i_highlight = 1; + this->i_choice = 0; + + win = newwin( i_height, i_width, i_starty, i_startx ); + wbkgd(win, ch); +} + +void +menu::display() +{ + int x, y, i; + + x = 2; + y = 2; + + box( win, 0, 0 ); + mvwprintw( win, y++, x, "%s", c_header ); + + for( i = 0; i < i_numchoices; i++ ) + { + ++y; + + if( i_highlight == i+1 ) // Highlight the current selection. + { + wattron( win, A_REVERSE); + mvwprintw( win, y, x, "%d. %s", i, choices[i]); + wattroff( win, A_REVERSE); + } else + { + mvwprintw( win, y, x, "%d. %s", i, choices[i]); + } + } + + wrefresh( win ); +} + +void +menu::start( void (*swich_case_menu_action)(int) ) +{ + refresh(); + bool b_flag = 1; + + while( b_flag ) + { + keypad(win, 1); + display(); + c = wgetch( win ); + + switch(c) + { + case KEY_UP: + if( i_highlight == 1 ) + i_highlight = i_numchoices; + else + --i_highlight; + break; + + case KEY_DOWN: + if( i_highlight == i_numchoices ) + i_highlight = 1; + else + ++i_highlight; + break; + + case 10: + i_choice = i_highlight; + break; + + default: + mvprintw( NCUR_MENU_CHAR_X, NCUR_MENU_CHAR_Y, "%3d %c ", c, c); + refresh(); + break; + } + + // Menu action. + ( *swich_case_menu_action ) ( i_choice ); + i_choice = 0; + } +} + +#endif +#endif diff --git a/yhttpd/src/ncur/menu.h b/yhttpd/src/ncur/menu.h new file mode 100644 index 0000000..af7dbe9 --- /dev/null +++ b/yhttpd/src/ncur/menu.h @@ -0,0 +1,39 @@ +#include "../incl.h" + +#ifdef NCURSES + +#ifndef MENU_H +#define MENU_H + +#include <ncurses.h> + +using namespace std; + +class menu +{ +private: + char **choices; + char *c_header; + + int i_startx, i_starty, i_width, i_height, i_highlight, i_choice, + i_numchoices, c; + + WINDOW *win; + + void initialize( const chtype ch ); + +public: + explicit menu( int i_startx, int i_starty, int i_width, int i_height, char *c_header, char **choices, int i_numchoices, const chtype ch ); + ~menu( ); + + void display(); + void start( void (*swich_case_menu_action)(int) ); + + void activate_menu_win() + { + keypad(win, 1); + } +}; + +#endif +#endif diff --git a/yhttpd/src/ncur/ncur.cpp b/yhttpd/src/ncur/ncur.cpp new file mode 100644 index 0000000..41bfdac --- /dev/null +++ b/yhttpd/src/ncur/ncur.cpp @@ -0,0 +1,259 @@ +#ifndef NCUR_CPP +#define NCUR_CPP + +#include "ncur.h" + +#ifdef CLI +#include "../cli/cli.h" +#endif +#include "../sign.h" + +using namespace std; + +#ifdef NCURSES + +const string GMAKE_PARAMS[] = + { "clean_base", "clean_modules", "all" + }; +const int GMAKE_ELEMENTS = 3; + +ncur::ncur( ) +{ + p_messagelist = new list<char*>; + pthread_mutex_init( &mut_messages, NULL ); + pthread_mutex_init( &mut_is_ready, NULL ); + i_message_length = 45; + b_is_ready = false; +} + +ncur::~ncur() +{ + pthread_mutex_destroy( &mut_messages ); + pthread_mutex_destroy( &mut_is_ready ); +} + +void +ncur::start( void *p_void ) +{ + ncur::init_ncurses(); + + char *choices[] = { + " ", + " ", + "Clear template cache ", + " ", + "Show max res. set size ", + "Compile changed sources ", + "Recompile all sources ", + "Show source stats ", + "Command line interface ", + " ", + "Shut down server" + }; + + p_serveroutput = newwin( 19, 49, 1, 31 ); + wbkgd(p_serveroutput, COLOR_PAIR(1)); + + box ( p_serveroutput, 0, 0 ); + mvwprintw( p_serveroutput, 2, 2, NCURMSG ); + wrefresh ( p_serveroutput ); + + print( string("yhttpd ") + VERSION ); + + + p_menu = new menu( 1, 1, 30, 19, NCURADM, choices, 11, COLOR_PAIR(1)); + + mvprintw(NCUR_SERVER_HEADER_X,NCUR_SERVER_HEADER_Y, NCURSE0); + mvprintw(NCUR_POOL_HEADER_X,NCUR_POOL_HEADER_Y, NCURSE1); + mvprintw(NCUR_CACHED_HEADER_X,NCUR_CACHED_HEADER_Y, NCURSE4); + + wrap::HTML->print_cached(0); + + is_ready(true); + wrap::SOCK->print_server_port(); + + p_menu->start( &switch_main_menu_ ); + + shutdown(); +} + +void +ncur::shutdown() +{ + ncur::close_ncurses(); +} + + +void +ncur::print( string *p_msg ) +{ + print( *p_msg ); +} + +void +ncur::print( string s_msg ) +{ + print( (char*)s_msg.c_str() ); +} + +void +ncur::print( char* c_print ) +{ + // Removing \n + if ( strlen(c_print) > i_message_length ) + { + string s_tmp(c_print); + print( s_tmp.substr( 0, i_message_length ) ); + print( s_tmp.substr( i_message_length, s_tmp.length()-i_message_length ) ); + return; + } + + int i; + char* c_temp = new char[i_message_length]; + memcpy( c_temp, c_print, strlen(c_print) ); + + for ( i = strlen(c_print); i < i_message_length; ++i ) + c_temp[i] = ' '; + + c_temp[i] = '\0'; + + pthread_mutex_lock( &mut_messages ); + + if ( p_messagelist->size() > 12 ) + { + char* c_front = p_messagelist->front(); + p_messagelist->pop_front(); + free(c_front); + } + + p_messagelist->push_back( c_temp ); + + + if ( is_ready() ) + { + list<char*>::iterator iter; + iter = p_messagelist->begin(); + + for ( i = 4; i < 18 && iter != p_messagelist->end(); ++i, ++iter ) + mvwprintw( p_serveroutput, i, 2, *iter ); + + wrefresh ( p_serveroutput ); + } + + pthread_mutex_unlock( &mut_messages ); +} + +void +ncur::switch_main_menu_( int i_choice ) +{ + int i; + + if( i_choice != 0 ) + switch ( i_choice ) + { + case 3: + wrap::HTML->clear_cache(); + mvprintw( 20,2, "Cleared the template cache "); + refresh(); + break; + case 4: + refresh(); + break; + case 5: + mvprintw( 20,2, "Showing max resident set size in memory "); + wrap::NCUR->print( STATRSS + string("(") + tool::int2string( + wrap::STAT->get_ru_maxrss()) + string(")")); + break; + case 6: + tool::shell_command( string(GMAKE), METH_NCURSES); + break; + case 7: + for ( i = 0; i < GMAKE_ELEMENTS; i++ ) + tool::shell_command( GMAKE + GMAKE_PARAMS[i], METH_NCURSES); + break; + case 8: + tool::shell_command( string(GMAKE) + " stats", METH_NCURSES); + break; + case 9: +#ifdef CLI + + wrap::NCUR->is_ready(false); + refresh(); /* Print it on to the real screen */ + + def_prog_mode(); /* Save the tty modes */ + endwin(); /* End curses mode temporarily */ + delete new cli(); /* Start CLI mode */ + reset_prog_mode(); /* Return to the previous tty mode*/ + /* stored by def_prog_mode() */ + refresh(); /* Do refresh() to restore the */ + /* Screen contents */ + wrap::NCUR->is_ready(true); + wrap::NCUR->activate_menu_win(); +#else + + mvprintw( 20,2, "CLI mode has not been compiled in! "); +#endif + + break; + case 10: +#ifdef DATABASE + +#endif + + break; + + case 11: // Shut down server + sign::terminate_received(0); + break; + + default: + mvprintw( 20,2, "Selection # %d not yet implemented!", i_choice-1); + wrap::NCUR->print( "Selection not yet implemented!" ); + refresh(); + break; + } +} + +void +ncur::init_ncurses() +{ + initscr(); + start_color(); + clear(); + noecho(); + cbreak(); // Line buffering disabled. pass on everything + init_pair(1, COLOR_BLACK, COLOR_CYAN); + mvprintw( 0,2, (char*)(tool::yhttpd_version()).c_str()); + curs_set(0); + refresh(); +} + +void +ncur::close_ncurses() +{ + refresh(); + clrtoeol(); + refresh(); + endwin(); +} + +void +ncur::is_ready( bool b_is_ready ) +{ + pthread_mutex_lock( &mut_is_ready ); + this->b_is_ready = b_is_ready; + pthread_mutex_unlock( &mut_is_ready ); +} + +bool +ncur::is_ready() +{ + bool b_ret; + pthread_mutex_lock( &mut_is_ready ); + b_ret = b_is_ready; + pthread_mutex_unlock( &mut_is_ready ); + return b_ret; +} + +#endif +#endif diff --git a/yhttpd/src/ncur/ncur.h b/yhttpd/src/ncur/ncur.h new file mode 100644 index 0000000..1830cdf --- /dev/null +++ b/yhttpd/src/ncur/ncur.h @@ -0,0 +1,51 @@ +#include "../incl.h" + +#ifdef NCURSES + +#ifndef NCUR_H +#define NCUR_H + +#include <ncurses.h> +#include <list> + +#include "menu.h" +#include "../thrd/thro.h" + +using namespace std; + +class ncur : public thro +{ +private: + friend class sign; + menu* p_menu; + WINDOW* p_serveroutput; + list<char*>* p_messagelist; // contains the messages for p_serveroutput! + int i_message_length; // the maximum length of a system message! + bool b_is_ready; // is set to TRUE if the admin interface is initialized. + static void init_ncurses(); + static void close_ncurses(); + + pthread_mutex_t mut_messages; + pthread_mutex_t mut_is_ready; + +public: + ncur(); + ~ncur(); + + void start( void *p_void ); + void print( char* c_print ); + void print( string s_msg ); + void print( string* p_msg ); + void is_ready( bool b_is_ready ); + bool is_ready(); + static void switch_main_menu_( int i_choice ); + void shutdown(); + + void activate_menu_win() + { + p_menu->activate_menu_win(); + } +}; + +#endif +#endif diff --git a/yhttpd/src/reqp.cpp b/yhttpd/src/reqp.cpp new file mode 100644 index 0000000..df9de5d --- /dev/null +++ b/yhttpd/src/reqp.cpp @@ -0,0 +1,285 @@ +#ifndef REQP_CPP +#define REQP_CPP + +#include "reqp.h" +#include "tool/tool.h" + +using namespace std; + +#define HEADER HEADER1 HEADER2 HEADER3 HEADER4 HEADER9 +#define STREAM HEADER5 HEADER6 + +const string reqp::s_http = HEADER; +const string reqp::s_http_stream = STREAM; +const string reqp::s_http_colength = HEADER7; +const string reqp::s_http_cotype = HEADER8; +const string reqp::s_http_cotype_add = HEADER8b; + +reqp::reqp( ) +{} + +void +reqp::get_request_parameters( string s_parameters, map<string,string>& map_params ) +{ + string s_tmp; + unsigned i_pos, i_pos2; + + while( (i_pos = s_parameters.find("&")) != string::npos ) + { + s_tmp = s_parameters.substr(0, i_pos ); + + if ( (i_pos2 = s_tmp.find("=")) != string::npos ) + map_params[ s_tmp.substr(0, i_pos2) ] = tool::replace( s_tmp.substr( i_pos2+1 ), "\\AND", "&"); + + s_parameters = s_parameters.substr( i_pos + 1 ); + } + + // Get the last request parameter, which does not have a "&" on the end! + if( (i_pos = s_parameters.find("=")) != string::npos ) + map_params[ s_parameters.substr(0, i_pos) ] = s_parameters.substr( i_pos+1 ); + + //map<string,string>::iterator iter; + //for ( iter = map_params.begin(); iter != map_params.end(); ++iter ) + //cout << ">>>" << iter->first << "=" << iter->second << endl; +} + +string +reqp::get_url( string s_req, map<string, string> &map_params, int& i_postpayloadoffset ) +{ + unsigned i_pos, i_pos2; + string s_vars( "" ); + string s_ret; + int i_req; + + // GET request + if ( s_req.find("GET") != string::npos) + { + // Be sure that the GET request has minimum length + if ( s_req.length() > 5 ) + { + // Get rid of "GET /" + if ( (i_pos = s_req.find("\n")) == string::npos ) + i_pos = s_req.length() - 1; + + s_req = s_req.substr(5, i_pos - 5); + + // Get HTML site to be displayed + if ( (i_pos = s_req.find("?")) == string::npos ) + { + if ( (i_pos2 = s_req.find(" HTTP")) != string::npos ) + s_ret = url_decode( s_req.substr(0, i_pos2)); + } + else + { + s_ret = url_decode( s_req.substr(0, i_pos) ); + + // Get request parameters: + if ( (i_pos2 = s_req.find(" HTTP")) != string::npos ) + { + s_req = url_decode( s_req.substr(i_pos + 1, i_pos2 - i_pos - 1) ); + get_request_parameters( s_req, map_params ); + } + } + + } + } + + // POST request + else + { + if ( (i_pos2 = s_req.find("HTTP")) != string::npos ) + { + if (i_pos2 > 13) + { + s_ret = url_decode( s_req.substr(6,i_pos2-7) ); + + //wrap::system_message(s_req); + //wrap::system_message(string("data offset=") + tool::int2string(i_postpayloadoffset)); + i_pos = s_req.find("event=",i_postpayloadoffset ); + if(i_pos != string::npos) + { + get_request_parameters( url_decode( s_req.substr(i_pos) ), map_params); + } + } + } + + } + +#ifdef VERBOSE + wrap::system_message( REQUEST + s_ret ); +#endif + + if ( s_ret.empty() ) + s_ret = wrap::CONF->get_elem( "httpd.startsite" ); + + else + s_ret = remove_dots(s_ret); + + map_params["request"] = s_ret; + + return s_ret; +} + +string +reqp::get_content_type(string &s_file) +{ + string s_ext(tool::get_extension( s_file )); + + if( s_ext == "" ) + s_ext = "default"; + + return wrap::CONF->get_elem( "httpd.contenttypes." + s_ext ); +} + +void +reqp::parse_headers( string s_req, map<string,string> &map_params ) +{ + int pos = s_req.find("\n"); + + if (pos != string::npos) + { + map_params["QUERY_STRING"] = tool::trim(s_req.substr(0,pos-1)); + + int pos2; + do + { + string s_line( s_req.substr(0, pos) ); + pos2 = s_line.find(":"); + + if (pos2 != string::npos && s_line.length() > pos2+1) + map_params[ tool::trim(s_line.substr(0, pos2)) ] = tool::trim(s_line.substr(pos2+1)); + + s_req = s_req.substr( s_line.size() + 1 ); + pos = s_req.find("\n"); + } + while( pos != string::npos); + } // if +} + +int +reqp::htoi(string *p_str) +{ + int value, c; + c = p_str->at(0); + + if( isupper(c) ) + c = tolower(c); + + value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16; + + c = p_str->at(1); + + if( isupper(c) ) + c = tolower(c); + + value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10; + + return value; +} + +string +reqp::url_decode( string s_url ) +{ + string s_dest = ""; + int i_len = s_url.size(); + int i_prv = i_len - 2; + + char c; + for( int i = 0; i < i_len; ++i) + { + c = s_url.at(i); + if( c == '+' ) + { + s_dest += " "; + } + else if (c == '%' && i < i_prv) + { + string s_tmp = s_url.substr(i+1, 2); + c = (char) htoi(&s_tmp); + s_dest += c; + i += 2; + } + else + { + s_dest += c; + } + } + + return s_dest; +} + +string +reqp::get_from_header( string s_req, string s_hdr ) +{ + unsigned i_pos[2]; + if ( (i_pos[0] = s_req.find( s_hdr, 0 )) == string::npos ) + return ""; + + if ( (i_pos[1] = s_req.find( "\n", i_pos[0]) ) == string::npos ) + return ""; + + unsigned i_len = s_hdr.length(); + return s_req.substr( i_pos[0] + i_len, i_pos[1] - i_pos[0] - i_len - 1 ); +} + +string +reqp::parse( socketcontainer *p_sock, string s_req, map<string,string> &map_params, int &i_postpayloadoffset ) +{ + + // store all request informations in map_params. store the url in + // map_params["request"]. + get_url( s_req, map_params, i_postpayloadoffset ); + + parse_headers( s_req, map_params ); + string s_event( map_params["event"] ); + + map_params["content-type"] = get_content_type( map_params["request"] ); + + string s_rep( "" ); + + + if ( wrap::CONF->get_elem("httpd.enablecgi").compare("true") == 0 && + string::npos != map_params["request"].find(".cgi") ) + { + s_rep.append( tool::shell_command( + wrap::CONF->get_elem("httpd.templatedir") + map_params["request"], + METH_RETSTRING ) ); + } + else + { + // parse and get the requested html-template and also use + // the values stored in map_params for %%KEY%% substituations. + s_rep.append( wrap::HTML->parse( map_params ) ); + } + + // create the http header. + + string s_resp(s_http); + if ( s_event.compare("stream") == 0 ) + s_resp.append( s_http_stream ); + + s_resp.append( s_http_colength + tool::int2string(s_rep.size()) + "\r\n" + + s_http_cotype + map_params["content-type"] + + s_http_cotype_add + "\r\n" ); + + s_resp.append(s_rep); + + + // return the parsed html-template. + return s_resp; +} + + +string +reqp::remove_dots( string s_ret ) +{ + // remove ".." from the request. + unsigned i_pos; + + if ( (i_pos = s_ret.find( ".." )) != string::npos ) + return remove_dots(s_ret.substr(0, i_pos)); + + return s_ret; +} + +#endif diff --git a/yhttpd/src/reqp.h b/yhttpd/src/reqp.h new file mode 100644 index 0000000..93e32e4 --- /dev/null +++ b/yhttpd/src/reqp.h @@ -0,0 +1,41 @@ +#include "incl.h" +#ifndef REQP_H +#define REQP_H + +#include "maps/hashmap.h" + +using namespace std; + +class reqp +{ +private: + static const string s_http; + static const string s_http_stream; + static const string s_http_colength; + static const string s_http_cotype; + static const string s_http_cotype_add; + + // returns the request url from thr client's http request header + // until the first "?" and stores all request parameter values + // ( behind "?" ) into map_params. + string get_url( string s_req, map<string,string> &map_params, int& i_postpayloadoffset ); + // returns a specific value of the client's http request header. + // ( s.t. like the User-Agent, Referer etc... ). + string get_from_header( string s_req, string s_hdr ); + + int htoi( string *p_str ); + // Removes double dots ".." + string remove_dots( string s_req ); + + // Parses "event=bla?blu=bli&sadasda=asddds ..." string and stores them in the map + void get_request_parameters( string s_parameters, map<string,string>& map_params ); + +public: + reqp( ); + string parse( socketcontainer* p_sock, string s_req, map<string,string> &map_params, int &i_postpayloadoffset ); + string url_decode ( string s_url ); + string get_content_type( string& s_file ); + void parse_headers( string s_req, map<string,string> &map_params ); +}; + +#endif diff --git a/yhttpd/src/sign.cpp b/yhttpd/src/sign.cpp new file mode 100644 index 0000000..efb28d7 --- /dev/null +++ b/yhttpd/src/sign.cpp @@ -0,0 +1,54 @@ +#ifndef SIGN_CPP +#define SIGN_CPP + +#include "sign.h" + +void +sign::clean_template_cache(int i_param) +{ + wrap::HTML->clear_cache(); +} + + +#ifdef CTCSEGV +void +sign::sigsev_received(int i_param) +{ + wrap::system_message(SIGNSEG); +} +#endif + +void +sign::terminate_received(int i_param) +{ + +#ifdef NCURSES + + mvprintw( 21,2, "Good bye !"); + wrap::NCUR->close_ncurses(); + +#endif + + exit(0); +} + +void +sign::init_signal_handlers() +{ + // Ignore SIGPIPE. otherwise the server will shut down with "Broken pipe" if + // a client unexpected disconnects himself from a SOCK_STREAM. + signal(SIGPIPE, SIG_IGN); + + signal(SIGUSR1, clean_template_cache); +#ifdef CTCSEGV + + signal(SIGSEGV, sigsev_received); +#endif + + signal(SIGHUP, terminate_received); + signal(SIGINT, terminate_received); + signal(SIGTERM, terminate_received); + //signal(SIGWINCH, ); +} + +#endif diff --git a/yhttpd/src/sign.h b/yhttpd/src/sign.h new file mode 100644 index 0000000..a8ec1e1 --- /dev/null +++ b/yhttpd/src/sign.h @@ -0,0 +1,21 @@ +#ifndef SIGN_H +#define SIGN_H + +#include "incl.h" +#include <signal.h> + +class sign +{ +private: + static void clean_template_cache(int i_param); +#ifdef CTCSEGV + + static void sigsev_received(int i_param); +#endif + +public: + static void init_signal_handlers(); + static void terminate_received(int i_param); +}; + +#endif diff --git a/yhttpd/src/sock/sock.cpp b/yhttpd/src/sock/sock.cpp new file mode 100644 index 0000000..5ae0be1 --- /dev/null +++ b/yhttpd/src/sock/sock.cpp @@ -0,0 +1,473 @@ +#ifndef SOCK_CPP +#define SOCK_CPP + +#include <arpa/inet.h> +#include <errno.h> +#include <sys/types.h> +#include <unistd.h> + +#include "sock.h" + +using namespace std; + +sock::sock() +{ + this->b_run = true; + this->i_req = 0; + this->req_parser = new reqp(); +#ifdef LOGGING + + this->log_daemon = new logd( wrap::CONF->get_elem( "httpd.logging.accessfile" ), + + wrap::CONF->get_elem( "httpd.logging.access_lines" ) ); +#endif +} + +int +sock::_send(socketcontainer *p_sock, const char *sz, int len) +{ + + return send( p_sock->i_sock, sz, len, 0 ); +} + +int +sock::_read(socketcontainer *p_sock, char *sz, int len) +{ + + return read( p_sock->i_sock, sz, len ); +} + +int +sock::_close(socketcontainer *p_sock) +{ + shutdown( p_sock->i_sock, 2 ); + close ( p_sock->i_sock ); + delete p_sock; +} + + +int +sock::_make_server_socket( int i_port ) +{ + size_t i_sock; + struct sockaddr_in name; + + // create the server socket. + i_sock = socket (PF_INET, SOCK_STREAM, 0); + if (i_sock < 0) + { + wrap::system_message( SOCKERR ); + + if ( ++i_port > MAXPORT ) + exit(1); + + wrap::system_message( SOCKERR ); + + return _make_server_socket( i_port ); + } + + // give the server socket a name. + name.sin_family = AF_INET; + name.sin_port = htons(i_port); + name.sin_addr.s_addr = htonl(INADDR_ANY); + int i_optval = 1; + + setsockopt( i_sock, SOL_SOCKET, SO_REUSEADDR, (char*)&i_optval, sizeof(int) ); + + if ( bind(i_sock, (struct sockaddr *) &name, sizeof (name)) < 0 ) + { + + wrap::system_message( BINDERR ); + + if ( ++i_port > MAXPORT ) + exit(1); + + wrap::system_message( string(SOCKERR) + tool::int2string(i_port) ); + + // Rerun recursive. + return _make_server_socket( i_port ); + } + + wrap::system_message( SOCKCRT + string("localhost:") + tool::int2string(i_port) ); + + i_server_port = i_port; + i_server_sock = i_sock; + + return i_sock; +} + +string +sock::read_http_line(socketcontainer *p_sock) +{ + string s_line; + int i_total = 0; + int i_read = 0; + char ch; + + do + { + i_read = _read(p_sock, &ch, sizeof(ch)); + + if(i_read <= 0) + return ""; + + s_line += ch; + i_total++; + } + while((ch != '\n') && i_total < MAXLENGTH); + + if(ch != '\n') + /* + ** the games people play + */ + return ""; + + return s_line; +} +int +sock::read_http(socketcontainer *p_sock, char *c_zbuf, int i_buflen, int &i_postpayloadoffset) +{ + /* + ** 1) Read the first line + ** 2) If GET, handle as such + ** 3) If POST, handle as such + */ + char ch; + int i_read; + int i_ret = -1; + int x,z; + + string s_content_length; + string s_cl; + string s_post_return; + string s_line = read_http_line(p_sock); + + i_postpayloadoffset = 0; + if(s_line.length() <= 0) + return -1; + + /* + ** GET yada\r\n Followed by stuff we don't care about :) heh. + ** 01234 + */ + /* + ** POST yada\r\n + ** xxxxx + ** Content-Length: NNN\n + ** \n + */ + if(s_line.substr(0,3) == "GET") + { + if(s_line.length() > i_buflen) + { + /* + ** Buffer overflow + */ + return -1; + } + else + { + memcpy(c_zbuf,s_line.c_str(),s_line.length()); + return s_line.length(); + } + } + + else + { + /* + ** POST yada + ** 01234 + */ + if(s_line.substr(0,4) != "POST") + return -1; + + /* + ** Get us to the Content-Length: + */ + s_post_return += s_line; + i_postpayloadoffset += s_line.length(); + + for(x=0 ;x < MAXLINES; x++) + { + s_line = read_http_line(p_sock); + s_post_return += s_line; + i_postpayloadoffset += s_line.length(); + + if (s_line.compare(0, 15, "Content-Length:")) + continue; + + // Match found on Content-Length:... process, and then break out and get us to the promised land + s_content_length = s_line.substr( 16 /*strlen("Content-Length: ")*/, + s_line.length() - 16 /*strlen("Content-Length: ")*/); + + /* + ** Content-Length: 333\n + ** 0123456789abcdefghijklmnopqrstuvwxyzAB + */ + + z = 0; + + do + { + ch = s_content_length[z]; + if(isdigit(ch)) + s_cl += ch; + + z++; + + } + while(ch != '\n'); + + break; + } + + if(s_cl.length() <= 0) + return -1; + + z = atoi(s_cl.c_str()); + + /* + ** If we are going to overflow the buffer just by the payload, leave + ** of, if z did not convert correctly. (should have been ok by isdigit) + */ + if(z > i_buflen || z < 0) + return -1; + + /* + ** We have MAXLINES to get to the blank line separating POST data. + */ + for(x=0 ;x < MAXLINES; x++) + { + s_line = read_http_line(p_sock); + s_post_return += s_line; + + i_postpayloadoffset += s_line.length(); + if(s_line == "\r\n") + break; + } + + /* + ** funny business + */ + if(x == MAXLINES) + return -1; + + for(x=0; x < z; x++) + { + if(_read(p_sock,&ch,sizeof(ch)) != 1) + return -1; + + s_post_return += ch; + } + + if(s_post_return.length() > i_buflen) + return -1; + + memcpy(c_zbuf,s_post_return.c_str(),s_post_return.length()); + return s_post_return.length(); + } +} + +int +sock::read_write(socketcontainer* p_sock) +{ + int i_postpayloadoffset; + int i_sock = p_sock->i_sock; + + char c_req[READSOCK]; + + memset(c_req,0,sizeof(c_req)); + + int i_bytes = read_http(p_sock, c_req, READSOCK-1,i_postpayloadoffset); + + if (i_bytes <= 0) + { + wrap::system_message( READERR ); + } + + else + { + // stores the request params. + map<string,string> map_params; + + // get the s_rep ( s_html response which will be send imediatly to the client + struct sockaddr_in client; + size_t size = sizeof(client); + + getpeername(i_sock, (struct sockaddr *)&client, &size); + + uint32_t &s_addr = client.sin_addr.s_addr; + if ( (map_params["REMOTE_ADDR"] = get_elem(s_addr)) == "" ) + { + map_params["REMOTE_ADDR"] = string(inet_ntoa(client.sin_addr)); + set_elem(map_params["REMOTE_ADDR"], s_addr); + wrap::system_message(SOCKCAC+map_params["REMOTE_ADDR"]); + } + + string s_rep = req_parser->parse(p_sock, string(c_req), map_params, i_postpayloadoffset); + +#ifdef LOGGING + + log_daemon->log_access(map_params); +#endif + + // send s_rep to the client. + _send(p_sock, s_rep.c_str(), s_rep.size()); + + // dont need those vals anymore. + map_params.clear(); + + _close(p_sock); + return 0; + } + + _close(p_sock); + return 1; +} + +void +sock::_main_loop_init() +{ + wrap::system_message(SOCKUNS); +} + +#ifdef OPENSSL +// This method is virtual, and is overloaded by sslsock! +bool +sock::_main_loop_do_ssl_stuff(int &i_new_sock) +{ + return 0; +} +#endif + +socketcontainer* +sock::_create_container(int &i_sock) +{ + socketcontainer* p_sock = new socketcontainer; + p_sock->i_sock = i_sock; + return p_sock; +} + +int +sock::start() +{ + wrap::system_message( SOCKSRV ); + pool* p_pool = wrap::POOL; + int i_sock = i_server_sock; + +#ifdef NCURSES + + print_hits(); + p_pool->print_pool_size(); +#endif + + int i_port = tool::string2int( wrap::CONF->get_elem( "httpd.serverport" ) ); + _main_loop_init(); + + int i; + fd_set active_fd_set, read_fd_set; + struct sockaddr_in clientname; + size_t size; + + if (listen (i_sock, 1) < 0) + { + wrap::system_message( LISTERR ); + exit( EXIT_FAILURE ); + } + + wrap::system_message( SOCKRDY ); + + // initialize the set of active sockets. + FD_ZERO (&active_fd_set); + FD_SET (i_sock, &active_fd_set); + + while( b_run ) + { + // block until input arrives on one or more active sockets. + read_fd_set = active_fd_set; + if (select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0) + { + wrap::system_message( SELCERR ); + + exit( EXIT_FAILURE ); + } + + // service all the sockets with input pending. + for ( i = 0; i < FD_SETSIZE; i++ ) + if ( FD_ISSET (i, &read_fd_set) ) + { + if ( i == i_sock ) + { + // connection request on original socket. + ++i_req; + +#ifdef NCURSES + + print_hits(); +#endif + + int i_new_sock; + size = sizeof(clientname); + i_new_sock = accept (i_sock, (struct sockaddr *) &clientname, &size); + +#ifdef OPENSSL + + if (_main_loop_do_ssl_stuff(i_new_sock)) + continue; +#endif + +#ifdef VERBOSE + + wrap::system_message(NEWREQU + + tool::int2string(i_req) + " " + + string(inet_ntoa( clientname.sin_addr )) + ":" + + tool::int2string(ntohs ( clientname.sin_port )) + ); +#endif + + FD_SET (i_new_sock, &active_fd_set); + + } + else + { + socketcontainer *p_sock = _create_container(i); + p_pool->run( (void*) p_sock ); + FD_CLR( i, &active_fd_set ); + } + } + } +} + +void +sock::clean_ipcache() +{ + int i_ipcachesize = wrap::CONF->get_int("httpd.ipcachesize"); + int i_currentsize = size(); + + if ( i_currentsize > 0 && (i_ipcachesize == 0 || i_ipcachesize <= i_currentsize) ) + { + wrap::system_message( + SOCKCA2+tool::int2string(i_currentsize)+","+tool::int2string(i_ipcachesize)+")"); + clear(); + } +} + +#ifdef NCURSES +void +sock::print_server_port() { + mvprintw( NCUR_PORT_X,NCUR_PORT_Y, "Port: %d ", i_server_port); + refresh(); +} + +void +sock::print_hits() +{ + if ( wrap::NCUR->is_ready() ) + { + mvprintw( NCUR_HITS_X,NCUR_HITS_Y, "Hits: %d ", i_req); + refresh(); + } +} +#endif + +#endif diff --git a/yhttpd/src/sock/sock.h b/yhttpd/src/sock/sock.h new file mode 100644 index 0000000..72944e2 --- /dev/null +++ b/yhttpd/src/sock/sock.h @@ -0,0 +1,91 @@ +#include "../incl.h" + +#ifndef SOCK_H +#define SOCK_H + +#include <queue> +#include <stdio.h> +#include <stdlib.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> + +#include "../reqp.h" + +#include "../thrd/pool.h" +#include "../maps/shashmap.h" + +#ifdef LOGGING +#include "../logd.h" +#endif + +using namespace std; + +class sock : public shashmap + < string, uint32_t, self_hash<uint32_t>, equals_allocator<uint32_t> > +{ +protected: +#ifdef LOGGING + + logd *log_daemon; // the log daemon +#endif + + int i_server_sock; + int i_server_port; + + // total number of server requests. + unsigned long long i_req; + bool b_run; // true while socket manager is running. + reqp *req_parser; // parses the http requests from clients. + char *c_buffer; // char buffer! + pthread_mutex_t mut_hits; + + static string inet_ntoa_callback(void* p_void); + +public: + // creates a server socket. + int read_http(socketcontainer *p_sock, char *c_zbuf, int i_buflen, int &i_payloadoffset); + string read_http_line(socketcontainer *p_sock); + + // small inline methods: + bool get_server_() const + { + return b_run; + } + // small inline methods: + bool get_run() const + { + return b_run; + } + bool set_run( bool b_run ) + { + this->b_run = b_run; + } + + sock(); + + int read_write( socketcontainer* p_sock ); + + int start(); + void clean_ipcache(); + + // the chat stream there all the chat messages will sent through. + virtual int _send(socketcontainer *p_sock, const char *sz, int len); + virtual int _read(socketcontainer *p_sock, char *sz, int len); + virtual int _close(socketcontainer *p_sock); + virtual void _main_loop_init(); +#ifdef OPENSSL + + virtual bool _main_loop_do_ssl_stuff(int& i_new_sock); +#endif + + virtual socketcontainer* _create_container(int& i_sock); + virtual int _make_server_socket(int i_port); + +#ifdef NCURSES + void print_server_port(); + void print_hits(); +#endif + +}; +#endif diff --git a/yhttpd/src/sock/sslsock.cpp b/yhttpd/src/sock/sslsock.cpp new file mode 100644 index 0000000..32efc0f --- /dev/null +++ b/yhttpd/src/sock/sslsock.cpp @@ -0,0 +1,141 @@ +#include "../incl.h" + +#ifdef OPENSSL +#ifndef SSLSOCK_CPP +#define SSLSOCK_CPP + +#include "sslsock.h" + +using namespace std; + +sslsock::sslsock() : sock() +{ + s_certificate_path = wrap::CONF->get_elem( "httpd.ssl.certificatepath" ); + s_privatekey_path = wrap::CONF->get_elem( "httpd.ssl.privatekeypath" ); + p_ctx = NULL; +} + +int +sslsock::_send(socketcontainer *p_sock, const char *sz, int len) +{ + return SSL_write((SSL*)p_sock->p_ssl_context,sz, len); +} + +int +sslsock::_read(socketcontainer *p_sock, char *sz, int len) +{ + return SSL_read((SSL*)p_sock->p_ssl_context,sz,len); +} + +int +sslsock::_close(socketcontainer *p_sock) +{ + SSL_free((SSL*)p_sock->p_ssl_context); + sock::_close(p_sock); +} + +int +sslsock::_make_server_socket(int i_port) +{ + SSL_METHOD *p_ssl_method; + unsigned long e; + char sz[1024]; + string s_error; + + int i_sock = sock::_make_server_socket(i_port); + + if(i_sock <= 0) + { + wrap::system_message(SSLERR1); + return -1; + } + + SSL_load_error_strings(); + SSLeay_add_ssl_algorithms(); + + p_ssl_method = SSLv23_server_method(); + p_ctx = SSL_CTX_new (p_ssl_method); + if (!p_ctx) + { + e = ERR_get_error(); + ERR_error_string_n(e, sz, sizeof(sz) - 1); + s_error = sz; + wrap::system_message(SSLERR1); + return -1; + } + + if (SSL_CTX_use_certificate_file(p_ctx, s_certificate_path.c_str(), SSL_FILETYPE_PEM) <= 0) + { + e = ERR_get_error(); + ERR_error_string_n(e, sz, sizeof(sz) - 1); + s_error = sz; + wrap::system_message(SSLERR1); + return -1; + } + + if (SSL_CTX_use_PrivateKey_file(p_ctx, s_privatekey_path.c_str(), SSL_FILETYPE_PEM) <= 0) + { + e = ERR_get_error(); + ERR_error_string_n(e, sz, sizeof(sz) - 1); + s_error = sz; + wrap::system_message(SSLERR1); + return -1; + } + + if (!SSL_CTX_check_private_key(p_ctx)) + { + wrap::system_message(SSLERR2); + return -1; + } + + return i_sock; +} + +void +sslsock::_main_loop_init() +{ + wrap::system_message(SOCKSEC); +} + +bool +sslsock::_main_loop_do_ssl_stuff(int& i_new_sock) +{ + SSL* p_ssl = SSL_new(p_ctx); + + if( p_ssl == NULL || i_new_sock < 0) + { + wrap::system_message(SSLERR3); + + close(i_new_sock); + if(p_ssl != NULL) + SSL_free(p_ssl); + + return 1; + } + + else + { + SSL_set_fd(p_ssl, i_new_sock); + if(SSL_accept(p_ssl) == -1) + { + wrap::system_message(SSLERR4); + close(i_new_sock); + return 1; + } + + map_certs[i_new_sock] = p_ssl; + } + + return 0; +} + +socketcontainer* +sslsock::_create_container(int &i_sock) +{ + socketcontainer* p_sock = sock::_create_container(i_sock); + p_sock->p_ssl_context = map_certs[i_sock]; + return p_sock; +} + +#endif +#endif diff --git a/yhttpd/src/sock/sslsock.h b/yhttpd/src/sock/sslsock.h new file mode 100644 index 0000000..f5358cc --- /dev/null +++ b/yhttpd/src/sock/sslsock.h @@ -0,0 +1,41 @@ +#include "../incl.h" + +#ifdef OPENSSL +#ifndef SSLSOCK_H +#define SSLSOCK_H + +#include "sock.h" + +#include <openssl/rsa.h> +#include <openssl/crypto.h> +#include <openssl/x509.h> +#include <openssl/pem.h> +#include <openssl/ssl.h> +#include <openssl/err.h> + +using namespace std; + +class sslsock : public sock +{ +private: + SSL_CTX* p_ctx; + string s_certificate_path; + string s_privatekey_path; + map<int,void*> map_certs; + +public: + + sslsock( ); + ~sslsock( ); + + int _send(socketcontainer *p_sock, const char *sz, int len); + int _read(socketcontainer *p_sock, char *sz, int len); + int _close(socketcontainer *p_sock); + void _main_loop_init(); + bool _main_loop_do_ssl_stuff(int &i_new_sock); + socketcontainer* _create_container(int& i_sock); + int _make_server_socket(int i_port); +}; + +#endif +#endif diff --git a/yhttpd/src/thrd/pool.cpp b/yhttpd/src/thrd/pool.cpp new file mode 100644 index 0000000..dd29d6a --- /dev/null +++ b/yhttpd/src/thrd/pool.cpp @@ -0,0 +1,157 @@ +#ifndef POOL_CPP +#define POOL_CPP + +#include "pool.h" + +using namespace std; + +pool::pool() +{ + pthread_mutex_init(&mut_threads, 0); + pthread_mutex_init(&mut_queue_tasks, 0); + pthread_mutex_init(&mut_num_avail_threads, 0); + pthread_cond_init(&cond_new_task, 0); + + i_num_total_threads = 0; + i_num_avail_threads = tool::string2int( wrap::CONF->get_elem( "httpd.thread.initpoolsize" ) ); + increase_pool(i_num_avail_threads); +} + +pool::~pool() +{ + pthread_mutex_lock(&mut_queue_tasks); + while (!queue_tasks.empty()) + { + delete queue_tasks.front(); + queue_tasks.pop(); + } + pthread_mutex_unlock(&mut_queue_tasks); + + pthread_mutex_destroy(&mut_threads); + pthread_mutex_destroy(&mut_queue_tasks); + pthread_mutex_destroy(&mut_num_avail_threads); + pthread_cond_destroy(&cond_new_task); +} + +int +pool::increase_pool(int i_num) +{ + wrap::system_message(POOLFLL + tool::int2string(i_num) +","+tool::int2string(i_num_total_threads)+")"); + int i_max_pool_size = tool::string2int( wrap::CONF->get_elem( "httpd.thread.maxpoolsize" ) ); + + for ( int i = 0; i < i_num; ++i ) + { + if ( i_max_pool_size != 0 && i_num_total_threads >= i_max_pool_size ) + { + wrap::system_message(POOLER2+tool::int2string(i_max_pool_size)+")"); + wrap::system_message(POOLER1+tool::int2string(i)+")"); + return i; + } + + ++i_num_total_threads; + pthread_t p_pthread; + pthread_create(&p_pthread, 0, wait_for_task, (void*) this ); + } + + return i_num; +} + +void +pool::add_task( void(*p_func)(void*), void* p_void ) +{ + pthread_mutex_lock(&mut_queue_tasks); + queue_tasks.push(new task(p_func, p_void)); + pthread_mutex_unlock(&mut_queue_tasks); + + pthread_cond_signal(&cond_new_task); + +} + +void* +pool::wait_for_task( void* p_void ) +{ + pool* p_pool = static_cast<pool*>(p_void); + + for (;;) + { + +#ifdef NCURSES + p_pool->print_pool_size(); +#endif + + pthread_mutex_lock(&p_pool->mut_threads); + pthread_cond_wait(&p_pool->cond_new_task, &p_pool->mut_threads); + + pthread_mutex_lock(&p_pool->mut_num_avail_threads); + if ( --p_pool->i_num_avail_threads < 5 ) + { + int i_size = 9 - p_pool->i_num_avail_threads; + i_size = p_pool->increase_pool(i_size); + p_pool->i_num_avail_threads += i_size; + } + pthread_mutex_unlock(&p_pool->mut_num_avail_threads); + + pthread_mutex_lock(&p_pool->mut_queue_tasks); + task* p_task = p_pool->queue_tasks.front(); + p_pool->queue_tasks.pop(); + pthread_mutex_unlock(&p_pool->mut_queue_tasks); + + pthread_mutex_unlock(&p_pool->mut_threads); + + (*(p_task->p_func))(p_task->p_void); + delete p_task; + + pthread_mutex_lock(&p_pool->mut_num_avail_threads); + p_pool->i_num_avail_threads++; + pthread_mutex_unlock(&p_pool->mut_num_avail_threads); + } + + return 0; +} + +void +pool::run(void* p_void) +{ + add_task(run_func, p_void); +} + +void +pool::run_func(void *p_void) +{ + socketcontainer* p_sock = static_cast<socketcontainer*>(p_void); + wrap::SOCK->read_write(p_sock); +} + +bool +pool::allow_user_login() +{ + pthread_mutex_lock(&mut_num_avail_threads); + if ( i_num_avail_threads < 2 ) + { + int i_max_pool_size = tool::string2int( wrap::CONF->get_elem( "httpd.thread.maxpoolsize" ) ); + if ( i_max_pool_size != 0 && i_max_pool_size == i_num_total_threads ) + { + pthread_mutex_unlock(&mut_num_avail_threads); + return false; + } + } + pthread_mutex_unlock(&mut_num_avail_threads); + + return true; +} + +#ifdef NCURSES +void +pool::print_pool_size() +{ + if ( wrap::NCUR->is_ready() ) + { + pthread_mutex_lock(&mut_num_avail_threads); + mvprintw( NCUR_POOL_WAIT_X,NCUR_POOL_WAIT_Y, "Wait/Tot: %d/%d ", i_num_avail_threads, i_num_total_threads); + mvprintw( NCUR_POOL_RUNNING_X,NCUR_POOL_RUNNING_Y, "Running: %d ", i_num_total_threads-i_num_avail_threads); + pthread_mutex_unlock(&mut_num_avail_threads); + refresh(); + } +} +#endif +#endif diff --git a/yhttpd/src/thrd/pool.h b/yhttpd/src/thrd/pool.h new file mode 100644 index 0000000..3a5f7b6 --- /dev/null +++ b/yhttpd/src/thrd/pool.h @@ -0,0 +1,55 @@ +#include "../incl.h" + +#ifndef POOL_H +#define POOL_H + +#include <queue> + +using namespace std; + +class pool +{ +private: + friend class thro; + + struct task + { + void(*p_func)(void*); + void *p_void; + + task(void(*p_func)(void*), void *p_void) + { + this->p_func = p_func; + this->p_void = p_void; + } + }; + + pthread_mutex_t mut_threads; + pthread_mutex_t mut_queue_tasks; + pthread_mutex_t mut_num_avail_threads; + pthread_cond_t cond_new_task; + + int i_num_avail_threads; + int i_num_total_threads; + + queue<task*> queue_tasks; + + int increase_pool(int i_num); + void add_task( void(*p_func)(void*), void* p_void ); + static void* wait_for_task(void *p_void); + static void run_func(void *p_void); + +public: + pool(); + ~pool(); + + void run(void* p_void); + bool allow_user_login(); + +#ifdef NCURSES + + void print_pool_size(); +#endif +}; + +#endif diff --git a/yhttpd/src/thrd/thro.cpp b/yhttpd/src/thrd/thro.cpp new file mode 100644 index 0000000..8b3f1ba --- /dev/null +++ b/yhttpd/src/thrd/thro.cpp @@ -0,0 +1,43 @@ +#ifndef THRO_CPP +#define THRO_CPP + +#include "thro.h" + +using namespace std; + +thro::thro() +{} + +thro::~thro() +{} + +void +thro::run() +{ + void *p_void; + run( p_void ); +} + +void +thro::run( void *p_void ) +{ + elem.p_thro = this; + elem.p_void = p_void; + //wrap::POOL->add_task(start_, &elem); + pthread_create( &pthread, NULL, start_, &elem ); +} + +void* +thro::start_( void *p_void ) +{ + elements *e = (elements*) p_void; + e->p_thro->start( e->p_void ); +} + +void +thro::start( void *p_void ) +{ + wrap::system_message( THRDSTR ); +} + +#endif diff --git a/yhttpd/src/thrd/thro.h b/yhttpd/src/thrd/thro.h new file mode 100644 index 0000000..8e7e0cf --- /dev/null +++ b/yhttpd/src/thrd/thro.h @@ -0,0 +1,30 @@ +#include "../incl.h" + +#ifndef THRO_H +#define THRO_H + +using namespace std; + +class thro +{ +private: + pthread_t pthread; + + struct elements + { + thro *p_thro; + void *p_void; + } + elem; + + static void *start_( void *p_void ); + +public: + thro( ); + ~thro( ); + void run(); + void run( void *p_void ); + virtual void start( void *p_void ); +}; + +#endif diff --git a/yhttpd/src/time/timo.cpp b/yhttpd/src/time/timo.cpp new file mode 100644 index 0000000..fd89bdf --- /dev/null +++ b/yhttpd/src/time/timo.cpp @@ -0,0 +1,38 @@ +#ifndef TIMO_CPP +#define TIMO_CPP + +#include "timo.h" + +using namespace std; + +timo::timo() +{ + pthread_mutex_init( &mut_t_time, NULL ); +} + +timo::~timo() +{ + pthread_mutex_destroy( &mut_t_time ); +} + +double +timo::get_last_activity( ) +{ + double d_ret; + + pthread_mutex_lock ( &mut_t_time ); + d_ret = wrap::TIMR->get_time_diff( t_time ); + pthread_mutex_unlock( &mut_t_time ); + + return d_ret; +} + +void +timo::renew_timeout( ) +{ + pthread_mutex_lock ( &mut_t_time ); + time( &t_time ); + pthread_mutex_unlock( &mut_t_time ); +} + +#endif diff --git a/yhttpd/src/time/timo.h b/yhttpd/src/time/timo.h new file mode 100644 index 0000000..ea160c1 --- /dev/null +++ b/yhttpd/src/time/timo.h @@ -0,0 +1,22 @@ +#include "../incl.h" + +#ifndef TIMO_H +#define TIMO_H + +using namespace std; + +class timo // timeout class +{ +protected: + time_t t_time; // last activity time. + pthread_mutex_t mut_t_time; + +public: + timo( ); + ~timo( ); + + double get_last_activity(); + virtual void renew_timeout(); +}; + +#endif diff --git a/yhttpd/src/time/timr.cpp b/yhttpd/src/time/timr.cpp new file mode 100644 index 0000000..c7f80ee --- /dev/null +++ b/yhttpd/src/time/timr.cpp @@ -0,0 +1,177 @@ +#ifndef TIMR_CPP +#define TIMR_CPP + +#include <sys/time.h> +#include "timr.h" + +using namespace std; + +timr::timr() +{ + wrap::system_message( TIMERIN ); + b_timer_active = true; + + pthread_mutex_init( &mut_s_time, NULL); + pthread_mutex_init( &mut_s_uptime, NULL); + pthread_mutex_init( &mut_i_offset, NULL); + + i_time_offset = tool::string2int( wrap::CONF->get_elem("chat.timeoffset") ); + wrap::system_message( TIMEROF + tool::int2string( i_time_offset ) ); + + s_time = "00:00:00"; + s_uptime = "00:00:00"; +} + +timr::~timr() +{ + pthread_mutex_destroy( &mut_s_time ); + pthread_mutex_destroy( &mut_s_uptime ); + pthread_mutex_destroy( &mut_i_offset ); +} + +bool +timr::get_timer_active() const +{ + return b_timer_active; +} + +int +timr::get_offset() +{ + pthread_mutex_lock ( &mut_i_offset ); + int i_ret_val = i_time_offset; + pthread_mutex_unlock( &mut_i_offset ); + return i_ret_val; +} + +void +timr::start( void *v_ptr ) +{ + wrap::system_message( TIMERTH ); + +#ifdef NCURSES + + print_time( ); +#endif + + time_t clock_start; + time_t clock_now; + + time( &clock_start ); + tm time_start = *localtime( &clock_start ); + tm time_now; + + while ( get_timer_active() ) + { + // sleep a second! + usleep( 1000000 ); + + // get the current time! + time( &clock_now ); + + time_now = *localtime( &clock_now ); + + // set the current time && the current yhttpd uptime! + set_time( difftime( clock_now, clock_start ), + time_now.tm_sec, time_now.tm_min, time_now.tm_hour ); + +#ifdef NCURSES + + if (wrap::NCUR->is_ready()) + print_time( ); +#endif + + // run every minute: + if ( time_now.tm_sec == 0 ) + { +#ifdef SERVMSG + cout << TIMERUP << get_uptime() << endl; +#endif + // Run every ten minutes: + if ( time_now.tm_min % 10 == 0 ) + { + + wrap::SOCK->clean_ipcache(); + // Run every hour + if ( time_now.tm_min % 60 == 0 ) + { + + // Run every day + if (time_now.tm_min == 0 || time_now.tm_min == 60 ) + if (time_now.tm_hour == 0 || time_now.tm_hour == 24) + wrap::STAT->update_rusage_history(); + } + } + } + } +} + +#ifdef NCURSES +void +timr::print_time( ) +{ + if ( !wrap::NCUR->is_ready() ) + return; + + mvprintw( NCUR_TIME_X, NCUR_TIME_Y, "Time: %s ", get_time().c_str()); + mvprintw( NCUR_UPTIME_X, NCUR_UPTIME_Y, "Uptime: %s ", get_uptime().c_str()); + refresh(); +} +#endif + +void +timr::set_time( double d_uptime, int i_cur_seconds, int i_cur_minutes, int i_cur_hours ) +{ + + int i_hours = (int) d_uptime / 3600; + int i_minutes = (int) d_uptime / 60; + + while ( i_minutes >= 60 ) + i_minutes -= 60; + + while ( d_uptime >= 60 ) + d_uptime -= 60; + + // Calculate offset time + i_cur_hours += get_offset(); + + for ( int i = 24-i_cur_hours; i < 0; i = 24-i_cur_hours ) + i_cur_hours =- i; + + if (i_cur_hours == 24) + i_cur_hours = 0; + + pthread_mutex_lock ( &mut_s_time ); + s_time = add_zero_to_front( tool::int2string( i_cur_hours ) ) + ":" + + add_zero_to_front( tool::int2string( i_cur_minutes ) ) + ":" + + add_zero_to_front( tool::int2string( i_cur_seconds ) ); + pthread_mutex_unlock( &mut_s_time ); + + pthread_mutex_lock ( &mut_s_uptime ); + s_uptime = add_zero_to_front( tool::int2string( i_hours ) ) + ":" + + add_zero_to_front( tool::int2string( i_minutes ) ) + ":" + + add_zero_to_front( tool::int2string( (int) d_uptime ) ); + pthread_mutex_unlock( &mut_s_uptime ); +} + +string +timr::add_zero_to_front( string s_time ) +{ + if ( s_time.length() == 1 ) + { + string s_new = "0" + s_time; + return s_new; + } + + return s_time; +} + +double +timr::get_time_diff( time_t &clock_diff ) +{ + time_t clock_now; + time( &clock_now ); + + return difftime(clock_now, clock_diff); +} +#endif diff --git a/yhttpd/src/time/timr.h b/yhttpd/src/time/timr.h new file mode 100644 index 0000000..12cdd8f --- /dev/null +++ b/yhttpd/src/time/timr.h @@ -0,0 +1,64 @@ +#include "../incl.h" + +#ifndef TIMR_H +#define TIMR_H + +#include "../thrd/thro.h" + +#include <unistd.h> + +using namespace std; + +class timr : public thro +{ +private: + bool b_timer_active; + int i_time_offset; + string s_uptime; + string s_time; + + pthread_mutex_t mut_s_time; + pthread_mutex_t mut_s_uptime; + pthread_mutex_t mut_i_offset; + +public: + timr(); + ~timr(); + + bool get_timer_active() const; + void start( void *v_ptr ); + +#ifdef NCURSES + + void print_time(); +#endif + + void set_time( double d_uptime, int i_cur_seconds, int i_cur_minutes, int i_cur_hours ); + string add_zero_to_front( string s_time ); + + // inline for dynamic module access! + string + get_time( ) + { + string s_ret; + pthread_mutex_lock ( &mut_s_time ); + s_ret = this->s_time; + pthread_mutex_unlock( &mut_s_time ); + return s_ret; + } + + string + get_uptime( ) + { + string s_ret; + pthread_mutex_lock ( &mut_s_uptime ); + s_ret = this->s_uptime; + pthread_mutex_unlock( &mut_s_uptime ); + return s_ret; + } + + int get_offset(); + double get_time_diff( time_t &clock_diff ); +}; + +#endif diff --git a/yhttpd/src/tool/dir.cpp b/yhttpd/src/tool/dir.cpp new file mode 100644 index 0000000..a23cca2 --- /dev/null +++ b/yhttpd/src/tool/dir.cpp @@ -0,0 +1,66 @@ +#ifndef DIR_CPP +#define DIR_CPP + +#include "dir.h" + +using namespace std; + +dir::dir() +{ + b_open = false; +} + +dir::~dir() +{ + vec_dir.clear(); + close_dir(); +} + +bool +dir::open_dir( char *c_dir ) +{ + string s_dir( c_dir ); + return open_dir( s_dir ); +} + +bool +dir::open_dir( string &s_dir ) +{ + if ( b_open ) + return false; + + p_d = opendir( s_dir.c_str() ); + + if ( p_d == NULL ) + return false; // Could not open dir. + + b_open = true; + + return true; // Could open dir with success. +} + +void +dir::close_dir() +{ + if ( b_open && p_d != NULL ) + { + closedir( p_d ); + b_open = false; + } +} + +void +dir::read_dir() +{ + if ( p_d != NULL ) + while( p_ep = readdir( p_d ) ) + vec_dir.push_back( string( p_ep->d_name ) ); +} + +vector<string> +dir::get_dir_vec() +{ + return vec_dir; +} + +#endif diff --git a/yhttpd/src/tool/dir.h b/yhttpd/src/tool/dir.h new file mode 100644 index 0000000..eea78f7 --- /dev/null +++ b/yhttpd/src/tool/dir.h @@ -0,0 +1,35 @@ + +#ifndef DIR_H +#define DIR_H + +#include <stddef.h> +#include <stdio.h> +#include <sys/types.h> +#include <dirent.h> + +#include <vector> + +#include "../incl.h" + +using namespace std; + +class dir +{ +private: + bool b_open; + DIR *p_d; + struct dirent *p_ep; + vector<string> vec_dir; + +public: + dir(); + ~dir(); + + bool open_dir( char *c_dir ); + bool open_dir( string &s_dir ); + void close_dir(); + void read_dir(); + vector<string> get_dir_vec(); +}; + +#endif diff --git a/yhttpd/src/tool/tool.cpp b/yhttpd/src/tool/tool.cpp new file mode 100644 index 0000000..dbb1f22 --- /dev/null +++ b/yhttpd/src/tool/tool.cpp @@ -0,0 +1,240 @@ +#ifndef TOOL_CPP +#define TOOL_CPP + +#include <ctype.h> +#include <time.h> +#include <unistd.h> +#include <sys/wait.h> +#include <stdio.h> +#include <sys/types.h> +#include <fcntl.h> + +#include "tool.h" + +bool +tool::is_alpha_numeric( string &s_digit ) +{ + const char *p_digit = s_digit.c_str(); + int i_len = strlen( p_digit ); + + for( int i=0; i<i_len; i++ ) + { + if ( ! isalnum( *p_digit ) ) + return false; + p_digit++; + } + + return true; +} + +string +tool::int2string( int i_int ) +{ + char buf[64]; + sprintf(buf, "%d", i_int); + return buf; +} + +long +tool::unixtime() +{ + time_t clock; + return (long) time( &clock ); +} + +int +tool::string2int( string s_digit ) +{ + const char *p_digit = s_digit.c_str(); + int i_res = 0; + + // Convert each digit char and add into result. + while (*p_digit >= '0' && *p_digit <='9') + { + i_res = (i_res * 10) + (*p_digit - '0'); + p_digit++; + } + + // Check that there were no non-digits at end. + if (*p_digit != 0) + { + return -1; + } + + return i_res; +} + +string +tool::to_lower( string s_str ) +{ + string s_tmp(""); + + for( int i = 0; i < s_str.size() ;i++ ) + s_tmp = s_tmp + (char) tolower( s_str.at(i) ); + + return s_tmp; +} + +void +tool::strip_html( string *p_str) +{ + int i_pos; + + if( (i_pos=p_str->find("<", 0)) == string::npos ) + return; + + while(true) + { + p_str->replace(i_pos, 1, "<"); + + if( (i_pos = p_str->find("<", 0)) == string::npos ) + return; + } +} + +string +tool::yhttpd_version() +{ + return "yhttpd " + string(VERSION) + + "-" + string(BRANCH) + + " Build " + int2string(BUILDNR); +} + +list<string> +tool::split_string(string s_string, string s_split) +{ + list<string> list_ret; + unsigned i_pos, i_len = s_split.length(); + + while ( (i_pos = s_string.find(s_split)) != string::npos ) + { + list_ret.push_back( s_string.substr(0, i_pos) ); + s_string = s_string.substr( i_pos + i_len ); + } + + list_ret.push_back( s_string ); + + return list_ret; +} + +string +tool::trim( string s_str ) +{ + if( s_str.empty() ) + return s_str; + + char c_cur = s_str[0]; + int i_pos = 0; + + // left trim + while ( c_cur == ' '|| c_cur == '\n' || c_cur == '\r' ) + { + s_str.erase(i_pos,1); + c_cur = s_str[++i_pos]; + } + + // right trim + i_pos = s_str.size(); + c_cur = s_str[s_str.size()]; + + while ( c_cur == ' ' || c_cur == '\n' || c_cur == '\0' || c_cur == '\r' ) + { + s_str.erase(i_pos, 1); + c_cur = s_str[--i_pos]; + } + + return s_str; +} + +char* +tool::clean_char( char* c_str ) +{ + // Ralf: + for ( char* c_pos = c_str; *c_pos != '\0'; ++c_pos ) + if ( iscntrl(*c_pos) ) + *c_pos = ' '; + + return c_str; +} + +string +tool::replace( string s_string, string s_search, string s_replace ) +{ + unsigned i_pos[2]; + + for ( i_pos[0] = s_string.find( s_search ); + i_pos[0] != string::npos; + i_pos[0] = s_string.find( s_search, i_pos[1] ) ) + { + s_string.replace( i_pos[0], s_search.length(), s_replace ); + i_pos[1] = i_pos[0] + s_replace.length(); + } + + return s_string; +} + +string +tool::get_extension( string s_file ) +{ + int i_pos = s_file.find_last_of("."); + + if( i_pos != string::npos ) + { + string s_ext = s_file.substr(i_pos+1, s_file.size()-i_pos-1 ); + for( int i = 0; i < s_ext.size(); ++i ) + s_ext[i] = tolower(s_ext[i]); + + return to_lower(s_ext); + } + + return ""; +} + +char* +tool::int2char( int i_int ) +{ + char *buf = new char[64]; + sprintf(buf, "%d", i_int); + return buf; +} + +string +tool::shell_command( string s_command, method m_method ) +{ + FILE *file; + char buf[READBUF]; + char *c_pos; + string s_ret = ""; + + wrap::system_message(SHELLEX); + wrap::system_message(s_command); + + if( (file=popen(s_command.c_str(), "r")) == NULL ) + { + wrap::system_message( SHELLER ); + } + else + { + while(true) + { + if(fgets(buf, READBUF, file) == NULL) + break; + + switch (m_method) + { + case METH_NCURSES: + wrap::system_message( clean_char(buf) ); + break; + default: + s_ret.append("\n" + string(buf)); + } // switch + } + + pclose(file); + } + + return s_ret; +} + +#endif + diff --git a/yhttpd/src/tool/tool.h b/yhttpd/src/tool/tool.h new file mode 100644 index 0000000..7a1958d --- /dev/null +++ b/yhttpd/src/tool/tool.h @@ -0,0 +1,29 @@ +#ifndef TOOL_H +#define TOOL_H + +#include "../incl.h" + +#include <list> + +using namespace std; + +class tool +{ +public: + static list<string> split_string(string s_string, string s_split); + static bool is_alpha_numeric( string &s_digit ); + static char* int2char( int i_int ); + static char* clean_char( char* c_str); + static string trim( string s_str ); + static string replace( string s_string, string s_search, string s_replace ); + static string int2string( int i_int ); + static long unixtime(); + static int string2int( string s_digit ); + static string get_extension( string s_file ); + static string to_lower( string s_str ); + static void strip_html( string *p_str ); + static string shell_command( string s_command, method m_method ); + static string yhttpd_version(); +}; + +#endif diff --git a/yhttpd/src/wrap.cpp b/yhttpd/src/wrap.cpp new file mode 100644 index 0000000..251a575 --- /dev/null +++ b/yhttpd/src/wrap.cpp @@ -0,0 +1,121 @@ +#ifndef WRAP_CPP +#define WRAP_CPP + +#include "wrap.h" + +using namespace std; + + +conf* wrap::CONF = NULL; +html* wrap::HTML = NULL; +#ifdef LOGGING +logd* wrap::LOGD = NULL; +#endif +#ifdef NCURSES +ncur* wrap::NCUR = NULL; +#endif +sock* wrap::SOCK = NULL; +stats* wrap::STAT = NULL; +timr* wrap::TIMR = NULL; +pool* wrap::POOL = NULL; +dynamic_wrap* wrap::WRAP = NULL; + +void +wrap::system_message( string s_message ) +{ +#ifdef NCURSES + if(NCUR) + { + NCUR->print( s_message ); + } + + else + { + cout << s_message << endl; + } +#endif + +#ifdef SERVMSG + cout << s_message << endl; +#endif + +#ifdef LOGGING + + LOGD->log_simple_line( s_message + "\n" ); +#endif +} + +void +wrap::init_wrapper(map<string,string>* p_main_loop_params) +{ + // Init the dynamic wrapper (is needed to pass all wrapped objects through a single pointer). + WRAP = new dynamic_wrap; + + // Init the config manager. + WRAP->CONF = CONF = new conf( CONFILE, p_main_loop_params ); + delete p_main_loop_params, + + // Init the statistic manager. + WRAP->STAT = STAT = new stats; + + // Init the html-template manager. + WRAP->HTML = HTML = new html; + +#ifdef LOGGING + // Init the system message logd + WRAP->LOGD = LOGD = new logd( CONF->get_elem("httpd.logging.systemfile"), + CONF->get_elem("httpd.logging.systemlines") ); +#endif + + // Init the socket manager. + int i_port = tool::string2int( wrap::CONF->get_elem( "httpd.serverport" ) ); + +#ifndef OPENSSL + + WRAP->SOCK = SOCK = new sock; +#else + + WRAP->SOCK = SOCK = new sslsock; +#endif + + // create the server socket and set it up to accept connections. + if(SOCK->_make_server_socket ( i_port ) <= 0) + { + system_message(SOCKER1); + exit(-1); + } + +#ifdef NCURSES + + WRAP->NCUR = NCUR = new ncur; // init the ncurses admin interface. + NCUR->run(); // run the thread + + // Wait until ncurses interface has been initialized. + do + { + usleep(1000); + } + while ( ! NCUR->is_ready() ); + + HTML->print_cached(0); +#else +#ifdef CLI + + cli* p_cli = new cli; + p_cli->run(); +#endif +#endif + + // Init the thread pool + WRAP->POOL = POOL = new pool; + + + // Init the system timer. + WRAP->TIMR = TIMR = new timr; + + + // Run threads + TIMR->run(); +} + +#endif diff --git a/yhttpd/src/wrap.h b/yhttpd/src/wrap.h new file mode 100644 index 0000000..768184a --- /dev/null +++ b/yhttpd/src/wrap.h @@ -0,0 +1,105 @@ +#ifndef WRAP_H +#define WRAP_H + +#include "incl.h" + + +struct socketcontainer +{ + int i_sock; +#ifdef OPENSSL + void *p_ssl_context; +#endif + +}; + +#ifdef DATABASE +#endif +#include "conf/conf.h" +#include "html.h" +#ifdef LOGGING +#include "logd.h" +#endif + +#ifdef NCURSES +#include "ncur/ncur.h" +#else +#ifdef CLI +#include "cli/cli.h" +#endif +#endif + + +#ifndef OPENSSL +#include "sock/sock.h" +#else +#include "sock/sslsock.h" +#endif + +#include "monitor/stats.h" +#include "time/timr.h" +#include "thrd/pool.h" + + +using namespace std; + + +class dynamic_wrap +{ +public: + + conf* CONF; + html* HTML; +#ifdef LOGGING + + logd* LOGD; +#endif +#ifdef NCURSES + + ncur* NCUR; +#endif + + sock* SOCK; + stats* STAT; + timr* TIMR; + pool* POOL; +}; + +class wrap +{ +public: + static void system_message( char* c_message ) + { + wrap::system_message( string(c_message) ); + } + + static void system_message( string* p_message ) + { + wrap::system_message( *p_message ); + } + + static void system_message( string s_message ); + + static void init_wrapper(map<string,string>* p_main_loop_params); + + + static conf* CONF; + static html* HTML; +#ifdef LOGGING + + static logd* LOGD; +#endif +#ifdef NCURSES + + static ncur* NCUR; +#endif + + static sock* SOCK; + static stats* STAT; + static timr* TIMR; + static pool* POOL; + static dynamic_wrap* WRAP; +}; + + +#endif |
