revising httpd
This commit is contained in:
		| @ -1,2 +1,2 @@ | ||||
| SUBDIRS = cmn awk sed net  | ||||
| SUBDIRS = cmn awk sed xli http  | ||||
| DIST_SUBDIRS = $(SUBDIRS)  | ||||
|  | ||||
| @ -270,7 +270,7 @@ target_alias = @target_alias@ | ||||
| top_build_prefix = @top_build_prefix@ | ||||
| top_builddir = @top_builddir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| SUBDIRS = cmn awk sed net  | ||||
| SUBDIRS = cmn awk sed xli http  | ||||
| DIST_SUBDIRS = $(SUBDIRS)  | ||||
| all: all-recursive | ||||
|  | ||||
|  | ||||
| @ -103,7 +103,7 @@ const qse_char_t* qse_awk_dflerrstr (const qse_awk_t* awk, qse_awk_errnum_t errn | ||||
| 		QSE_T("illegal operand for increment/decrement operator"), | ||||
| 		QSE_T("'@include' not followed by a string"), | ||||
| 		QSE_T("include level too deep"), | ||||
| 		QSE_T("@word '${0}' not recognized"), | ||||
| 		QSE_T("'${0}' not recognized"), | ||||
| 		QSE_T("@ not followed by a valid word"), | ||||
|  | ||||
| 		QSE_T("divide by zero"), | ||||
|  | ||||
| @ -5512,7 +5512,7 @@ static int get_string ( | ||||
|  | ||||
| 		if (c == QSE_CHAR_EOF) | ||||
| 		{ | ||||
| 			SETERR_TOK (awk, QSE_AWK_ESTRNC); | ||||
| 			SETERR_LOC (awk, QSE_AWK_ESTRNC, &awk->tok.loc); | ||||
| 			return -1; | ||||
| 		} | ||||
|  | ||||
| @ -6072,7 +6072,7 @@ retry: | ||||
|  | ||||
| 			if (c == QSE_CHAR_EOF) | ||||
| 			{ | ||||
| 				SETERR_TOK (awk, QSE_AWK_ESTRNC); | ||||
| 				SETERR_LOC (awk, QSE_AWK_ESTRNC, &awk->tok.loc); | ||||
| 				return -1; | ||||
| 			} | ||||
|  | ||||
|  | ||||
| @ -20,6 +20,7 @@ | ||||
|  | ||||
| #include <qse/cmn/main.h> | ||||
| #include <qse/cmn/mbwc.h> | ||||
|  | ||||
| #include "mem.h" | ||||
|  | ||||
| int qse_runmain ( | ||||
|  | ||||
| @ -21,10 +21,10 @@ | ||||
| #include <qse/cmn/uri.h> | ||||
| #include "mem.h" | ||||
|  | ||||
| int qse_mbstouri (const qse_mchar_t* str, qse_uri_t* uri, int flags) | ||||
| int qse_mbstouri (const qse_mchar_t* str, qse_muri_t* uri, int flags) | ||||
| { | ||||
| 	const qse_mchar_t* ptr, * colon; | ||||
| 	qse_uri_t xuri; | ||||
| 	qse_muri_t xuri; | ||||
|  | ||||
| 	QSE_MEMSET (&xuri, 0, QSE_SIZEOF(xuri)); | ||||
|  | ||||
| @ -145,10 +145,10 @@ int qse_mbstouri (const qse_mchar_t* str, qse_uri_t* uri, int flags) | ||||
|  | ||||
| /* -------------------------------------------------------- */ | ||||
|  | ||||
| int qse_wcstouri (const qse_wchar_t* str, qse_uri_t* uri, int flags) | ||||
| int qse_wcstouri (const qse_wchar_t* str, qse_wuri_t* uri, int flags) | ||||
| { | ||||
| 	const qse_wchar_t* ptr, * colon; | ||||
| 	qse_uri_t xuri; | ||||
| 	qse_wuri_t xuri; | ||||
|  | ||||
| 	QSE_MEMSET (&xuri, 0, QSE_SIZEOF(xuri)); | ||||
|  | ||||
|  | ||||
| @ -5,8 +5,8 @@ AM_CPPFLAGS = \ | ||||
| 	-I$(top_srcdir)/include \
 | ||||
| 	-I$(includedir) | ||||
| 
 | ||||
| lib_LTLIBRARIES = libqsenet.la | ||||
| libqsenet_la_SOURCES = \
 | ||||
| lib_LTLIBRARIES = libqsehttp.la | ||||
| libqsehttp_la_SOURCES = \
 | ||||
| 	httpd.h \
 | ||||
| 	upxd.h \
 | ||||
| 	http.c \
 | ||||
| @ -23,6 +23,6 @@ libqsenet_la_SOURCES = \ | ||||
| 	httpd-text.c \
 | ||||
| 	upxd.c | ||||
| 
 | ||||
| libqsenet_la_LDFLAGS = -version-info 1:0:0 -no-undefined -L../cmn -L$(libdir) | ||||
| libqsenet_la_LIBADD = -lqsecmn $(SOCKET_LIBS) $(SENDFILE_LIBS) $(SSL_LIBS) | ||||
| libqsehttp_la_LDFLAGS = -version-info 1:0:0 -no-undefined -L../cmn -L$(libdir) | ||||
| libqsehttp_la_LIBADD = -lqsecmn $(SOCKET_LIBS) $(SENDFILE_LIBS) $(SSL_LIBS) | ||||
| 
 | ||||
| @ -34,7 +34,7 @@ PRE_UNINSTALL = : | ||||
| POST_UNINSTALL = : | ||||
| build_triplet = @build@ | ||||
| host_triplet = @host@ | ||||
| subdir = lib/net | ||||
| subdir = lib/http | ||||
| DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in | ||||
| ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 | ||||
| am__aclocal_m4_deps = $(top_srcdir)/m4/argz.m4 \
 | ||||
| @ -79,19 +79,19 @@ am__uninstall_files_from_dir = { \ | ||||
| am__installdirs = "$(DESTDIR)$(libdir)" | ||||
| LTLIBRARIES = $(lib_LTLIBRARIES) | ||||
| am__DEPENDENCIES_1 = | ||||
| libqsenet_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
 | ||||
| libqsehttp_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
 | ||||
| 	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) | ||||
| am_libqsenet_la_OBJECTS = http.lo htre.lo htrd.lo httpd.lo \
 | ||||
| am_libqsehttp_la_OBJECTS = http.lo htre.lo htrd.lo httpd.lo \
 | ||||
| 	httpd-cgi.lo httpd-dir.lo httpd-file.lo httpd-proxy.lo \
 | ||||
| 	httpd-resol.lo httpd-std.lo httpd-task.lo httpd-text.lo \
 | ||||
| 	upxd.lo | ||||
| libqsenet_la_OBJECTS = $(am_libqsenet_la_OBJECTS) | ||||
| libqsehttp_la_OBJECTS = $(am_libqsehttp_la_OBJECTS) | ||||
| AM_V_lt = $(am__v_lt_@AM_V@) | ||||
| am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) | ||||
| am__v_lt_0 = --silent | ||||
| libqsenet_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
 | ||||
| libqsehttp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
 | ||||
| 	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
 | ||||
| 	$(libqsenet_la_LDFLAGS) $(LDFLAGS) -o $@ | ||||
| 	$(libqsehttp_la_LDFLAGS) $(LDFLAGS) -o $@ | ||||
| DEFAULT_INCLUDES =  | ||||
| depcomp = $(SHELL) $(top_srcdir)/ac/depcomp | ||||
| am__depfiles_maybe = depfiles | ||||
| @ -118,8 +118,8 @@ am__v_CCLD_0 = @echo "  CCLD  " $@; | ||||
| AM_V_GEN = $(am__v_GEN_@AM_V@) | ||||
| am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) | ||||
| am__v_GEN_0 = @echo "  GEN   " $@; | ||||
| SOURCES = $(libqsenet_la_SOURCES) | ||||
| DIST_SOURCES = $(libqsenet_la_SOURCES) | ||||
| SOURCES = $(libqsehttp_la_SOURCES) | ||||
| DIST_SOURCES = $(libqsehttp_la_SOURCES) | ||||
| ETAGS = etags | ||||
| CTAGS = ctags | ||||
| DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) | ||||
| @ -303,8 +303,8 @@ AM_CPPFLAGS = \ | ||||
| 	-I$(top_srcdir)/include \
 | ||||
| 	-I$(includedir) | ||||
| 
 | ||||
| lib_LTLIBRARIES = libqsenet.la | ||||
| libqsenet_la_SOURCES = \
 | ||||
| lib_LTLIBRARIES = libqsehttp.la | ||||
| libqsehttp_la_SOURCES = \
 | ||||
| 	httpd.h \
 | ||||
| 	upxd.h \
 | ||||
| 	http.c \
 | ||||
| @ -321,8 +321,8 @@ libqsenet_la_SOURCES = \ | ||||
| 	httpd-text.c \
 | ||||
| 	upxd.c | ||||
| 
 | ||||
| libqsenet_la_LDFLAGS = -version-info 1:0:0 -no-undefined -L../cmn -L$(libdir) | ||||
| libqsenet_la_LIBADD = -lqsecmn $(SOCKET_LIBS) $(SENDFILE_LIBS) $(SSL_LIBS) | ||||
| libqsehttp_la_LDFLAGS = -version-info 1:0:0 -no-undefined -L../cmn -L$(libdir) | ||||
| libqsehttp_la_LIBADD = -lqsecmn $(SOCKET_LIBS) $(SENDFILE_LIBS) $(SSL_LIBS) | ||||
| all: all-am | ||||
| 
 | ||||
| .SUFFIXES: | ||||
| @ -336,9 +336,9 @@ $(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps) | ||||
| 	      exit 1;; \
 | ||||
| 	  esac; \
 | ||||
| 	done; \
 | ||||
| 	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/net/Makefile'; \
 | ||||
| 	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/http/Makefile'; \
 | ||||
| 	$(am__cd) $(top_srcdir) && \
 | ||||
| 	  $(AUTOMAKE) --foreign lib/net/Makefile | ||||
| 	  $(AUTOMAKE) --foreign lib/http/Makefile | ||||
| .PRECIOUS: Makefile | ||||
| Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status | ||||
| 	@case '$?' in \
 | ||||
| @ -388,8 +388,8 @@ clean-libLTLIBRARIES: | ||||
| 	  echo "rm -f \"$${dir}/so_locations\""; \
 | ||||
| 	  rm -f "$${dir}/so_locations"; \
 | ||||
| 	done | ||||
| libqsenet.la: $(libqsenet_la_OBJECTS) $(libqsenet_la_DEPENDENCIES) $(EXTRA_libqsenet_la_DEPENDENCIES)  | ||||
| 	$(AM_V_CCLD)$(libqsenet_la_LINK) -rpath $(libdir) $(libqsenet_la_OBJECTS) $(libqsenet_la_LIBADD) $(LIBS) | ||||
| libqsehttp.la: $(libqsehttp_la_OBJECTS) $(libqsehttp_la_DEPENDENCIES) $(EXTRA_libqsehttp_la_DEPENDENCIES)  | ||||
| 	$(AM_V_CCLD)$(libqsehttp_la_LINK) -rpath $(libdir) $(libqsehttp_la_OBJECTS) $(libqsehttp_la_LIBADD) $(LIBS) | ||||
| 
 | ||||
| mostlyclean-compile: | ||||
| 	-rm -f *.$(OBJEXT) | ||||
| @ -18,7 +18,7 @@ | ||||
|     License along with QSE. If not, see <htrd://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #include <qse/net/htrd.h> | ||||
| #include <qse/http/htrd.h> | ||||
| #include <qse/cmn/chr.h> | ||||
| #include <qse/cmn/path.h> | ||||
| #include "../cmn/mem.h" | ||||
| @ -18,7 +18,7 @@ | ||||
|     License along with QSE. If not, see <htrd://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #include <qse/net/htre.h> | ||||
| #include <qse/http/htre.h> | ||||
| #include "../cmn/mem.h" | ||||
| 
 | ||||
| static void free_hdrval (qse_htb_t* htb, void* vptr, qse_size_t vlen) | ||||
| @ -18,7 +18,7 @@ | ||||
|     License along with QSE. If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #include <qse/net/http.h> | ||||
| #include <qse/http/http.h> | ||||
| #include <qse/cmn/str.h> | ||||
| #include <qse/cmn/chr.h> | ||||
| #include <qse/cmn/htb.h> | ||||
| @ -446,13 +446,20 @@ static int cgi_add_env ( | ||||
| 	if (suffix && suffix[0] != QSE_MT('\0'))  | ||||
| 	{ | ||||
| 		const qse_mchar_t* tmp[3]; | ||||
| 		qse_mchar_t* tr; | ||||
| 
 | ||||
| 		tmp[0] = docroot;  | ||||
| 		tmp[1] = suffix;  | ||||
| 		tmp[2] = QSE_NULL; | ||||
| 
 | ||||
| 		tr = qse_mbsadup  (tmp, QSE_NULL, httpd->mmgr); | ||||
| 		if (tr)  | ||||
| 		{ | ||||
| 			qse_canonmbspath (tr, tr, 0); | ||||
| 			qse_env_insertmbs (env, QSE_MT("PATH_TRANSLATED"), tr); | ||||
| 			QSE_MMGR_FREE (httpd->mmgr, tr); | ||||
| 		} | ||||
| 		qse_env_insertmbs (env, QSE_MT("PATH_INFO"), suffix); | ||||
| 		qse_env_insertmbsa (env, QSE_MT("PATH_TRANSLATED"), tmp); | ||||
| 	} | ||||
| 
 | ||||
| 	qse_env_insertmbs (env, QSE_MT("REQUEST_METHOD"), qse_htre_getqmethodname(req)); | ||||
| @ -18,6 +18,7 @@ | ||||
|     License along with QSE. If not, see <htrd://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #include <qse/http/std.h> | ||||
| #include "httpd.h" | ||||
| #include "../cmn/mem.h" | ||||
| #include <qse/cmn/hton.h> | ||||
| @ -90,35 +91,18 @@ | ||||
| #define DEFAULT_PORT        80 | ||||
| #define DEFAULT_SECURE_PORT 443 | ||||
| 
 | ||||
| enum server_xtn_cfg_id_t | ||||
| { | ||||
| 	SERVER_XTN_CFG_DOCROOT = 0, | ||||
| 	SERVER_XTN_CFG_REALM, | ||||
| 	SERVER_XTN_CFG_AUTH, /* basic auth */ | ||||
| 	SERVER_XTN_CFG_DIRCSS, /* can't be too long due to internal buffer size  */ | ||||
| 	SERVER_XTN_CFG_ERRCSS, | ||||
| 	SERVER_XTN_CFG_MAX | ||||
| }; | ||||
| 
 | ||||
| typedef struct server_xtn_t server_xtn_t; | ||||
| struct server_xtn_t | ||||
| { | ||||
| 	qse_mchar_t* cfg[SERVER_XTN_CFG_MAX]; | ||||
| 
 | ||||
| 	union | ||||
| 	{ | ||||
| 		void* a[4]; | ||||
| 		struct | ||||
| 		{ | ||||
| 			qse_httpd_server_cbstd_t*    cbstd; | ||||
| 			qse_httpd_server_cgistd_t*   cgistd; | ||||
| 			qse_httpd_server_mimestd_t*  mimestd; | ||||
| 			qse_httpd_server_idxstd_t*   idxstd; | ||||
| 		} s; | ||||
| 	} cfg2; | ||||
| 
 | ||||
| 	/* private */ | ||||
| 	qse_httpd_server_predetach_t predetach; | ||||
| 	qse_httpd_server_reconfig_t reconfig; | ||||
| 
 | ||||
| 	qse_httpd_serverstd_query_t query; | ||||
| 	qse_httpd_serverstd_makersrc_t makersrc; | ||||
| 	qse_httpd_serverstd_freersrc_t freersrc; | ||||
| 
 | ||||
| 	/* temporary buffer to handle authorization */ | ||||
| 	qse_mxstr_t auth; | ||||
| }; | ||||
| 
 | ||||
| /* ------------------------------------------------------------------- */ | ||||
| @ -640,7 +624,7 @@ static int server_open (qse_httpd_t* httpd, qse_httpd_server_t* server) | ||||
| 	qse_skad_t addr; | ||||
| 	int addrsize; | ||||
| 
 | ||||
| 	addrsize = qse_nwadtoskad (&server->nwad, &addr); | ||||
| 	addrsize = qse_nwadtoskad (&server->dope.nwad, &addr); | ||||
| 	if (addrsize <= -1) | ||||
| 	{ | ||||
| 		qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL); | ||||
| @ -694,13 +678,13 @@ static int server_open (qse_httpd_t* httpd, qse_httpd_server_t* server) | ||||
| 	setsockopt (fd, SOL_IP, IP_TRANSPARENT, &flag, QSE_SIZEOF(flag)); | ||||
| 	#endif | ||||
| 
 | ||||
| 	if (server->flags & QSE_HTTPD_SERVER_BINDTONWIF) | ||||
| 	if (server->dope.flags & QSE_HTTPD_SERVER_BINDTONWIF) | ||||
| 	{ | ||||
| 	#if defined(SO_BINDTODEVICE) | ||||
| 		qse_mchar_t tmp[64]; | ||||
| 		qse_size_t len; | ||||
| 
 | ||||
| 		len = qse_nwifindextombs (server->nwif, tmp, QSE_COUNTOF(tmp)); | ||||
| 		len = qse_nwifindextombs (server->dope.nwif, tmp, QSE_COUNTOF(tmp)); | ||||
| 
 | ||||
| 		if (len <= 0 || setsockopt (fd, SOL_SOCKET, SO_BINDTODEVICE, tmp, len) <= -1) | ||||
| 		{ | ||||
| @ -815,7 +799,7 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: too many client?\n")); | ||||
| 	if (qse_skadtonwad (&addr, &client->remote_addr) <= -1) | ||||
| 	{ | ||||
| /* TODO: logging */ | ||||
| 		client->remote_addr = server->nwad; | ||||
| 		client->remote_addr = server->dope.nwad; | ||||
| 	} | ||||
| 
 | ||||
| 	addrlen = QSE_SIZEOF(addr); | ||||
| @ -823,7 +807,7 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: too many client?\n")); | ||||
| 	    qse_skadtonwad (&addr, &client->local_addr) <= -1) | ||||
| 	{ | ||||
| /* TODO: logging */ | ||||
| 		client->local_addr = server->nwad; | ||||
| 		client->local_addr = server->dope.nwad; | ||||
| 	} | ||||
| 
 | ||||
| 	#if defined(SO_ORIGINAL_DST) | ||||
| @ -1870,7 +1854,7 @@ if (qse_htre_getcontentlen(req) > 0) | ||||
| 					task = qse_httpd_entaskdisconnect (httpd, client, QSE_NULL); | ||||
| 				} | ||||
| 			} | ||||
| 			else if (server_xtn->cfg2.s.cbstd->makersrc (httpd, client, req, &rsrc) <= -1) | ||||
| 			else if (server_xtn->makersrc (httpd, client, req, &rsrc) <= -1) | ||||
| 			{ | ||||
| 				qse_httpd_discardcontent (httpd, req); | ||||
| 				task = qse_httpd_entaskerr (httpd, client, QSE_NULL, 500, req); | ||||
| @ -1878,8 +1862,8 @@ if (qse_htre_getcontentlen(req) > 0) | ||||
| 			else | ||||
| 			{ | ||||
| 				task = qse_httpd_entaskrsrc (httpd, client, QSE_NULL, &rsrc, req); | ||||
| 				if (server_xtn->cfg2.s.cbstd->freersrc)  | ||||
| 					server_xtn->cfg2.s.cbstd->freersrc (httpd, client, req, &rsrc); | ||||
| 				if (server_xtn->freersrc)  | ||||
| 					server_xtn->freersrc (httpd, client, req, &rsrc); | ||||
| 			} | ||||
| 			if (task == QSE_NULL) goto oops; | ||||
| 		} | ||||
| @ -1934,8 +1918,8 @@ static int format_err ( | ||||
| 
 | ||||
| 	server_xtn = qse_httpd_getserverxtn (httpd, client->server); | ||||
| 
 | ||||
| 	css = server_xtn->cfg[SERVER_XTN_CFG_ERRCSS]; | ||||
| 	if (!css) css = QSE_MT(""); | ||||
| 	if (server_xtn->query (httpd, client->server, QSE_NULL, QSE_NULL, QSE_HTTPD_SERVERSTD_ERRCSS, &css) <= -1) css = QSE_NULL; | ||||
| 	if (css == QSE_NULL) css = QSE_MT(""); | ||||
| 
 | ||||
| 	msg = qse_httpstatustombs(code); | ||||
| 
 | ||||
| @ -1972,8 +1956,8 @@ static int format_dir ( | ||||
| 			const qse_mchar_t* css; | ||||
| 			int is_root = (qse_mbscmp (qpath, QSE_MT("/")) == 0); | ||||
| 
 | ||||
| 			css = server_xtn->cfg[SERVER_XTN_CFG_DIRCSS]; | ||||
| 			if (!css) css = QSE_MT(""); | ||||
| 			if (server_xtn->query (httpd, client->server, QSE_NULL, QSE_NULL, QSE_HTTPD_SERVERSTD_DIRCSS, &css) <= -1) css = QSE_NULL; | ||||
| 			if (css == QSE_NULL) css = QSE_MT(""); | ||||
| 
 | ||||
| /* TODO: html escaping of qpath */ | ||||
| 			n = snprintf (buf, bufsz, | ||||
| @ -2119,6 +2103,8 @@ static void free_resource ( | ||||
| 				QSE_MMGR_FREE (httpd->mmgr, (qse_mchar_t*)target->u.cgi.script); | ||||
| 			if (target->u.cgi.path != qpath) | ||||
| 				QSE_MMGR_FREE (httpd->mmgr, (qse_mchar_t*)target->u.cgi.path); | ||||
| 			if (target->u.cgi.shebang) | ||||
| 				QSE_MMGR_FREE (httpd->mmgr, (qse_mchar_t*)target->u.cgi.shebang); | ||||
| 
 | ||||
| 			break; | ||||
| 
 | ||||
| @ -2160,85 +2146,141 @@ static qse_mchar_t* merge_paths ( | ||||
| 	return xpath; | ||||
| } | ||||
| 
 | ||||
| static int attempt_cgi ( | ||||
| 	qse_httpd_t* httpd, const qse_mchar_t* docroot,  | ||||
| 	qse_mchar_t* xpath, const qse_mchar_t* qpath, const qse_mchar_t* idxfile, | ||||
| 	qse_httpd_server_cgistd_t cgistd[], qse_httpd_rsrc_t* target) | ||||
| static void merge_paths_to_buf ( | ||||
| 	qse_httpd_t* httpd, const qse_mchar_t* base,  | ||||
| 	const qse_mchar_t* path, qse_size_t plen, qse_mchar_t* xpath) | ||||
| { | ||||
| 	qse_mchar_t* ext; | ||||
| 	qse_mchar_t* script, * suffix; | ||||
| 	qse_size_t i; | ||||
| 	/* this function merges two path names into a buffer large enough
 | ||||
| 	 * to hold the result. it doesn't duplicate the result */ | ||||
| 	qse_size_t len = 0; | ||||
| 	len += qse_mbscpy (&xpath[len], base); | ||||
| 	len += qse_mbscpy (&xpath[len], QSE_MT("/")); | ||||
| 	len += qse_mbsncpy (&xpath[len], path, plen); | ||||
| 	qse_canonmbspath (xpath, xpath, 0); | ||||
| } | ||||
| 
 | ||||
| 	if (idxfile) | ||||
| struct rsrc_tmp_t | ||||
| { | ||||
| 	const qse_mchar_t* qpath; | ||||
| 	const qse_mchar_t* idxfile; | ||||
| 	qse_mchar_t* xpath; | ||||
| 
 | ||||
| 	const qse_mchar_t* docroot; | ||||
| 	const qse_mchar_t* realm; | ||||
| 	const qse_mchar_t* auth; | ||||
| 	qse_httpd_serverstd_index_t index; | ||||
| 
 | ||||
| 	int final_match; | ||||
| }; | ||||
| 
 | ||||
| static int attempt_cgi ( | ||||
| 	qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req,  | ||||
| 	struct rsrc_tmp_t* tmp, qse_httpd_rsrc_t* target) | ||||
| { | ||||
| 	server_xtn_t* server_xtn; | ||||
| 	qse_mchar_t* shebang = QSE_NULL; | ||||
| 	qse_mchar_t* suffix = QSE_NULL; | ||||
| 	qse_mchar_t* script = QSE_NULL; | ||||
| 	qse_httpd_serverstd_cgi_t cgi; | ||||
| 
 | ||||
| 	server_xtn = qse_httpd_getserverxtn (httpd, client->server); | ||||
| 
 | ||||
| 	if (tmp->final_match) | ||||
| 	{ | ||||
| 		for (i = 0; cgistd[i].ext; i++) | ||||
| 		/* it is a final match. tmp->xpath is tmp->docroot + tmp->qpath  */ | ||||
| 		if (server_xtn->query (httpd, client->server, req, tmp->xpath, QSE_HTTPD_SERVERSTD_CGI, &cgi) >= 0 && cgi.cgi) | ||||
| 		{ | ||||
| 			if (qse_mbsend (idxfile, cgistd[i].ext)) | ||||
| 			if (tmp->idxfile) | ||||
| 			{ | ||||
| 				script = merge_paths (httpd, qpath, idxfile); | ||||
| 				if (script == QSE_NULL) return -1; | ||||
| 
 | ||||
| 				target->type = QSE_HTTPD_RSRC_CGI; | ||||
| 				target->u.cgi.nph = cgistd[i].nph; | ||||
| 				target->u.cgi.path = xpath; | ||||
| 				target->u.cgi.script = script; | ||||
| 				target->u.cgi.suffix = QSE_NULL; | ||||
| 				target->u.cgi.docroot = docroot; | ||||
| 				target->u.cgi.shebang = cgistd[i].shebang; | ||||
| 				return 1; | ||||
| 				script = merge_paths (httpd, tmp->qpath, tmp->idxfile); | ||||
| 				if (script == QSE_NULL) goto oops; | ||||
| 			} | ||||
| 			else script = (qse_mchar_t*)tmp->qpath; | ||||
| 
 | ||||
| 			if (cgi.shebang) | ||||
| 			{ | ||||
| 				shebang = qse_mbsdup (cgi.shebang, httpd->mmgr); | ||||
| 				if (shebang == QSE_NULL) goto oops; | ||||
| 			} | ||||
| 
 | ||||
| 			goto bingo; | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		for (i = 0; cgistd[i].ext; i++) | ||||
| 		/* inspect each segment from the head. */ | ||||
| 		const qse_mchar_t* ptr; | ||||
| 		const qse_mchar_t* slash; | ||||
| 
 | ||||
| 		QSE_ASSERT (tmp->qpath[0] == QSE_T('/')); | ||||
| 
 | ||||
| 		ptr = tmp->qpath + 1; | ||||
| 		while (*ptr != QSE_MT('\0')) | ||||
| 		{ | ||||
| 	/* TODO: attempt other segments if qpath is like
 | ||||
| 	 *       /abc/x.cgi/y.cgi/ttt. currently, it tries x.cgi only. | ||||
| 	 *       x.cgi could be a directory name . | ||||
| 	 */ | ||||
| 			ext = qse_mbsstr (qpath, cgistd[i].ext); | ||||
| 	 | ||||
| 			if (ext && (ext[cgistd[i].len] == QSE_MT('/') ||  | ||||
| 			            ext[cgistd[i].len] == QSE_MT('\0')))  | ||||
| 			slash = qse_mbschr (ptr, QSE_MT('/')); | ||||
| 			if (slash) | ||||
| 			{ | ||||
| 				if (ext[cgistd[i].len] == QSE_MT('/'))  | ||||
| 				if (slash > ptr) | ||||
| 				{ | ||||
| 					/* it has a path suffix */ | ||||
| 					script = qse_mbsxdup (qpath, ext - qpath + cgistd[i].len, httpd->mmgr); | ||||
| 					suffix = qse_mbsdup (&ext[cgistd[i].len], httpd->mmgr); | ||||
| 					if (script == QSE_NULL || suffix == QSE_NULL) | ||||
| 					qse_httpd_stat_t st; | ||||
| 					int stx; | ||||
| 
 | ||||
| 					/* a slash is found and the segment is not empty.
 | ||||
| 					 * | ||||
| 					 * tmp->xpath should be large enough to hold the merge path made of | ||||
| 					 * the subsegments of the original query path and docroot. */ | ||||
| 					merge_paths_to_buf (httpd, tmp->docroot, tmp->qpath, slash - tmp->qpath, tmp->xpath); | ||||
| 	 | ||||
| 					/* attempt this */ | ||||
| 					stx = stat_file (httpd, tmp->xpath, &st, 0); | ||||
| 					if (stx >= 0 && !st.isdir) | ||||
| 					{ | ||||
| 						if (suffix) QSE_MMGR_FREE (httpd->mmgr, suffix); | ||||
| 						if (script) QSE_MMGR_FREE (httpd->mmgr, script); | ||||
| 						httpd->errnum = QSE_HTTPD_ENOMEM; | ||||
| 						return -1; | ||||
| 						if (server_xtn->query (httpd, client->server, req, tmp->xpath, QSE_HTTPD_SERVERSTD_CGI, &cgi) >= 0 && cgi.cgi) | ||||
| 						{ | ||||
| 							script = qse_mbsxdup (tmp->qpath, slash - tmp->qpath , httpd->mmgr); | ||||
| 							suffix = qse_mbsdup (slash, httpd->mmgr); | ||||
| 							if (!script || !suffix) goto oops; | ||||
| 
 | ||||
| 							if (cgi.shebang) | ||||
| 							{ | ||||
| 								shebang = qse_mbsdup (cgi.shebang, httpd->mmgr); | ||||
| 								if (shebang == QSE_NULL) goto oops; | ||||
| 							} | ||||
| 
 | ||||
| 							goto bingo; | ||||
| 						} | ||||
| 					} | ||||
| 	 | ||||
| 					/* drop the suffix part */ | ||||
| 					xpath[qse_mbslen(xpath) - qse_mbslen(suffix)] = QSE_MT('\0'); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					/* it has no path suffix  */ | ||||
| 					script = qpath; | ||||
| 					suffix = QSE_NULL; | ||||
| 				} | ||||
| 	 | ||||
| 				target->type = QSE_HTTPD_RSRC_CGI; | ||||
| 				target->u.cgi.nph = cgistd[i].nph; | ||||
| 				target->u.cgi.path = xpath; | ||||
| 				target->u.cgi.script = script; | ||||
| 				target->u.cgi.suffix = suffix; | ||||
| 				target->u.cgi.docroot = docroot; | ||||
| 				target->u.cgi.shebang = cgistd[i].shebang; | ||||
| 				return 1; | ||||
| 
 | ||||
| 				ptr = slash + 1; | ||||
| 			} | ||||
| 			else  | ||||
| 			{ | ||||
| 				/* no more slash is found. the last segement doesn't have to be checked 
 | ||||
| 				 * here since it's attempted by the caller. */ | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| 	return 0; /* not a cgi */ | ||||
| 
 | ||||
| bingo: | ||||
| 	target->type = QSE_HTTPD_RSRC_CGI; | ||||
| 	target->u.cgi.nph = cgi.nph; | ||||
| 	target->u.cgi.path = tmp->xpath; | ||||
| 	target->u.cgi.script = script; | ||||
| 	target->u.cgi.suffix = suffix; | ||||
| 	target->u.cgi.docroot = tmp->docroot; | ||||
| 	target->u.cgi.shebang = shebang; | ||||
| 	return 1; | ||||
| 
 | ||||
| oops: | ||||
| 	httpd->errnum = QSE_HTTPD_ENOMEM; | ||||
| 	if (shebang) QSE_MMGR_FREE (httpd->mmgr, shebang); | ||||
| 	if (suffix) QSE_MMGR_FREE (httpd->mmgr, suffix); | ||||
| 	if (script && script != tmp->qpath) QSE_MMGR_FREE (httpd->mmgr, script); | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| static int make_resource ( | ||||
| @ -2246,214 +2288,308 @@ static int make_resource ( | ||||
| 	qse_htre_t* req, qse_httpd_rsrc_t* target) | ||||
| { | ||||
| 	server_xtn_t* server_xtn; | ||||
| 	const qse_mchar_t* qpath; | ||||
| 	const qse_mchar_t* idxfile; | ||||
| 	qse_mchar_t* xpath;  | ||||
| 	struct rsrc_tmp_t tmp; | ||||
| 
 | ||||
| 	qse_httpd_stat_t st; | ||||
| 	qse_size_t i; | ||||
| 	int n, stx; | ||||
| 
 | ||||
| 	qpath = qse_htre_getqpath(req); | ||||
| 	QSE_MEMSET (&tmp, 0, QSE_SIZEOF(tmp)); | ||||
| 	tmp.qpath = qse_htre_getqpath(req); | ||||
| 
 | ||||
| 	QSE_MEMSET (target, 0, QSE_SIZEOF(*target)); | ||||
| 
 | ||||
| qse_printf (QSE_T(">>> MAKING RESOURCE [%hs]\n"), qpath); | ||||
| qse_printf (QSE_T(">>> MAKING RESOURCE [%hs]\n"), tmp.qpath); | ||||
| 	server_xtn = qse_httpd_getserverxtn (httpd, client->server); | ||||
| 
 | ||||
| 	if (server_xtn->cfg[SERVER_XTN_CFG_REALM] && | ||||
| 	    server_xtn->cfg[SERVER_XTN_CFG_AUTH]) | ||||
| 	if (server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_DOCROOT, &tmp.docroot) <= -1 || | ||||
| 	    server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_REALM, &tmp.realm) <= -1 || | ||||
| 	    server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_AUTH, &tmp.auth) <= -1 || | ||||
| 	    server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_INDEX, &tmp.index) <= -1) | ||||
| 	{ | ||||
| 		const qse_htre_hdrval_t* auth; | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 		auth = qse_htre_getheaderval (req, QSE_MT("Authorization")); | ||||
| 		if (auth) | ||||
| 	/* default to the root directory. */ | ||||
| 	if (!tmp.docroot) tmp.docroot = QSE_MT("/");  | ||||
| 
 | ||||
| 	if (tmp.realm && tmp.auth) | ||||
| 	{ | ||||
| 		const qse_htre_hdrval_t* authv; | ||||
| 
 | ||||
| 		authv = qse_htre_getheaderval (req, QSE_MT("Authorization")); | ||||
| 		if (authv) | ||||
| 		{ | ||||
| 			while (auth->next) auth = auth->next; | ||||
| 			while (authv->next) authv = authv->next; | ||||
| 
 | ||||
| 			if (qse_mbszcasecmp(auth->ptr, QSE_MT("Basic "), 6) == 0)  | ||||
| 			if (qse_mbszcasecmp(authv->ptr, QSE_MT("Basic "), 6) == 0)  | ||||
| 			{ | ||||
| 				if (qse_mbscmp (&auth->ptr[6], server_xtn->cfg[SERVER_XTN_CFG_AUTH]) == 0) goto auth_ok; | ||||
| 				qse_size_t authl, authl2; | ||||
| 
 | ||||
| 				/* basic authorization is a base64-encoded string of username:password. */ | ||||
| 
 | ||||
| 				authl = qse_mbslen(&authv->ptr[6]);	 | ||||
| 				if (authl > server_xtn->auth.len) | ||||
| 				{ | ||||
| 					qse_mchar_t* tmp; | ||||
| 					tmp = qse_httpd_reallocmem (httpd, server_xtn->auth.ptr, authl * QSE_SIZEOF(qse_mchar_t)); | ||||
| 					if (!tmp) return -1; | ||||
| 
 | ||||
| 					server_xtn->auth.ptr = tmp; | ||||
| 					/* the maximum capacity that can hold the largest authorization value */ | ||||
| 					server_xtn->auth.len = authl;	  | ||||
| 				} | ||||
| 
 | ||||
| 				/* decoding a base64-encoded string result in a shorter value than the input.
 | ||||
| 				 * so passing the length of the input(authl) as the output buffer size is ok */ | ||||
| 				authl2 = qse_debase64 (&authv->ptr[6], authl, server_xtn->auth.ptr, authl, QSE_NULL); | ||||
| 				if (qse_mbsxcmp (server_xtn->auth.ptr, authl2, tmp.auth) == 0) goto auth_ok; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		target->type = QSE_HTTPD_RSRC_AUTH; | ||||
| 		target->u.auth.realm = server_xtn->cfg[SERVER_XTN_CFG_REALM]; | ||||
| 		target->u.auth.realm = tmp.realm;  | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| auth_ok: | ||||
| 	idxfile = QSE_NULL; | ||||
| 	xpath = merge_paths (httpd, server_xtn->cfg[SERVER_XTN_CFG_DOCROOT], qpath); | ||||
| 	if (xpath == QSE_NULL) return -1; | ||||
| 	tmp.xpath = merge_paths (httpd, tmp.docroot, tmp.qpath); | ||||
| 	if (tmp.xpath == QSE_NULL) return -1; | ||||
| 
 | ||||
| 	stx = stat_file (httpd, xpath, &st, 0); | ||||
| 	stx = stat_file (httpd, tmp.xpath, &st, 0); | ||||
| #if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) | ||||
| 	if (stx <= -1) | ||||
| 	{ | ||||
| 		/* this OS may fail in stat_file() if the path contains the trailing 
 | ||||
| 		 * separator. it's beause of the way FindFirstFile() or DosQueryPathInfo() | ||||
| 		 * is ussed in stat_file(). let me work around it here. */ | ||||
| 		qse_size_t pl = qse_mbslen(xpath); | ||||
| 		if (pl > 1 && xpath[pl - 1] == QSE_MT('/'))  | ||||
| 		 * is used in stat_file(). let me work around it here. */ | ||||
| 		qse_size_t pl = qse_mbslen(tmp.xpath); | ||||
| 		if (pl > 1 && tmp.xpath[pl - 1] == QSE_MT('/'))  | ||||
| 		{ | ||||
| 			xpath[pl-1] = QSE_MT('\0'); | ||||
| 			stx = stat_file (httpd, xpath, &st, 0); | ||||
| 			xpath[pl-1] = QSE_MT('/'); | ||||
| 			tmp.xpath[pl-1] = QSE_MT('\0'); | ||||
| 			stx = stat_file (httpd, tmp.xpath, &st, 0); | ||||
| 			tmp.xpath[pl-1] = QSE_MT('/'); | ||||
| 		} | ||||
| 	} | ||||
| #endif | ||||
| 	if (stx >= 0 && st.isdir) | ||||
| 
 | ||||
| 	if (stx >= 0) | ||||
| 	{ | ||||
| 		/* it is a directory */ | ||||
| 		if (server_xtn->cfg2.s.idxstd) | ||||
| 		{ | ||||
| 			/* try to locate an index file */ | ||||
| 			for (i = 0; server_xtn->cfg2.s.idxstd[i].name; i++) | ||||
| 		/* xpath/qpath is a final match.
 | ||||
| 		 * mark that the segments in the query path don't need inspection. */ | ||||
| 		tmp.final_match = 1; | ||||
| 
 | ||||
| 		if (st.isdir) | ||||
| 		{	 | ||||
| 			/* it is a directory */ | ||||
| 			if (tmp.index.count > 0) | ||||
| 			{ | ||||
| 				qse_mchar_t* tpath; | ||||
| 				/* try to locate an index file */ | ||||
| 				qse_size_t i; | ||||
| 				const qse_mchar_t* ptr; | ||||
| 
 | ||||
| 				tpath = merge_paths (httpd, xpath, server_xtn->cfg2.s.idxstd[i].name); | ||||
| 				if (tpath == QSE_NULL)  | ||||
| 				ptr = tmp.index.files; | ||||
| 				for (i = 0; i < tmp.index.count; i++, ptr += qse_mbslen(ptr) + 1) | ||||
| 				{ | ||||
| 					QSE_MMGR_FREE (httpd->mmgr, xpath); | ||||
| 					return -1; | ||||
| 					qse_mchar_t* tpath; | ||||
| 
 | ||||
| 					tpath = merge_paths (httpd, tmp.xpath, ptr); | ||||
| 					if (tpath == QSE_NULL)  | ||||
| 					{ | ||||
| 						QSE_MMGR_FREE (httpd->mmgr, tmp.xpath); | ||||
| 						return -1; | ||||
| 					} | ||||
| 
 | ||||
| 					if (httpd->scb->file.stat (httpd, tpath, &st) >= 0 && !st.isdir) | ||||
| 					{ | ||||
| 						/* the index file is found */ | ||||
| 						QSE_MMGR_FREE (httpd->mmgr, tmp.xpath); | ||||
| 						tmp.xpath = tpath; | ||||
| 						tmp.idxfile = ptr; | ||||
| 						goto attempt_file; | ||||
| 					}	 | ||||
| 
 | ||||
| 					QSE_MMGR_FREE (httpd->mmgr, tpath); | ||||
| 				} | ||||
| 
 | ||||
| 				if (httpd->scb->file.stat (httpd, tpath, &st) >= 0 && !st.isdir) | ||||
| 				{ | ||||
| 					/* the index file is found */ | ||||
| 					QSE_MMGR_FREE (httpd->mmgr, xpath); | ||||
| 					xpath = tpath; | ||||
| 					idxfile = server_xtn->cfg2.s.idxstd[i].name; | ||||
| 					goto attempt_file; | ||||
| 				}	 | ||||
| 
 | ||||
| 				QSE_MMGR_FREE (httpd->mmgr, tpath); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		target->type = QSE_HTTPD_RSRC_DIR; | ||||
| 		target->u.dir.path = xpath; | ||||
| 			target->type = QSE_HTTPD_RSRC_DIR; | ||||
| 			target->u.dir.path = tmp.xpath; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			/* let me treat it as a file. */ | ||||
| 			goto attempt_file; | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		/* well, stat failed. i don't know if it is a file.
 | ||||
| 		 * i must try each segment in the query path. */ | ||||
| 	attempt_file: | ||||
| 		if (server_xtn->cfg2.s.cgistd) | ||||
| 		/* check if the request can resolve to a cgi script */ | ||||
| 		n = attempt_cgi (httpd, client, req, &tmp, target); | ||||
| 		if (n <= -1)  | ||||
| 		{ | ||||
| 			/* check if the request can resolve to a cgi script */ | ||||
| 			n = attempt_cgi (httpd, server_xtn->cfg[SERVER_XTN_CFG_DOCROOT],  | ||||
| 			                 xpath, qpath, idxfile, server_xtn->cfg2.s.cgistd, target); | ||||
| 			if (n <= -1)  | ||||
| 			{ | ||||
| 				QSE_MMGR_FREE (httpd->mmgr, xpath); | ||||
| 				return -1; | ||||
| 			} | ||||
| 			if (n >= 1) return 0; | ||||
| 			QSE_MMGR_FREE (httpd->mmgr, tmp.xpath); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		if (n >= 1) return 0; | ||||
| 
 | ||||
| 		/* fall back to a normal file. */ | ||||
| 		target->type = QSE_HTTPD_RSRC_FILE; | ||||
| 		target->u.file.path = xpath; | ||||
| 		target->u.file.mime = QSE_NULL; | ||||
| 		if (server_xtn->cfg2.s.mimestd) | ||||
| 		target->u.file.path = tmp.xpath; | ||||
| 
 | ||||
| 		if (server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_MIME, &target->u.file.mime) <= -1) | ||||
| 		{ | ||||
| 			for (i = 0; server_xtn->cfg2.s.mimestd[i].ext; i++) | ||||
| 			{ | ||||
| /* TODO: require the table sorted and so the binary search */ | ||||
| 				if (qse_mbsend (qpath, server_xtn->cfg2.s.mimestd[i].ext)) | ||||
| 					target->u.file.mime = server_xtn->cfg2.s.mimestd[i].type;  | ||||
| 			} | ||||
| 			/* don't care about failure */ | ||||
| 			target->u.file.mime = QSE_NULL; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static qse_httpd_server_cbstd_t server_cbstd = | ||||
| { | ||||
| 	make_resource, | ||||
| 	free_resource	 | ||||
| }; | ||||
| /* ------------------------------------------------------------------- */ | ||||
| 
 | ||||
| static void predetach_server (qse_httpd_t* httpd, qse_httpd_server_t* server) | ||||
| { | ||||
| 	server_xtn_t* server_xtn; | ||||
| 	qse_size_t i; | ||||
| 
 | ||||
| 	server_xtn = (server_xtn_t*) qse_httpd_getserverxtn (httpd, server); | ||||
| 	 | ||||
| 	if (server_xtn->predetach) server_xtn->predetach (httpd, server); | ||||
| 	if (server_xtn->auth.ptr) QSE_MMGR_FREE (httpd->mmgr, server_xtn->auth.ptr); | ||||
| } | ||||
| 
 | ||||
| 	for (i = QSE_COUNTOF(server_xtn->cfg); i > 0; ) | ||||
| static void reconfig_server (qse_httpd_t* httpd, qse_httpd_server_t* server) | ||||
| { | ||||
| 	server_xtn_t* server_xtn; | ||||
| 	server_xtn = (server_xtn_t*) qse_httpd_getserverxtn (httpd, server); | ||||
| 	 | ||||
| 	if (server_xtn->reconfig) server_xtn->reconfig (httpd, server); | ||||
| 
 | ||||
| 	/* nothing more to do here ...  */ | ||||
| } | ||||
| 
 | ||||
| struct mime_tab_t | ||||
| { | ||||
| 	const qse_mchar_t* suffix; | ||||
| 	const qse_mchar_t* type; | ||||
| }; | ||||
| static struct mime_tab_t mimetab[] = | ||||
| { | ||||
| 	{ QSE_MT(".htm"),  QSE_MT("text/html") }, | ||||
| 	{ QSE_MT(".html"), QSE_MT("text/html") }, | ||||
| 	{ QSE_MT(".txt"),  QSE_MT("text/plain") } | ||||
| }; | ||||
| 
 | ||||
| struct cgi_tab_t | ||||
| { | ||||
| 	const qse_mchar_t* suffix; | ||||
| 	qse_httpd_serverstd_cgi_t cgi; | ||||
| }; | ||||
| static struct cgi_tab_t cgitab[] = | ||||
| { | ||||
| 	{ QSE_MT(".cgi"), { 1, 0, QSE_NULL } }, | ||||
| 	{ QSE_MT(".nph"), { 1, 1, QSE_NULL } }, | ||||
| }; | ||||
| 
 | ||||
| static int query_server ( | ||||
| 	qse_httpd_t* httpd, qse_httpd_server_t* server,  | ||||
| 	qse_htre_t* req, const qse_mchar_t* xpath, | ||||
| 	qse_httpd_serverstd_query_code_t code, void* result) | ||||
| { | ||||
| 	qse_size_t i; | ||||
| 
 | ||||
| 	switch (code) | ||||
| 	{ | ||||
| 		if (server_xtn->cfg[--i]) | ||||
| 		case QSE_HTTPD_SERVERSTD_DOCROOT: | ||||
| 		case QSE_HTTPD_SERVERSTD_REALM: | ||||
| 		case QSE_HTTPD_SERVERSTD_AUTH: | ||||
| 		case QSE_HTTPD_SERVERSTD_ERRCSS: | ||||
| 		case QSE_HTTPD_SERVERSTD_DIRCSS: | ||||
| 			*(const qse_mchar_t**)result = QSE_NULL; | ||||
| 			return 0; | ||||
| 
 | ||||
| 		case QSE_HTTPD_SERVERSTD_INDEX: | ||||
| 		{ | ||||
| 			QSE_MMGR_FREE (httpd->mmgr, server_xtn->cfg[i]); | ||||
| 			server_xtn->cfg[i] = QSE_NULL; | ||||
| 			qse_httpd_serverstd_index_t* index = (qse_httpd_serverstd_index_t*)result; | ||||
| 			index->count = 2; | ||||
| 			index->files = QSE_MT("index.html\0index.cgi\0"); | ||||
| 			return 0; | ||||
| 		} | ||||
| 
 | ||||
| 		case QSE_HTTPD_SERVERSTD_CGI: | ||||
| 		{ | ||||
| 			qse_httpd_serverstd_cgi_t* cgi = (qse_httpd_serverstd_cgi_t*)result; | ||||
| 			for (i = 0; i < QSE_COUNTOF(cgitab); i++) | ||||
| 			{ | ||||
| 				if (qse_mbsend (xpath, cgitab[i].suffix)) | ||||
| 				{ | ||||
| 					QSE_MEMCPY (cgi, &cgitab[i].cgi, QSE_SIZEOF(*cgi)); | ||||
| 					return 0; | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			QSE_MEMSET (cgi,0, QSE_SIZEOF(*cgi)); | ||||
| 			return 0; | ||||
| 		} | ||||
| 
 | ||||
| 		case QSE_HTTPD_SERVERSTD_MIME: | ||||
| 			/* TODO: binary search if the table is large */ | ||||
| 			for (i = 0; i < QSE_COUNTOF(mimetab); i++) | ||||
| 			{ | ||||
| 				if (qse_mbsend (xpath, mimetab[i].suffix)) | ||||
| 				{ | ||||
| 					*(const qse_mchar_t**)result = mimetab[i].type; | ||||
| 					return 0; | ||||
| 				} | ||||
| 			}			 | ||||
| 
 | ||||
| 			*(const qse_mchar_t**)result = QSE_NULL; | ||||
| 			return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL); | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| qse_httpd_server_t* qse_httpd_attachserverstd ( | ||||
| 	qse_httpd_t* httpd, const qse_char_t* uri,  | ||||
| 	qse_httpd_server_predetach_t predetach, qse_size_t xtnsize) | ||||
| 	qse_httpd_t* httpd, qse_httpd_serverstd_t* server, qse_size_t xtnsize) | ||||
| { | ||||
| 	qse_httpd_server_t server; | ||||
| 	qse_httpd_server_dope_t dope; | ||||
| 	qse_httpd_server_t* xserver; | ||||
| 	server_xtn_t* server_xtn; | ||||
| 	qse_mxstr_t ba; | ||||
| 	qse_size_t balen2; | ||||
| 
 | ||||
| 	/* memcpy here assumes that the top of the dope structure
 | ||||
| 	 * is common with the server structure */ | ||||
| 	QSE_MEMCPY (&dope, server, QSE_SIZEOF(dope)); | ||||
| 	dope.predetach = predetach_server; /* set my own detaching function */ | ||||
| 	dope.reconfig = reconfig_server; /* set my own detaching function */ | ||||
| 
 | ||||
| 	xserver = qse_httpd_attachserver (httpd, &dope, QSE_SIZEOF(*server_xtn) + xtnsize); | ||||
| 	if (xserver == QSE_NULL) return QSE_NULL; | ||||
| 
 | ||||
| 	server_xtn = qse_httpd_getserverxtn (httpd, xserver); | ||||
| 	server_xtn->predetach = server->predetach; | ||||
| 	server_xtn->reconfig = server->reconfig; | ||||
| 	server_xtn->query = query_server; | ||||
| 	server_xtn->makersrc = make_resource; | ||||
| 	server_xtn->freersrc = free_resource; | ||||
| 
 | ||||
| 	return xserver; | ||||
| } | ||||
| 
 | ||||
| qse_httpd_server_t* qse_httpd_attachserverstdwithuri ( | ||||
| 	qse_httpd_t* httpd, const qse_char_t* uri,  | ||||
| 	qse_httpd_server_predetach_t predetach,  | ||||
| 	qse_httpd_server_reconfig_t reconfig,  | ||||
| 	qse_httpd_serverstd_query_t query,  | ||||
| 	qse_size_t xtnsize) | ||||
| { | ||||
| 	qse_httpd_serverstd_t server; | ||||
| 	qse_uint16_t default_port; | ||||
| 	qse_uri_t xuri; | ||||
| 
 | ||||
| 
 | ||||
| 	static qse_httpd_server_cgistd_t server_cgistd[] =  | ||||
| 	{ | ||||
| 		{ QSE_MT(".cgi"), 4, 0, QSE_NULL }, | ||||
| 		{ QSE_MT(".nph"), 4, 1, QSE_NULL }, | ||||
| 		{ QSE_NULL,       0, 0, QSE_NULL } | ||||
| 	}; | ||||
| 
 | ||||
| 	static qse_httpd_server_mimestd_t server_mimestd[] = | ||||
| 	{ | ||||
| 		{ QSE_MT(".html"), QSE_MT("text/html") }, | ||||
| 		{ QSE_MT(".htm"),  QSE_MT("text/htm") }, | ||||
| 		{ QSE_MT(".txt"),  QSE_MT("text/plain") }, | ||||
| 		{ QSE_MT(".log"),  QSE_MT("text/plain") }, | ||||
| 		{ QSE_MT(".css"),  QSE_MT("text/css") }, | ||||
| 		{ QSE_MT(".xml"),  QSE_MT("text/xml") }, | ||||
| 		{ QSE_MT(".js"),   QSE_MT("application/javascript") }, | ||||
| 		{ QSE_MT(".jpg"),  QSE_MT("image/jpeg") }, | ||||
| 		{ QSE_MT(".png"),  QSE_MT("image/png") }, | ||||
| 		{ QSE_MT(".mp4"),  QSE_MT("video/mp4") }, | ||||
| 		{ QSE_MT(".mp3"),  QSE_MT("audio/mpeg") },  | ||||
| 		{ QSE_MT(".c"),    QSE_MT("text/plain") },  | ||||
| 		{ QSE_MT(".h"),    QSE_MT("text/plain") }, | ||||
| 		{ QSE_MT(".cpp"),  QSE_MT("text/plain") },  | ||||
| 		{ QSE_MT(".hpp"),  QSE_MT("text/plain") }, | ||||
| 		{ QSE_NULL,        QSE_NULL } | ||||
| 	}; | ||||
| 
 | ||||
| #if defined(QSE_CHAR_IS_MCHAR) | ||||
| 	qse_mcstr_t tmp[4] =  | ||||
| 	{  | ||||
| 		{ QSE_MT(""),  0 }, | ||||
| 		{ QSE_MT(":"), 1 }, | ||||
| 		{ QSE_MT(""),  0 }, | ||||
| 		{ QSE_NULL,    0 }  | ||||
| 	}; | ||||
| #else | ||||
| 	qse_wcstr_t tmp[4] =  | ||||
| 	{  | ||||
| 		{ QSE_WT(""),  0 }, | ||||
| 		{ QSE_WT(":"), 1 }, | ||||
| 		{ QSE_WT(""),  0 }, | ||||
| 		{ QSE_NULL,    0 }  | ||||
| 	}; | ||||
| #endif | ||||
| 
 | ||||
| 	QSE_MEMSET (&server, 0, QSE_SIZEOF(server)); | ||||
| 
 | ||||
| 	if (qse_strtouri (uri, &xuri, QSE_STRTOURI_NOQUERY) <= -1)  goto invalid; | ||||
| @ -2485,93 +2621,25 @@ qse_httpd_server_t* qse_httpd_attachserverstd ( | ||||
| 			server.nwad.u.in6.port = qse_hton16(default_port); | ||||
| 	} | ||||
| 
 | ||||
| 	xserver = qse_httpd_attachserver ( | ||||
| 		httpd, &server, predetach_server, QSE_SIZEOF(*server_xtn) + xtnsize); | ||||
| 	if (xserver == QSE_NULL) return QSE_NULL; | ||||
| 
 | ||||
| 	server_xtn = qse_httpd_getserverxtn (httpd, xserver); | ||||
| 
 | ||||
| 	if (!xuri.path.ptr) | ||||
| 	server.predetach = predetach; | ||||
| 	server.reconfig = reconfig; | ||||
| #if 0 | ||||
| 	server.docroot = xuri.path; | ||||
| 	if (server.docroot.ptr && qse_ismbsdriveabspath((const qse_mchar_t*)server.docroot.ptr + 1)) | ||||
| 	{ | ||||
| 		/* the path part is not specified */ | ||||
| #if defined(QSE_CHAR_IS_MCHAR) | ||||
| 		xuri.path.ptr = QSE_MT("/"); | ||||
| #else | ||||
| 		xuri.path.ptr = QSE_WT("/"); | ||||
| 		/* if the path name is something like /C:/xxx on support platforms ... */ | ||||
| 		server.docroot.ptr++;	 | ||||
| 		server.docroot.len--; | ||||
| 	} | ||||
| 	server.realm = xuri.frag; | ||||
| 	server.user = xuri.auth.user; | ||||
| 	server.pass = xuri.auth.pass; | ||||
| #endif | ||||
| 		xuri.path.len = 1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (xuri.auth.user.ptr) | ||||
| 	{ | ||||
| 		tmp[0].ptr = xuri.auth.user.ptr; | ||||
| 		tmp[0].len = xuri.auth.user.len; | ||||
| 	} | ||||
| 	if (xuri.auth.pass.ptr) | ||||
| 	{ | ||||
| 		tmp[2].ptr = xuri.auth.pass.ptr; | ||||
| 		tmp[2].len = xuri.auth.pass.len; | ||||
| 	} | ||||
| 
 | ||||
| #if defined(QSE_CHAR_IS_MCHAR) | ||||
| 	if (qse_ismbsdriveabspath((const qse_mchar_t*)xuri.path.ptr + 1)) | ||||
| 		server_xtn->cfg[SERVER_XTN_CFG_DOCROOT] = qse_mbsxdup ((const qse_mchar_t*)xuri.path.ptr + 1, xuri.path.len - 1, httpd->mmgr); | ||||
| 	else | ||||
| 		server_xtn->cfg[SERVER_XTN_CFG_DOCROOT] = qse_mbsxdup (xuri.path.ptr, xuri.path.len, httpd->mmgr); | ||||
| 	if (xuri.frag.ptr) server_xtn->cfg[SERVER_XTN_CFG_REALM] = qse_mbsxdup (xuri.frag.ptr, xuri.frag.len, httpd->mmgr); | ||||
| 	ba.ptr = qse_mcstradup (tmp, &ba.len, httpd->mmgr); | ||||
| 	 | ||||
| #else | ||||
| 	if (qse_iswcsdriveabspath((const qse_wchar_t*)xuri.path.ptr + 1)) | ||||
| 		server_xtn->cfg[SERVER_XTN_CFG_DOCROOT] = qse_wcsntombsdup ((const qse_wchar_t*)xuri.path.ptr + 1, xuri.path.len - 1, QSE_NULL, httpd->mmgr); | ||||
| 	else | ||||
| 		server_xtn->cfg[SERVER_XTN_CFG_DOCROOT] = qse_wcsntombsdup (xuri.path.ptr, xuri.path.len, QSE_NULL, httpd->mmgr); | ||||
| 	if (xuri.frag.ptr) server_xtn->cfg[SERVER_XTN_CFG_REALM] = qse_wcsntombsdup (xuri.frag.ptr, xuri.frag.len, QSE_NULL, httpd->mmgr); | ||||
| 	ba.ptr = qse_wcsnatombsdup (tmp, &ba.len, httpd->mmgr); | ||||
| #endif | ||||
| 
 | ||||
| 	if ((!server_xtn->cfg[SERVER_XTN_CFG_DOCROOT]) || | ||||
| 	    (xuri.frag.ptr && !server_xtn->cfg[SERVER_XTN_CFG_REALM]) || | ||||
| 	    !ba.ptr)  | ||||
| 	{ | ||||
| 		if (ba.ptr) QSE_MMGR_FREE (httpd->mmgr, ba.ptr); | ||||
| 		goto nomem_after_attach; | ||||
| 	} | ||||
| 
 | ||||
| 	balen2 = ((ba.len / 3) + 1) * 4; | ||||
| 	server_xtn->cfg[SERVER_XTN_CFG_AUTH] = QSE_MMGR_ALLOC ( | ||||
| 		httpd->mmgr, (balen2 + 1) * QSE_SIZEOF(qse_mchar_t)); | ||||
| 	if (!server_xtn->cfg[SERVER_XTN_CFG_AUTH])  | ||||
| 	{ | ||||
| 		QSE_MMGR_FREE (httpd->mmgr, ba.ptr); | ||||
| 		goto nomem_after_attach; | ||||
| 	} | ||||
| 
 | ||||
| 	qse_enbase64 ( | ||||
| 		ba.ptr, ba.len, | ||||
| 		server_xtn->cfg[SERVER_XTN_CFG_AUTH], | ||||
| 		balen2, | ||||
| 		&balen2 | ||||
| 	); | ||||
| 	QSE_MMGR_FREE (httpd->mmgr, ba.ptr); | ||||
| 	(server_xtn->cfg[SERVER_XTN_CFG_AUTH])[balen2] = QSE_MT('\0'); | ||||
| 
 | ||||
| 	server_xtn->predetach = predetach; | ||||
| 	server_xtn->cfg2.s.cbstd = &server_cbstd; | ||||
| 	server_xtn->cfg2.s.cgistd = server_cgistd; | ||||
| 	server_xtn->cfg2.s.mimestd = server_mimestd; | ||||
| 	server_xtn->cfg2.s.idxstd = QSE_NULL; | ||||
| 
 | ||||
| 	return xserver; | ||||
| 	return qse_httpd_attachserverstd (httpd, &server, xtnsize); | ||||
| 
 | ||||
| invalid: | ||||
| 	httpd->errnum = QSE_HTTPD_EINVAL; | ||||
| 	return QSE_NULL; | ||||
| 
 | ||||
| nomem_after_attach: | ||||
| 	qse_httpd_detachserver (httpd, xserver);	 | ||||
| 	httpd->errnum = QSE_HTTPD_ENOMEM; | ||||
| 	return QSE_NULL; | ||||
| } | ||||
| 
 | ||||
| int qse_httpd_getserveroptstd ( | ||||
| @ -2584,19 +2652,16 @@ int qse_httpd_getserveroptstd ( | ||||
| 
 | ||||
| 	switch (id) | ||||
| 	{ | ||||
| 		case QSE_HTTPD_SERVER_DOCROOT: | ||||
| 		case QSE_HTTPD_SERVER_REALM: | ||||
| 		case QSE_HTTPD_SERVER_AUTH: | ||||
| 		case QSE_HTTPD_SERVER_ERRCSS: | ||||
| 		case QSE_HTTPD_SERVER_DIRCSS: | ||||
| 			*(qse_mchar_t**)value = server_xtn->cfg[id - QSE_HTTPD_SERVER_DOCROOT]; | ||||
| 		case QSE_HTTPD_SERVERSTD_QUERY: | ||||
| 			*(qse_httpd_serverstd_query_t*)value = server_xtn->query; | ||||
| 			return 0; | ||||
| 
 | ||||
| 		case QSE_HTTPD_SERVER_CBSTD: | ||||
| 		case QSE_HTTPD_SERVER_CGISTD: | ||||
| 		case QSE_HTTPD_SERVER_MIMESTD: | ||||
| 		case QSE_HTTPD_SERVER_IDXSTD: | ||||
| 			*(void**)value = (void*)server_xtn->cfg2.a[id - QSE_HTTPD_SERVER_CBSTD]; | ||||
| 		case QSE_HTTPD_SERVERSTD_MAKERSRC: | ||||
| 			*(qse_httpd_serverstd_makersrc_t*)value = server_xtn->makersrc; | ||||
| 			return 0; | ||||
| 
 | ||||
| 		case QSE_HTTPD_SERVERSTD_FREERSRC: | ||||
| 			*(qse_httpd_serverstd_freersrc_t*)value = server_xtn->freersrc; | ||||
| 			return 0; | ||||
| 	}	 | ||||
| 
 | ||||
| @ -2604,47 +2669,27 @@ int qse_httpd_getserveroptstd ( | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int qse_httpd_setserveroptstd ( | ||||
| 	qse_httpd_t* httpd, qse_httpd_server_t* server, | ||||
| 	qse_httpd_server_optstd_t id, const void* value) | ||||
| { | ||||
| 	server_xtn_t* server_xtn; | ||||
| 	qse_mchar_t* mctmp; | ||||
| 
 | ||||
| 	server_xtn = qse_httpd_getserverxtn (httpd, server); | ||||
| 
 | ||||
| 	switch (id) | ||||
| 	{ | ||||
| 		case QSE_HTTPD_SERVER_DOCROOT: | ||||
| 		case QSE_HTTPD_SERVER_REALM: | ||||
| 		case QSE_HTTPD_SERVER_AUTH: | ||||
| 		case QSE_HTTPD_SERVER_ERRCSS: | ||||
| 		case QSE_HTTPD_SERVER_DIRCSS: | ||||
| 			mctmp = (qse_mchar_t*)value; | ||||
| 			if(mctmp) | ||||
| 			{	 | ||||
| 				mctmp = qse_mbsdup ((qse_mchar_t*)mctmp, httpd->mmgr); | ||||
| 				if (mctmp == QSE_NULL) | ||||
| 				{ | ||||
| 					httpd->errnum = QSE_HTTPD_ENOMEM; | ||||
| 					return -1; | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			if (server_xtn->cfg[id - QSE_HTTPD_SERVER_DOCROOT]) | ||||
| 			{ | ||||
| 				QSE_MMGR_FREE (httpd->mmgr,  | ||||
| 					server_xtn->cfg[id - QSE_HTTPD_SERVER_DOCROOT]); | ||||
| 			} | ||||
| 		 | ||||
| 			server_xtn->cfg[id - QSE_HTTPD_SERVER_DOCROOT] = mctmp; | ||||
| 		case QSE_HTTPD_SERVERSTD_QUERY: | ||||
| 			server_xtn->query = (qse_httpd_serverstd_query_t)value; | ||||
| 			return 0; | ||||
| 
 | ||||
| 		case QSE_HTTPD_SERVER_CBSTD: | ||||
| 		case QSE_HTTPD_SERVER_CGISTD: | ||||
| 		case QSE_HTTPD_SERVER_MIMESTD: | ||||
| 		case QSE_HTTPD_SERVER_IDXSTD: | ||||
| 			server_xtn->cfg2.a[id - QSE_HTTPD_SERVER_CBSTD] = value; | ||||
| 		case QSE_HTTPD_SERVERSTD_MAKERSRC: | ||||
| 			server_xtn->makersrc = (qse_httpd_serverstd_makersrc_t)value; | ||||
| 			return 0; | ||||
| 
 | ||||
| 		case QSE_HTTPD_SERVERSTD_FREERSRC: | ||||
| 			server_xtn->freersrc = (qse_httpd_serverstd_freersrc_t)value; | ||||
| 			return 0; | ||||
| 	}	 | ||||
| 
 | ||||
| @ -89,6 +89,11 @@ void qse_httpd_stop (qse_httpd_t* httpd) | ||||
| 	httpd->stopreq = 1; | ||||
| } | ||||
| 
 | ||||
| void qse_httpd_reconfig (qse_httpd_t* httpd) | ||||
| { | ||||
| 	httpd->reconfigreq = 1; | ||||
| } | ||||
| 
 | ||||
| qse_httpd_errnum_t qse_httpd_geterrnum (qse_httpd_t* httpd) | ||||
| { | ||||
| 	return httpd->errnum; | ||||
| @ -159,6 +164,14 @@ QSE_INLINE void* qse_httpd_allocmem (qse_httpd_t* httpd, qse_size_t size) | ||||
| 	return ptr; | ||||
| } | ||||
| 
 | ||||
| QSE_INLINE void* qse_httpd_callocmem (qse_httpd_t* httpd, qse_size_t size) | ||||
| { | ||||
| 	void* ptr = QSE_MMGR_ALLOC (httpd->mmgr, size); | ||||
| 	if (ptr == QSE_NULL) httpd->errnum = QSE_HTTPD_ENOMEM; | ||||
| 	else QSE_MEMSET (ptr, 0, size); | ||||
| 	return ptr; | ||||
| } | ||||
| 
 | ||||
| QSE_INLINE void* qse_httpd_reallocmem ( | ||||
| 	qse_httpd_t* httpd, void* ptr, qse_size_t size) | ||||
| { | ||||
| @ -172,6 +185,34 @@ QSE_INLINE void qse_httpd_freemem (qse_httpd_t* httpd, void* ptr) | ||||
| 	QSE_MMGR_FREE (httpd->mmgr, ptr); | ||||
| } | ||||
| 
 | ||||
| qse_mchar_t* qse_httpd_strtombsdup (qse_httpd_t* httpd, const qse_char_t* str) | ||||
| { | ||||
| 	qse_mchar_t* mptr; | ||||
| 
 | ||||
| #if defined(QSE_CHAR_IS_MCHAR) | ||||
| 	mptr = qse_mbsdup (str, httpd->mmgr); | ||||
| #else | ||||
| 	mptr = qse_wcstombsdup (str, QSE_NULL, httpd->mmgr); | ||||
| #endif | ||||
| 
 | ||||
| 	if (mptr == QSE_NULL) httpd->errnum = QSE_HTTPD_ENOMEM; | ||||
| 	return mptr; | ||||
| } | ||||
| 
 | ||||
| qse_mchar_t* qse_httpd_strntombsdup (qse_httpd_t* httpd, const qse_char_t* str, qse_size_t len) | ||||
| { | ||||
| 	qse_mchar_t* mptr; | ||||
| 
 | ||||
| #if defined(QSE_CHAR_IS_MCHAR) | ||||
| 	mptr = qse_mbsxdup (str, len, httpd->mmgr); | ||||
| #else | ||||
| 	mptr = qse_wcsntombsdup (str, len, QSE_NULL, httpd->mmgr); | ||||
| #endif | ||||
| 
 | ||||
| 	if (mptr == QSE_NULL) httpd->errnum = QSE_HTTPD_ENOMEM; | ||||
| 	return mptr; | ||||
| } | ||||
| 
 | ||||
| /* --------------------------------------------------- */ | ||||
| 
 | ||||
| static qse_httpd_task_t* enqueue_task ( | ||||
| @ -434,15 +475,15 @@ static int accept_client ( | ||||
| 		{ | ||||
| /* TODO: proper logging */ | ||||
| qse_char_t tmp[128]; | ||||
| qse_nwadtostr (&server->nwad, tmp, QSE_COUNTOF(tmp), QSE_NWADTOSTR_ALL); | ||||
| qse_printf (QSE_T("failed to accept from server %s\n"), tmp); | ||||
| qse_nwadtostr (&server->dope.nwad, tmp, QSE_COUNTOF(tmp), QSE_NWADTOSTR_ALL); | ||||
| qse_printf (QSE_T("failed to accept from server [%s] [%d]\n"), tmp, server->handle.i); | ||||
| 
 | ||||
| 			return -1; | ||||
| 		} | ||||
| 
 | ||||
| /* TODO: check maximum number of client. if exceed call client.close */ | ||||
| 
 | ||||
| 		if (server->flags & QSE_HTTPD_SERVER_SECURE) clibuf.status |= CLIENT_SECURE; | ||||
| 		if (server->dope.flags & QSE_HTTPD_SERVER_SECURE) clibuf.status |= CLIENT_SECURE; | ||||
| 		clibuf.server = server; | ||||
| 
 | ||||
| 		client = new_client (httpd, &clibuf); | ||||
| @ -496,11 +537,11 @@ static void deactivate_servers (qse_httpd_t* httpd) | ||||
| 
 | ||||
| 	for (server = httpd->server.list.head; server; server = server->next) | ||||
| 	{ | ||||
| 		if (server->flags & QSE_HTTPD_SERVER_ACTIVE) | ||||
| 		if (server->dope.flags & QSE_HTTPD_SERVER_ACTIVE) | ||||
| 		{ | ||||
| 			httpd->scb->mux.delhnd (httpd, httpd->mux, server->handle); | ||||
| 			httpd->scb->server.close (httpd, server); | ||||
| 			server->flags &= ~QSE_HTTPD_SERVER_ACTIVE; | ||||
| 			server->dope.flags &= ~QSE_HTTPD_SERVER_ACTIVE; | ||||
| 			httpd->server.nactive--; | ||||
| 		} | ||||
| 	} | ||||
| @ -515,13 +556,12 @@ static int activate_servers (qse_httpd_t* httpd) | ||||
| 		if (httpd->scb->server.open (httpd, server) <= -1) | ||||
| 		{ | ||||
| 			qse_char_t buf[64]; | ||||
| 			qse_nwadtostr (&server->nwad,  | ||||
| 				buf, QSE_COUNTOF(buf), QSE_NWADTOSTR_ALL); | ||||
| 			qse_nwadtostr (&server->dope.nwad, buf, QSE_COUNTOF(buf), QSE_NWADTOSTR_ALL); | ||||
| 
 | ||||
| /*
 | ||||
| 			httpd->rcb->log (httpd, 0,  | ||||
| 				QSE_T("cannot activate %s"), buf); | ||||
| 			httpd->rcb->log (httpd, 0, QSE_T("cannot activate %s"), buf); | ||||
| */ | ||||
| qse_printf(QSE_T("cannot activate [%s]\n"), buf); | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| @ -529,18 +569,17 @@ static int activate_servers (qse_httpd_t* httpd) | ||||
| 			httpd, httpd->mux, server->handle, QSE_HTTPD_MUX_READ, server) <= -1) | ||||
| 		{ | ||||
| 			qse_char_t buf[64]; | ||||
| 			qse_nwadtostr (&server->nwad,  | ||||
| 				buf, QSE_COUNTOF(buf), QSE_NWADTOSTR_ALL); | ||||
| 			qse_nwadtostr (&server->dope.nwad, buf, QSE_COUNTOF(buf), QSE_NWADTOSTR_ALL); | ||||
| /*
 | ||||
| 			httpd->rcb->log (httpd, 0,  | ||||
| 				QSE_T("cannot activate %s - "), buf); | ||||
| 			httpd->rcb->log (httpd, 0, QSE_T("cannot activate %s - "), buf); | ||||
| */ | ||||
| qse_printf(QSE_T("cannot add handle [%s]\n"), buf); | ||||
| 
 | ||||
| 			httpd->scb->server.close (httpd, server); | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		server->flags |= QSE_HTTPD_SERVER_ACTIVE; | ||||
| 		server->dope.flags |= QSE_HTTPD_SERVER_ACTIVE; | ||||
| 		httpd->server.nactive++; | ||||
| 	} | ||||
| 
 | ||||
| @ -566,20 +605,18 @@ static void free_server_list (qse_httpd_t* httpd) | ||||
| } | ||||
| 
 | ||||
| qse_httpd_server_t* qse_httpd_attachserver ( | ||||
| 	qse_httpd_t* httpd, const qse_httpd_server_t* tmpl,  | ||||
| 	qse_httpd_server_predetach_t predetach, qse_size_t xtnsize) | ||||
| 	qse_httpd_t* httpd, const qse_httpd_server_dope_t* dope, qse_size_t xtnsize) | ||||
| { | ||||
| 	qse_httpd_server_t* server; | ||||
| 
 | ||||
| 	server = qse_httpd_allocmem (httpd, QSE_SIZEOF(*server) + xtnsize); | ||||
| 	server = qse_httpd_callocmem (httpd, QSE_SIZEOF(*server) + xtnsize); | ||||
| 	if (server == QSE_NULL) return QSE_NULL; | ||||
| 
 | ||||
| 	QSE_MEMCPY (server, tmpl, QSE_SIZEOF(*server));  | ||||
| 	QSE_MEMSET (server + 1, 0, xtnsize); | ||||
| 
 | ||||
| 	server->type = QSE_HTTPD_SERVER; | ||||
| 	server->flags &= ~QSE_HTTPD_SERVER_ACTIVE; | ||||
| 	server->predetach = predetach; | ||||
| 	/* copy the server dope */ | ||||
| 	server->dope = *dope; | ||||
| 	/* and correct some fields in case the dope contains invalid stuffs */ | ||||
| 	server->dope.flags &= ~QSE_HTTPD_SERVER_ACTIVE; | ||||
| 
 | ||||
| 	/* chain the server to the tail of the list */ | ||||
| 	server->prev = httpd->server.list.tail; | ||||
| @ -601,9 +638,9 @@ void qse_httpd_detachserver (qse_httpd_t* httpd, qse_httpd_server_t* server) | ||||
| 	prev = server->prev; | ||||
| 	next = server->next; | ||||
| 
 | ||||
| 	QSE_ASSERT (!(server->flags & QSE_HTTPD_SERVER_ACTIVE)); | ||||
| 	QSE_ASSERT (!(server->dope.flags & QSE_HTTPD_SERVER_ACTIVE)); | ||||
| 
 | ||||
| 	if (server->predetach) server->predetach (httpd, server); | ||||
| 	if (server->dope.predetach) server->dope.predetach (httpd, server); | ||||
| 
 | ||||
| 	qse_httpd_freemem (httpd, server); | ||||
| 	httpd->server.navail--; | ||||
| @ -1112,11 +1149,24 @@ qse_printf (QSE_T("MUX ADDHND CLIENT RW(ENTASK) %d\n"), client->handle.i); | ||||
| static int dispatch_mux ( | ||||
| 	qse_httpd_t* httpd, void* mux, qse_ubi_t handle, int mask, void* cbarg) | ||||
| { | ||||
| 	return ((qse_httpd_server_t*)cbarg)->type == QSE_HTTPD_SERVER? | ||||
| 	return ((qse_httpd_mate_t*)cbarg)->type == QSE_HTTPD_SERVER? | ||||
| 		accept_client (httpd, mux, handle, mask, cbarg): | ||||
| 		perform_client_task (httpd, mux, handle, mask, cbarg); | ||||
| } | ||||
| 
 | ||||
| static void reconfig_servers (qse_httpd_t* httpd) | ||||
| { | ||||
| 	qse_httpd_server_t* server; | ||||
| 
 | ||||
| 	for (server = httpd->server.list.head; server; server = server->next) | ||||
| 	{ | ||||
| 		if (server->dope.flags & QSE_HTTPD_SERVER_ACTIVE) | ||||
| 		{ | ||||
| 			if (server->dope.reconfig) server->dope.reconfig (httpd, server); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| int qse_httpd_loop ( | ||||
| 	qse_httpd_t* httpd, qse_httpd_scb_t* scb, | ||||
| 	qse_httpd_rcb_t* rcb, const qse_ntime_t* tmout) | ||||
| @ -1166,12 +1216,21 @@ int qse_httpd_loop ( | ||||
| 		count = httpd->scb->mux.poll (httpd, httpd->mux, tmout); | ||||
| 		if (count <= -1)  | ||||
| 		{ | ||||
| 			xret = -1;  | ||||
| 			break; | ||||
| 			if (httpd->errnum != QSE_HTTPD_EINTR) | ||||
| 			{ | ||||
| 				xret = -1;  | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		purge_bad_clients (httpd); | ||||
| 		purge_idle_clients (httpd); | ||||
| 
 | ||||
| 		if (httpd->reconfigreq) | ||||
| 		{ | ||||
| 			reconfig_servers (httpd);	 | ||||
| 			httpd->reconfigreq = 0; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	purge_client_list (httpd); | ||||
| @ -18,12 +18,12 @@ | ||||
|     License along with QSE. If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _QSE_LIB_NET_HTTPD_H_ | ||||
| #define _QSE_LIB_NET_HTTPD_H_ | ||||
| #ifndef _QSE_LIB_HTTP_HTTPD_H_ | ||||
| #define _QSE_LIB_HTTP_HTTPD_H_ | ||||
| 
 | ||||
| /* private header file for httpd */ | ||||
| 
 | ||||
| #include <qse/net/httpd.h> | ||||
| #include <qse/http/httpd.h> | ||||
| 
 | ||||
| #include <qse/cmn/stdio.h> /* TODO: remove this.. only for debugging at this moment */ | ||||
| 
 | ||||
| @ -39,7 +39,8 @@ struct qse_httpd_t | ||||
| 	{ | ||||
| 		int trait; | ||||
| 	} opt; | ||||
| 	int stopreq; | ||||
| 	int stopreq: 1; | ||||
| 	int reconfigreq: 1; | ||||
| 
 | ||||
| 	qse_mchar_t sname[128]; /* server name for the server header */ | ||||
| 	qse_mchar_t gtbuf[10][64]; /* GMT time buffers */ | ||||
| @ -21,7 +21,7 @@ | ||||
| #ifndef _QSE_LIB_NET_UPXD_H_ | ||||
| #define _QSE_LIB_NET_UPXD_H_ | ||||
| 
 | ||||
| #include <qse/net/upxd.h> | ||||
| #include <qse/http/upxd.h> | ||||
| #include "../cmn/mem.h" | ||||
| 
 | ||||
| typedef struct qse_upxd_server_session_t qse_upxd_server_session_t; | ||||
| @ -39,10 +39,16 @@ const qse_char_t* qse_xli_dflerrstr ( | ||||
| 		QSE_T("I/O error with file '${0}'"), | ||||
| 		QSE_T("error returned by user I/O handler"), | ||||
|  | ||||
| 		QSE_T("syntax error"), | ||||
| 		QSE_T("semicolon expected in place of '${0}'"), | ||||
| 		QSE_T("left-brace or equal-sign expected in place of '${0}'"), | ||||
| 		QSE_T("right-brace expected in place of '${0}'"), | ||||
| 		QSE_T("pair value expected in place of '${0}'") | ||||
| 		QSE_T("pair value expected in place of '${0}'"), | ||||
| 		QSE_T("string not closed"), | ||||
| 		QSE_T("'@include' not followed by a string"), | ||||
| 		QSE_T("invalid character '${0}'"), | ||||
| 		QSE_T("'${0}' not recognized"), | ||||
| 		QSE_T("@ not followed by a valid word") | ||||
| 	}; | ||||
|  | ||||
| 	return (errnum >= 0 && errnum < QSE_COUNTOF(errstr))? | ||||
|  | ||||
| @ -48,6 +48,7 @@ enum tok_t | ||||
| 	TOK_LBRACE, | ||||
| 	TOK_RBRACE, | ||||
| 	TOK_EQ, | ||||
| 	TOK_COMMA, | ||||
| 	TOK_DQSTR, | ||||
| 	TOK_SQSTR, | ||||
| 	TOK_IDENT, | ||||
| @ -221,6 +222,7 @@ static int get_symbols (qse_xli_t* xli, qse_cint_t c, qse_xli_tok_t* tok) | ||||
| 	static struct ops_t ops[] =  | ||||
| 	{ | ||||
| 		{ QSE_T("="),   1, TOK_EQ          }, | ||||
| 		{ QSE_T(","),   1, TOK_COMMA       }, | ||||
| 		{ QSE_T(";"),   1, TOK_SEMICOLON   }, | ||||
| 		{ QSE_T("{"),   1, TOK_LBRACE      }, | ||||
| 		{ QSE_T("}"),   1, TOK_RBRACE      }, | ||||
| @ -310,9 +312,7 @@ static int begin_include (qse_xli_t* xli) | ||||
| 	); | ||||
| 	if (pair == QSE_NULL) | ||||
| 	{ | ||||
| #if 0 | ||||
| 		SETERR_LOC (xli, QSE_XLI_ENOMEM, &xli->ptok.loc); | ||||
| #endif | ||||
| 		qse_xli_seterror (xli, QSE_XLI_ENOMEM, QSE_NULL, &xli->tok.loc); | ||||
| 		goto oops; | ||||
| 	} | ||||
|  | ||||
| @ -320,13 +320,7 @@ static int begin_include (qse_xli_t* xli) | ||||
| 	QSE_HTB_VLEN(pair) = QSE_HTB_KLEN(pair);*/ | ||||
|  | ||||
| 	arg = (qse_xli_io_arg_t*) qse_xli_callocmem (xli, QSE_SIZEOF(*arg)); | ||||
| 	if (arg == QSE_NULL) | ||||
| 	{ | ||||
| #if 0 | ||||
| 		ADJERR_LOC (xli, &xli->ptok.loc); | ||||
| #endif | ||||
| 		goto oops; | ||||
| 	} | ||||
| 	if (arg == QSE_NULL) goto oops; | ||||
|  | ||||
| 	arg->flags = QSE_XLI_IO_INCLUDED; | ||||
| 	arg->name = QSE_HTB_KPTR(pair); | ||||
| @ -411,9 +405,7 @@ retry: | ||||
| 		{ | ||||
| 			/* this directive is empty,  | ||||
| 			 * not followed by a valid word */ | ||||
| #if  0 | ||||
| 			SETERR_LOC (xli, QSE_XLI_EXKWEM, &(xli)->tok.loc); | ||||
| #endif | ||||
| 			qse_xli_seterror (xli, QSE_XLI_EXKWEM, QSE_NULL, &xli->tok.loc); | ||||
| 			return -1; | ||||
| 		} | ||||
|  | ||||
| @ -429,9 +421,7 @@ retry: | ||||
| 		if (type == TOK_IDENT) | ||||
| 		{ | ||||
| 			/* this directive is not recognized */ | ||||
| #if 0 | ||||
| 			SETERR_TOK (xli, QSE_XLI_EXKWNR); | ||||
| #endif | ||||
| 			qse_xli_seterror (xli, QSE_XLI_EXKWNR, QSE_STR_CSTR(xli->tok.name), &xli->tok.loc); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		SET_TOKEN_TYPE (xli, tok, type); | ||||
| @ -465,9 +455,7 @@ retry: | ||||
| 			if (c == QSE_CHAR_EOF) | ||||
| 			{ | ||||
| 				/* the string is not closed */ | ||||
| #if 0 | ||||
| 				SETERR_TOK (xli, QSE_XLI_ESTRNC); | ||||
| #endif | ||||
| 				qse_xli_seterror (xli, QSE_XLI_ESTRNC, QSE_NULL, &xli->tok.loc); | ||||
| 				return -1; | ||||
| 			} | ||||
|  | ||||
| @ -490,18 +478,18 @@ retry: | ||||
| 			/* not handled yet */ | ||||
| 			if (c == QSE_T('\0')) | ||||
| 			{ | ||||
| #if 0 | ||||
| 				SETERR_ARG_LOC ( | ||||
| 					xli, QSE_XLI_ELXCHR, | ||||
| 					QSE_T("<NUL>"), 5, &tok->loc); | ||||
| #endif | ||||
| 				qse_cstr_t ea; | ||||
| 				ea.ptr = QSE_T("<NUL>"); | ||||
| 				ea.len = 5; | ||||
| 				qse_xli_seterror (xli, QSE_XLI_ELXCHR, &ea, &tok->loc); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| #if 0 | ||||
| 				qse_char_t cc = (qse_char_t)c; | ||||
| 				SETERR_ARG_LOC (xli, QSE_XLI_ELXCHR, &cc, 1, &tok->loc); | ||||
| #endif | ||||
| 				qse_cstr_t ea; | ||||
| 				ea.ptr = &cc; | ||||
| 				ea.len = 1; | ||||
| 				qse_xli_seterror (xli, QSE_XLI_ELXCHR, &ea, &tok->loc); | ||||
| 			} | ||||
| 			return -1; | ||||
| 		} | ||||
| @ -518,9 +506,7 @@ retry: | ||||
| 	if (skip_semicolon_after_include) | ||||
| 	{ | ||||
| 		/* semiclon has not been skipped yet */ | ||||
| #if 0 | ||||
| 		qse_xli_seterror (xli, QSE_XLI_ESCOLON, QSE_STR_CSTR(tok->name), &tok->loc); | ||||
| #endif | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| @ -538,7 +524,7 @@ static int read_pair (qse_xli_t* xli, qse_xli_list_t* list) | ||||
| 	qse_char_t* name = QSE_NULL; | ||||
| 	qse_xli_pair_t* pair; | ||||
|  | ||||
| 	if (xli->opt.trait & QSE_XLI_NODUPKEY) | ||||
| 	if (xli->opt.trait & QSE_XLI_KEYNODUP) | ||||
| 	{ | ||||
| 		qse_xli_atom_t* atom; | ||||
|  | ||||
| @ -566,10 +552,10 @@ static int read_pair (qse_xli_t* xli, qse_xli_list_t* list) | ||||
|  | ||||
| 	if (get_token (xli) <= -1) goto oops; | ||||
|  | ||||
| 	if  (xli->opt.trait & QSE_XLI_NAMEDKEY) | ||||
| 	if  (xli->opt.trait & QSE_XLI_KEYNAME) | ||||
| 	{ | ||||
| 		/* the name part must be unique for the same key(s) */ | ||||
| 		if (MATCH (xli, TOK_SQSTR) || MATCH(xli, TOK_DQSTR)) | ||||
| 		if (MATCH (xli, TOK_IDENT) || MATCH (xli, TOK_DQSTR) || MATCH (xli, TOK_SQSTR)) | ||||
| 		{ | ||||
| 			qse_xli_atom_t* atom; | ||||
|  | ||||
| @ -602,14 +588,42 @@ static int read_pair (qse_xli_t* xli, qse_xli_list_t* list) | ||||
| 	{ | ||||
| 		if (get_token (xli) <= -1) goto oops; | ||||
|  | ||||
| 		if (MATCH (xli, TOK_SQSTR) || MATCH (xli, TOK_DQSTR)) | ||||
| 		if (MATCH (xli, TOK_SQSTR) || MATCH (xli, TOK_DQSTR) || MATCH (xli, TOK_IDENT)) | ||||
| 		{ | ||||
| 			pair = qse_xli_insertpairwithstr ( | ||||
| 				xli, list, QSE_NULL, key, name,  | ||||
| 				QSE_STR_PTR(xli->tok.name), MATCH (xli, TOK_SQSTR)); | ||||
| 			if (pair == QSE_NULL) goto oops; | ||||
| 			if (qse_str_ncpy (xli->tmp[0], QSE_STR_PTR(xli->tok.name), QSE_STR_LEN(xli->tok.name) + 1) == (qse_size_t)-1) | ||||
| 			{ | ||||
| 				qse_xli_seterrnum (xli, QSE_XLI_ENOMEM, QSE_NULL); | ||||
| 				goto oops; | ||||
| 			} | ||||
|  | ||||
| 			if (get_token (xli) <= -1) goto oops; | ||||
| 			if (MATCH(xli, TOK_COMMA)) | ||||
| 			{ | ||||
| 				/* multi-segmented string */ | ||||
| 				do | ||||
| 				{ | ||||
| 					if (get_token (xli) <= -1) goto oops; /* skip the comma */ | ||||
|  | ||||
| 					if (!MATCH (xli, TOK_SQSTR) && !MATCH (xli, TOK_DQSTR) && !MATCH (xli, TOK_IDENT)) | ||||
| 					{ | ||||
| 						qse_xli_seterror (xli, QSE_XLI_ESYNTAX, QSE_NULL, &xli->tok.loc); | ||||
| 						goto oops; | ||||
| 					} | ||||
|  | ||||
| 					if (qse_str_ncat (xli->tmp[0], QSE_STR_PTR(xli->tok.name), QSE_STR_LEN(xli->tok.name) + 1) == (qse_size_t)-1) | ||||
| 					{ | ||||
| 						qse_xli_seterrnum (xli, QSE_XLI_ENOMEM, QSE_NULL); | ||||
| 						goto oops; | ||||
| 					} | ||||
|  | ||||
| 					if (get_token (xli) <= -1) goto oops; /* skip the value */ | ||||
| 				} | ||||
| 				while (MATCH (xli, TOK_COMMA)); | ||||
| 			} | ||||
| 			 | ||||
| 			pair = qse_xli_insertpairwithstr ( | ||||
| 				xli, list, QSE_NULL, key, name, QSE_STR_CSTR(xli->tmp[0])); | ||||
| 			if (pair == QSE_NULL) goto oops; | ||||
|  | ||||
| 			/* semicolon is mandatory for a string */ | ||||
| 			if (!MATCH (xli, TOK_SEMICOLON)) | ||||
| @ -651,6 +665,15 @@ static int read_pair (qse_xli_t* xli, qse_xli_list_t* list) | ||||
| 			if (get_token (xli) <= -1) goto oops; | ||||
| 		} | ||||
| 	} | ||||
| 	else if (MATCH (xli, TOK_SEMICOLON)) | ||||
| 	{ | ||||
| 		/* no value has been specified for the pair */ | ||||
| 		pair = qse_xli_insertpair (xli, list, QSE_NULL, key, name, &xli->xnil); | ||||
| 		if (pair == QSE_NULL) goto oops; | ||||
|  | ||||
| 		/* skip the semicolon */ | ||||
| 		if (get_token (xli) <= -1) goto oops; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		qse_xli_seterror (xli, QSE_XLI_ELBREQ, QSE_STR_CSTR(xli->tok.name), &xli->tok.loc); | ||||
| @ -677,9 +700,7 @@ static int read_list (qse_xli_t* xli, qse_xli_list_t* list) | ||||
|  | ||||
| 			if (!MATCH(xli,TOK_SQSTR) && !MATCH(xli,TOK_DQSTR)) | ||||
| 			{ | ||||
| #if 0 | ||||
| 				SETERR_LOC (xli, QSE_XLI_EINCLSTR, &xli->ptok.loc); | ||||
| #endif | ||||
| 				qse_xli_seterror (xli, QSE_XLI_EINCLSTR, QSE_NULL, &xli->tok.loc); | ||||
| 				return -1; | ||||
| 			} | ||||
|  | ||||
| @ -693,7 +714,10 @@ static int read_list (qse_xli_t* xli, qse_xli_list_t* list) | ||||
| 		{ | ||||
| 			if (get_token(xli) <= -1) goto oops; | ||||
| 		} | ||||
| 		else break; | ||||
| 		else  | ||||
| 		{ | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| @ -733,8 +757,7 @@ int qse_xli_read (qse_xli_t* xli, qse_xli_io_impl_t io) | ||||
|  | ||||
| 	if (!MATCH (xli, TOK_EOF)) | ||||
| 	{ | ||||
| /* TODO: set erro code */ | ||||
| qse_printf (QSE_T("NOT ENDING WITH EOF... %s\n"), QSE_STR_PTR(xli->tok.name)); | ||||
| 		qse_xli_seterror (xli, QSE_XLI_ESYNTAX, QSE_NULL, &xli->tok.loc); | ||||
| 		goto oops; | ||||
| 	} | ||||
|  | ||||
|  | ||||
| @ -24,12 +24,12 @@ int qse_xli_write (qse_xli_t* xli, qse_xli_io_impl_t io) | ||||
| { | ||||
| 	if (io == QSE_NULL) | ||||
| 	{ | ||||
| 		xli->errnum = QSE_XLI_EINVAL; | ||||
| 		qse_xli_seterrnum (xli, QSE_XLI_EINVAL, QSE_NULL); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	/* TODO: write data to io stream */ | ||||
| 	xli->errnum = QSE_XLI_ENOIMPL; | ||||
| 	qse_xli_seterrnum (xli, QSE_XLI_ENOIMPL, QSE_NULL); | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -19,6 +19,7 @@ | ||||
|  */ | ||||
|  | ||||
| #include "xli.h" | ||||
| #include <qse/cmn/chr.h> | ||||
|  | ||||
| qse_xli_t* qse_xli_open (qse_mmgr_t* mmgr, qse_size_t xtnsize) | ||||
| { | ||||
| @ -51,10 +52,18 @@ void qse_xli_close (qse_xli_t* xli) | ||||
|  | ||||
| int qse_xli_init (qse_xli_t* xli, qse_mmgr_t* mmgr) | ||||
| { | ||||
| 	qse_size_t i; | ||||
|  | ||||
| 	QSE_MEMSET (xli, 0, QSE_SIZEOF(*xli)); | ||||
| 	xli->mmgr = mmgr; | ||||
| 	xli->errstr = qse_xli_dflerrstr; | ||||
|  | ||||
| 	for (i = 0; i < QSE_COUNTOF(xli->tmp); i++) | ||||
| 	{ | ||||
| 		xli->tmp[i] = qse_str_open (mmgr, 0, 128); | ||||
| 		if (xli->tmp[i] == QSE_NULL) goto oops; | ||||
| 	} | ||||
|  | ||||
| 	xli->tok.name = qse_str_open (mmgr, 0, 128); | ||||
| 	if (xli->tok.name == QSE_NULL) goto oops; | ||||
|  | ||||
| @ -67,20 +76,34 @@ int qse_xli_init (qse_xli_t* xli, qse_mmgr_t* mmgr) | ||||
| 		qse_gethtbmancbs(QSE_HTB_MANCBS_INLINE_KEY_COPIER) | ||||
| 	); | ||||
| 	 | ||||
| 	xli->root.type = QSE_XLI_LIST; | ||||
| 	xli->xnil.type = QSE_XLI_NIL; | ||||
| 	return 0; | ||||
|  | ||||
| oops: | ||||
| 	qse_xli_seterrnum (xli, QSE_XLI_ENOMEM, QSE_NULL); | ||||
| 	if (xli->sio_names) qse_htb_close (xli->sio_names); | ||||
| 	if (xli->tok.name) qse_str_close (xli->tok.name); | ||||
| 	 | ||||
| 	for (i = QSE_COUNTOF(xli->tmp); i > 0; ) | ||||
| 	{ | ||||
| 		if (xli->tmp[--i]) qse_str_close (xli->tmp[i]); | ||||
| 	} | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| void qse_xli_fini (qse_xli_t* xli) | ||||
| { | ||||
| 	qse_size_t i; | ||||
|  | ||||
| 	qse_xli_clear (xli); | ||||
| 	qse_htb_close (xli->sio_names); | ||||
| 	qse_str_close (xli->tok.name); | ||||
|  | ||||
| 	for (i = QSE_COUNTOF(xli->tmp); i > 0; ) | ||||
| 	{ | ||||
| 		if (xli->tmp[--i]) qse_str_close (xli->tmp[i]); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| qse_mmgr_t* qse_xli_getmmgr (qse_xli_t* xli) | ||||
| @ -243,20 +266,20 @@ qse_xli_pair_t* qse_xli_insertpairwithemptylist ( | ||||
|  | ||||
| qse_xli_pair_t* qse_xli_insertpairwithstr ( | ||||
| 	qse_xli_t* xli, qse_xli_list_t* parent, qse_xli_atom_t* peer, | ||||
| 	const qse_char_t* key, const qse_char_t* name, const qse_char_t* value, int verbatim) | ||||
| 	const qse_char_t* key, const qse_char_t* name, const qse_cstr_t* value) | ||||
| { | ||||
| 	qse_xli_str_t* val; | ||||
| 	qse_xli_pair_t* tmp; | ||||
| 	qse_size_t vlen; | ||||
|  | ||||
| 	vlen = qse_strlen (value); | ||||
| 	val = qse_xli_callocmem (xli, QSE_SIZEOF(*val) + ((vlen  + 1) * QSE_SIZEOF(*value))); | ||||
| 	val = qse_xli_callocmem (xli, QSE_SIZEOF(*val) + ((value->len  + 1) * QSE_SIZEOF(*value->ptr))); | ||||
| 	if (val == QSE_NULL) return QSE_NULL; | ||||
|  | ||||
| 	val->type = QSE_XLI_STR; | ||||
|  | ||||
| 	qse_strncpy ((qse_char_t*)(val + 1), value->ptr, value->len); | ||||
| 	val->ptr = (const qse_char_t*)(val + 1); | ||||
| 	val->len = vlen; | ||||
| 	val->verbatim = verbatim; | ||||
| 	val->len = value->len; | ||||
|  | ||||
| 	tmp = qse_xli_insertpair (xli, parent, peer, key, name, (qse_xli_val_t*)val);	 | ||||
| 	if (tmp == QSE_NULL) qse_xli_freemem (xli, val); | ||||
| 	return tmp; | ||||
| @ -275,7 +298,6 @@ qse_xli_text_t* qse_xli_inserttext ( | ||||
|  | ||||
| 	text->type = QSE_XLI_TEXT; | ||||
| 	text->ptr = (const qse_char_t*)(text + 1); | ||||
| 	text->len = slen; | ||||
|  | ||||
| 	insert_atom (xli, parent, peer, (qse_xli_atom_t*)text); | ||||
|  | ||||
| @ -292,10 +314,13 @@ static void free_atom (qse_xli_t* xli, qse_xli_atom_t* atom) | ||||
| 	{ | ||||
| 		qse_xli_pair_t* pair = (qse_xli_pair_t*)atom; | ||||
|  | ||||
| 		if (pair->val->type == QSE_XLI_LIST) | ||||
| 			free_list (xli, (qse_xli_list_t*)pair->val); | ||||
| 		if (pair->val != &xli->xnil) | ||||
| 		{ | ||||
| 			if (pair->val->type == QSE_XLI_LIST) | ||||
| 				free_list (xli, (qse_xli_list_t*)pair->val); | ||||
|  | ||||
| 		QSE_MMGR_FREE (xli->mmgr, pair->val); | ||||
| 			QSE_MMGR_FREE (xli->mmgr, pair->val); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	QSE_MMGR_FREE (xli->mmgr, atom); | ||||
| @ -319,6 +344,186 @@ static void free_list (qse_xli_t* xli, qse_xli_list_t* list) | ||||
|  | ||||
| void qse_xli_clear (qse_xli_t* xli) | ||||
| { | ||||
| 	/* TODO: free data under xli->root */ | ||||
| 	free_list (xli, &xli->root); | ||||
| } | ||||
|  | ||||
| static qse_xli_pair_t* find_pair_byname ( | ||||
| 	qse_xli_t* xli, const qse_xli_list_t* list,  | ||||
| 	const qse_cstr_t* key, const qse_cstr_t* name) | ||||
| { | ||||
| 	qse_xli_atom_t* p; | ||||
|  | ||||
| 	/* TODO: speed up. no linear search */ | ||||
| 	p = list->head; | ||||
| 	while (p) | ||||
| 	{ | ||||
| 		if (p->type == QSE_XLI_PAIR) | ||||
| 		{ | ||||
| 			qse_xli_pair_t* pair = (qse_xli_pair_t*)p; | ||||
| 			if (qse_strxcmp (key->ptr, key->len, pair->key) == 0)  | ||||
| 			{ | ||||
| 				if (name == QSE_NULL ||  | ||||
| 				    qse_strxcmp (name->ptr, name->len, pair->name) == 0) return pair; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		p = p->next; | ||||
| 	} | ||||
|  | ||||
| 	return QSE_NULL; | ||||
| } | ||||
|  | ||||
| static qse_xli_pair_t* find_pair_byindex ( | ||||
| 	qse_xli_t* xli, const qse_xli_list_t* list,  | ||||
| 	const qse_cstr_t* key, qse_size_t index) | ||||
| { | ||||
| 	qse_xli_atom_t* p; | ||||
| 	qse_size_t count = 0; | ||||
|  | ||||
| 	/* TODO: speed up. no linear search */ | ||||
| 	p = list->head; | ||||
| 	while (p) | ||||
| 	{ | ||||
| 		if (p->type == QSE_XLI_PAIR) | ||||
| 		{ | ||||
| 			qse_xli_pair_t* pair = (qse_xli_pair_t*)p; | ||||
| 			if (qse_strxcmp (key->ptr, key->len, pair->key) == 0)  | ||||
| 			{ | ||||
| 				if (index == count) return pair; | ||||
| 				count++; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		p = p->next; | ||||
| 	} | ||||
|  | ||||
| 	return QSE_NULL; | ||||
| } | ||||
|  | ||||
| qse_xli_pair_t* qse_xli_findpairbyname ( | ||||
| 	qse_xli_t* xli, const qse_xli_list_t* list, const qse_char_t* name) | ||||
| { | ||||
| 	const qse_char_t* ptr; | ||||
| 	qse_cstr_t seg; | ||||
| 	qse_xli_list_t* curlist; | ||||
| 	qse_xli_pair_t* pair; | ||||
|  | ||||
| 	curlist = list? list: &xli->root; | ||||
|  | ||||
| 	ptr = name; | ||||
| 	while (1) | ||||
| 	{ | ||||
| 		seg.ptr = ptr; | ||||
| 		while (*ptr != QSE_T('\0') &&  | ||||
| 		       *ptr != QSE_T('.') &&  | ||||
| 		       *ptr != QSE_T('[')) ptr++; | ||||
| 		if (ptr == seg.ptr) goto inval; | ||||
| 		seg.len = ptr - seg.ptr; | ||||
|  | ||||
| 		if (curlist->type != QSE_XLI_LIST)  | ||||
| 		{ | ||||
| 			/* check the type of curlist. this check is needed | ||||
| 			 * because of the unconditional switching at the bottom of the  | ||||
| 			 * this loop. this implementation strategy has been chosen | ||||
| 			 * to provide the segment name easily. */ | ||||
| 			goto noent; | ||||
| 		} | ||||
|  | ||||
| 		if (*ptr == QSE_T('[')) | ||||
| 		{ | ||||
| 			/*  index is specified */ | ||||
| 			ptr++; | ||||
|  | ||||
| 			if (QSE_ISDIGIT(*ptr)) | ||||
| 			{ | ||||
| 				/* numeric index */ | ||||
| 				qse_size_t index = 0, count = 0; | ||||
| 				do  | ||||
| 				{ | ||||
| 					index = index * 10 + (*ptr++ - QSE_T('0'));  | ||||
| 					count++; | ||||
| 				} | ||||
| 				while (QSE_ISDIGIT(*ptr)); | ||||
|  | ||||
| 				if (*ptr != QSE_T(']')) goto inval; | ||||
|  | ||||
| 				pair = find_pair_byindex (xli, curlist, &seg, index); | ||||
| 				if (pair == QSE_NULL)  | ||||
| 				{ | ||||
| 					seg.len += count + 2; /* adjustment for error message */ | ||||
| 					goto noent; | ||||
| 				} | ||||
| 			} | ||||
| 			else if (QSE_ISALPHA(*ptr)) | ||||
| 			{ | ||||
| 				/* word index */ | ||||
| 				qse_cstr_t idx; | ||||
|  | ||||
| 				idx.ptr = ptr; | ||||
| 				do ptr++; while (QSE_ISALNUM(*ptr) || *ptr == QSE_T('_') || *ptr == QSE_T('-')); | ||||
| 				idx.len = ptr - idx.ptr; | ||||
| 	 | ||||
| 				if (*ptr != QSE_T(']')) goto inval; | ||||
|  | ||||
| 				pair = find_pair_byname (xli, curlist, &seg, &idx); | ||||
| 				if (pair == QSE_NULL)  | ||||
| 				{ | ||||
| 					seg.len += idx.len + 2; /* adjustment for error message */ | ||||
| 					goto noent; | ||||
| 				} | ||||
| 			} | ||||
| 			else if (*ptr == QSE_T('\'') || *ptr == QSE_T('\"')) | ||||
| 			{ | ||||
| 				qse_cstr_t idx; | ||||
| 				qse_char_t cc = *ptr++; | ||||
|  | ||||
| 				idx.ptr = ptr; | ||||
| 				do ptr++; while (*ptr != cc && *ptr != QSE_T('\0')); | ||||
| 				idx.len = ptr - idx.ptr; | ||||
| 		 | ||||
| 				if (*ptr != cc) goto inval; | ||||
| 				if (*++ptr != QSE_T(']')) goto inval; | ||||
|  | ||||
| 				pair = find_pair_byname (xli, curlist, &seg, &idx); | ||||
| 				if (pair == QSE_NULL)  | ||||
| 				{ | ||||
| 					seg.len += idx.len + 4; /* adjustment for error message */ | ||||
| 					goto noent; | ||||
| 				} | ||||
| 			} | ||||
| 			else goto inval; | ||||
|  | ||||
| 			ptr++;  /* skip ] */ | ||||
|  | ||||
| 			if (*ptr == QSE_T('\0')) break; /* no more segments */ | ||||
| 			else if (*ptr != QSE_T('.')) goto inval; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			pair = find_pair_byname (xli, curlist, &seg, QSE_NULL); | ||||
| 			if (pair == QSE_NULL) goto noent; | ||||
|  | ||||
| 			if (*ptr == QSE_T('\0')) break; /* no more segments */ | ||||
| 		} | ||||
|  | ||||
| 		/* more segments to handle */ | ||||
| 		QSE_ASSERT (*ptr == QSE_T('.')); | ||||
| 		ptr++; | ||||
|  | ||||
| 		/* switch to the value regardless of its type. | ||||
| 		 * check if it is a list in the beginning of the loop | ||||
| 		 * just after having gotten the next segment name */ | ||||
| 		curlist = (qse_xli_list_t*)pair->val; | ||||
| 	} | ||||
|  | ||||
| 	return pair; | ||||
|  | ||||
| inval: | ||||
| 	qse_xli_seterrnum (xli, QSE_XLI_EINVAL, QSE_NULL); | ||||
| 	return QSE_NULL; | ||||
|  | ||||
| noent: | ||||
| 	qse_xli_seterrnum (xli, QSE_XLI_ENOENT, &seg); | ||||
| 	return QSE_NULL; | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -52,12 +52,14 @@ struct qse_xli_t | ||||
| 	qse_xli_ecb_t* ecb; | ||||
|  | ||||
| 	qse_xli_list_t root; | ||||
| 	qse_xli_nil_t xnil; | ||||
|  | ||||
| 	qse_str_t* tmp[1]; | ||||
| 	qse_xli_tok_t tok; | ||||
| 	struct | ||||
| 	{ | ||||
| 		qse_xli_io_impl_t inf; /* input handler */ | ||||
| 		qse_xli_io_lxc_t last;	 | ||||
| 		qse_xli_io_lxc_t  last;	 | ||||
| 		qse_xli_io_arg_t  arg; /* for top level */ | ||||
| 		qse_xli_io_arg_t* inp; /* current */ | ||||
| 	} sio; | ||||
|  | ||||
		Reference in New Issue
	
	Block a user