revising httpd
This commit is contained in:
		| @ -1,2 +1,2 @@ | ||||
| SUBDIRS = awk sed xli net  | ||||
| SUBDIRS = 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 = awk sed xli net  | ||||
| SUBDIRS = awk sed xli http  | ||||
| DIST_SUBDIRS = $(SUBDIRS)  | ||||
| all: all-recursive | ||||
|  | ||||
|  | ||||
| @ -8,8 +8,8 @@ AM_CPPFLAGS = \ | ||||
| bin_PROGRAMS = qsehttpd | ||||
| 
 | ||||
| qsehttpd_SOURCES = httpd.c | ||||
| qsehttpd_LDFLAGS = -L../../lib/net -L../../lib/cmn -L$(libdir) | ||||
| qsehttpd_LDADD = -lqsenet -lqsecmn | ||||
| qsehttpd_LDFLAGS = -L../../lib/xli -L../../lib/http -L../../lib/cmn -L$(libdir) | ||||
| qsehttpd_LDADD = -lqsexli -lqsehttp -lqsecmn | ||||
| 
 | ||||
| if WIN32  | ||||
| if WCHAR | ||||
| @ -36,7 +36,7 @@ build_triplet = @build@ | ||||
| host_triplet = @host@ | ||||
| bin_PROGRAMS = qsehttpd$(EXEEXT) | ||||
| @WCHAR_TRUE@@WIN32_TRUE@am__append_1 = $(UNICOWS_LIBS) | ||||
| subdir = cmd/net | ||||
| subdir = cmd/http | ||||
| DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in | ||||
| ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 | ||||
| am__aclocal_m4_deps = $(top_srcdir)/m4/argz.m4 \
 | ||||
| @ -276,8 +276,8 @@ AM_CPPFLAGS = \ | ||||
| 	-I$(includedir) | ||||
| 
 | ||||
| qsehttpd_SOURCES = httpd.c | ||||
| qsehttpd_LDFLAGS = -L../../lib/net -L../../lib/cmn -L$(libdir) | ||||
| qsehttpd_LDADD = -lqsenet -lqsecmn $(am__append_1) | ||||
| qsehttpd_LDFLAGS = -L../../lib/xli -L../../lib/http -L../../lib/cmn -L$(libdir) | ||||
| qsehttpd_LDADD = -lqsexli -lqsehttp -lqsecmn $(am__append_1) | ||||
| all: all-am | ||||
| 
 | ||||
| .SUFFIXES: | ||||
| @ -291,9 +291,9 @@ $(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps) | ||||
| 	      exit 1;; \
 | ||||
| 	  esac; \
 | ||||
| 	done; \
 | ||||
| 	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign cmd/net/Makefile'; \
 | ||||
| 	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign cmd/http/Makefile'; \
 | ||||
| 	$(am__cd) $(top_srcdir) && \
 | ||||
| 	  $(AUTOMAKE) --foreign cmd/net/Makefile | ||||
| 	  $(AUTOMAKE) --foreign cmd/http/Makefile | ||||
| .PRECIOUS: Makefile | ||||
| Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status | ||||
| 	@case '$?' in \
 | ||||
							
								
								
									
										555
									
								
								qse/cmd/http/httpd.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										555
									
								
								qse/cmd/http/httpd.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,555 @@ | ||||
|  | ||||
| #include <qse/http/std.h> | ||||
| #include <qse/xli/std.h> | ||||
| #include <qse/cmn/stdio.h> | ||||
| #include <qse/cmn/main.h> | ||||
| #include <qse/cmn/str.h> | ||||
| #include <qse/cmn/mem.h> | ||||
| #include <qse/cmn/mbwc.h> | ||||
| #include <qse/cmn/time.h> | ||||
|  | ||||
| #include <signal.h> | ||||
| #include <locale.h> | ||||
|  | ||||
| #if defined(_WIN32) | ||||
| #	include <winsock2.h> | ||||
| #	include <windows.h> | ||||
| #	include <tchar.h> | ||||
| #	include <process.h> | ||||
| #elif defined(__OS2__) | ||||
| #	define INCL_DOSPROCESS | ||||
| #	define INCL_DOSEXCEPTIONS | ||||
| #	define INCL_ERRORS | ||||
| #	include <os2.h> | ||||
| #elif defined(__DOS__) | ||||
| #	include <dos.h> | ||||
| #else | ||||
| #	include <unistd.h> | ||||
| #	include <errno.h> | ||||
| #endif | ||||
|  | ||||
| #if defined(HAVE_SSL) | ||||
| #	include <openssl/ssl.h> | ||||
| #	include <openssl/err.h> | ||||
| #	include <openssl/engine.h> | ||||
| #endif | ||||
|  | ||||
| /* --------------------------------------------------------------------- */ | ||||
|  | ||||
| static qse_httpd_t* g_httpd = QSE_NULL; | ||||
|  | ||||
| static void sigint (int sig) | ||||
| { | ||||
| 	if (g_httpd) qse_httpd_stop (g_httpd); | ||||
| } | ||||
|  | ||||
| static void sighup (int sig) | ||||
| { | ||||
| 	if (g_httpd) qse_httpd_reconfig (g_httpd); | ||||
| } | ||||
|  | ||||
| static void setup_signal_handlers () | ||||
| { | ||||
| 	struct sigaction act; | ||||
|  | ||||
| #if defined(SIGINT) | ||||
| 	qse_memset (&act, 0, QSE_SIZEOF(act)); | ||||
| 	act.sa_handler = sigint; | ||||
| 	sigaction (SIGINT, &act, QSE_NULL); | ||||
| #endif | ||||
|  | ||||
| #if defined(SIGHUP) | ||||
| 	qse_memset (&act, 0, QSE_SIZEOF(act)); | ||||
| 	act.sa_handler = sighup; | ||||
| 	sigaction (SIGHUP, &act, QSE_NULL); | ||||
| #endif | ||||
|  | ||||
| #if defined(SIGPIPE) | ||||
| 	qse_memset (&act, 0, QSE_SIZEOF(act)); | ||||
| 	act.sa_handler = SIG_IGN; | ||||
| 	sigaction (SIGPIPE, &act, QSE_NULL); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static void restore_signal_handlers () | ||||
| { | ||||
| 	struct sigaction act; | ||||
|  | ||||
| #if defined(SIGINT) | ||||
| 	qse_memset (&act, 0, QSE_SIZEOF(act)); | ||||
| 	act.sa_handler = SIG_DFL; | ||||
| 	sigaction (SIGINT, &act, QSE_NULL); | ||||
| #endif | ||||
|  | ||||
| #if defined(SIGHUP) | ||||
| 	qse_memset (&act, 0, QSE_SIZEOF(act)); | ||||
| 	act.sa_handler = SIG_DFL; | ||||
| 	sigaction (SIGHUP, &act, QSE_NULL); | ||||
| #endif | ||||
|  | ||||
| #if defined(SIGPIPE) | ||||
| 	qse_memset (&act, 0, QSE_SIZEOF(act)); | ||||
| 	act.sa_handler = SIG_DFL; | ||||
| 	sigaction (SIGPIPE, &act, QSE_NULL); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| /* --------------------------------------------------------------------- */ | ||||
|  | ||||
| typedef struct server_xtn_t server_xtn_t; | ||||
| struct server_xtn_t | ||||
| { | ||||
| 	int tproxy; | ||||
| 	int nodir; /* no directory listing */ | ||||
|  | ||||
| 	qse_httpd_serverstd_makersrc_t orgmakersrc; | ||||
| 	qse_httpd_serverstd_freersrc_t orgfreersrc; | ||||
| 	qse_httpd_serverstd_query_t orgquery; | ||||
|  | ||||
| 	qse_mchar_t* docroot; | ||||
| 	qse_mchar_t* realm; | ||||
| 	qse_mchar_t* auth; | ||||
| 	qse_mchar_t* dircss; | ||||
| 	qse_mchar_t* errcss; | ||||
| 	 | ||||
| 	qse_httpd_serverstd_index_t index; | ||||
| }; | ||||
|  | ||||
| static int make_resource ( | ||||
| 	qse_httpd_t* httpd, qse_httpd_client_t* client, | ||||
| 	qse_htre_t* req, qse_httpd_rsrc_t* rsrc) | ||||
| { | ||||
| 	server_xtn_t* server_xtn; | ||||
|  | ||||
| 	server_xtn = qse_httpd_getserverxtnstd (httpd, client->server); | ||||
|  | ||||
| 	if (server_xtn->tproxy) | ||||
| 	{ | ||||
| 		if (qse_nwadequal(&client->orgdst_addr, &client->local_addr)) /* both equal and error */ | ||||
| 		{ | ||||
| 			/* TODO: implement a better check that the | ||||
| 			 *       destination is not one of the local addresses */ | ||||
|  | ||||
| 			rsrc->type = QSE_HTTPD_RSRC_ERR; | ||||
| 			rsrc->u.err.code = 500; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			rsrc->type = QSE_HTTPD_RSRC_PROXY; | ||||
| 			rsrc->u.proxy.dst = client->orgdst_addr; | ||||
| 			rsrc->u.proxy.src = client->remote_addr; | ||||
| 	 | ||||
| 			if (rsrc->u.proxy.src.type == QSE_NWAD_IN4) | ||||
| 				rsrc->u.proxy.src.u.in4.port = 0; /* reset the port to 0. */ | ||||
| 			else if (rsrc->u.proxy.src.type == QSE_NWAD_IN6) | ||||
| 				rsrc->u.proxy.src.u.in6.port = 0; /* reset the port to 0. */ | ||||
| 		} | ||||
|  | ||||
| 		return 0; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		if (server_xtn->orgmakersrc (httpd, client, req, rsrc) <= -1) return -1; | ||||
| 		if (server_xtn->nodir && rsrc->type == QSE_HTTPD_RSRC_DIR) | ||||
| 		{ | ||||
| 			/* prohibit no directory listing */ | ||||
| 			if (server_xtn->orgfreersrc) | ||||
| 				server_xtn->orgfreersrc (httpd, client, req, rsrc); | ||||
| 			rsrc->type = QSE_HTTPD_RSRC_ERR; | ||||
| 			rsrc->u.err.code = 403; | ||||
| 		} | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void free_resource ( | ||||
| 	qse_httpd_t* httpd, qse_httpd_client_t* client,  | ||||
| 	qse_htre_t* req, qse_httpd_rsrc_t* rsrc) | ||||
| { | ||||
| 	server_xtn_t* server_xtn; | ||||
|  | ||||
| 	server_xtn = qse_httpd_getserverxtnstd (httpd, client->server); | ||||
|  | ||||
| 	if (server_xtn->tproxy) | ||||
| 	{ | ||||
| 		/* nothing to do */ | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		if (server_xtn->orgfreersrc)  | ||||
| 			server_xtn->orgfreersrc (httpd, client, req, rsrc); | ||||
| 	} | ||||
| } | ||||
| /* --------------------------------------------------------------------- */ | ||||
| static void predetach_server (qse_httpd_t* httpd, qse_httpd_server_t* server) | ||||
| { | ||||
| 	server_xtn_t* server_xtn; | ||||
|  | ||||
| 	server_xtn = qse_httpd_getserverxtnstd (httpd, server); | ||||
|  | ||||
| 	if (server_xtn->docroot) qse_httpd_freemem (httpd, server_xtn->docroot); | ||||
| 	if (server_xtn->realm) qse_httpd_freemem (httpd, server_xtn->realm); | ||||
| 	if (server_xtn->auth) qse_httpd_freemem (httpd, server_xtn->auth); | ||||
| 	if (server_xtn->dircss) qse_httpd_freemem (httpd, server_xtn->dircss); | ||||
| 	if (server_xtn->errcss) qse_httpd_freemem (httpd, server_xtn->errcss); | ||||
| 	if (server_xtn->index.files) qse_httpd_freemem (httpd, server_xtn->index.files); | ||||
| } | ||||
|  | ||||
| static void reconfig_server (qse_httpd_t* httpd, qse_httpd_server_t* server) | ||||
| { | ||||
| 	qse_printf (QSE_T("reconfiguring server.....\n")); | ||||
| } | ||||
|  | ||||
| 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) | ||||
| { | ||||
| 	server_xtn_t* server_xtn; | ||||
|  | ||||
| 	server_xtn = qse_httpd_getserverxtnstd (httpd, server); | ||||
|  | ||||
| 	switch (code) | ||||
| 	{ | ||||
| 		case QSE_HTTPD_SERVERSTD_DOCROOT: | ||||
| 			*(const qse_mchar_t**)result = server_xtn->docroot; | ||||
| 			return 0; | ||||
|  | ||||
| 		case QSE_HTTPD_SERVERSTD_REALM: | ||||
| 			*(const qse_mchar_t**)result = server_xtn->realm; | ||||
| 			return 0; | ||||
|  | ||||
| 		case QSE_HTTPD_SERVERSTD_AUTH: | ||||
| 			*(const qse_mchar_t**)result = server_xtn->auth; | ||||
| 			return 0; | ||||
|  | ||||
| 		case QSE_HTTPD_SERVERSTD_DIRCSS: | ||||
| 			*(const qse_mchar_t**)result = server_xtn->dircss; | ||||
| 			return 0; | ||||
|  | ||||
| 		case QSE_HTTPD_SERVERSTD_ERRCSS: | ||||
| 			*(const qse_mchar_t**)result = server_xtn->errcss; | ||||
| 			return 0; | ||||
|  | ||||
| 		case QSE_HTTPD_SERVERSTD_INDEX: | ||||
| 			*(qse_httpd_serverstd_index_t*)result = server_xtn->index; | ||||
| 			return 0; | ||||
|  | ||||
|  | ||||
| #if 0 | ||||
| 		case QSE_HTTPD_SERVERSTD_CGI: | ||||
| 		case QSE_HTTPD_SERVERSTD_MIME: | ||||
| #endif | ||||
| 	} | ||||
|  | ||||
| 	return server_xtn->orgquery (httpd, server, req, xpath, code, result); | ||||
| } | ||||
|  | ||||
| /* --------------------------------------------------------------------- */ | ||||
|  | ||||
| static int load_server (qse_httpd_t* httpd, qse_xli_t* xli, qse_xli_list_t* list) | ||||
| { | ||||
| 	qse_httpd_serverstd_t server;  | ||||
| 	qse_httpd_server_t* xserver; | ||||
| 	server_xtn_t* server_xtn; | ||||
| 	qse_xli_pair_t* pair; | ||||
|  | ||||
| 	pair = qse_xli_findpairbyname (xli, list, QSE_T("bind")); | ||||
| 	if (pair == QSE_NULL) | ||||
| 	{ | ||||
| 		/* TOOD: logging */ | ||||
| 		qse_printf (QSE_T("WARNING: no bind specified for  ....\n")); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (pair->val->type != QSE_XLI_STR) | ||||
| 	{ | ||||
| 		/*  TOOD: logging */ | ||||
| 		qse_printf (QSE_T("WARNING: non-string value for bind\n")); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	qse_memset (&server, 0, QSE_SIZEOF(server)); | ||||
| 	if (qse_strtonwad (((qse_xli_str_t*)pair->val)->ptr, &server.nwad) <= -1) | ||||
| 	{ | ||||
| 		/*  TOOD: logging */ | ||||
| 		qse_printf (QSE_T("WARNING: invalid value for bind - %s\n"), ((qse_xli_str_t*)pair->val)->ptr); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	server.predetach = predetach_server; | ||||
| 	xserver = qse_httpd_attachserverstd (httpd, &server, QSE_SIZEOF(server_xtn_t)); | ||||
| 	if (xserver == QSE_NULL)  | ||||
| 	{ | ||||
| 		/* TODO: logging */ | ||||
| 		qse_printf (QSE_T("WARNING: failed to attach server\n")); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	server_xtn = qse_httpd_getserverxtnstd (httpd, xserver); | ||||
|  | ||||
| 	qse_httpd_getserveroptstd (httpd, xserver, QSE_HTTPD_SERVERSTD_QUERY, &server_xtn->orgquery); | ||||
| 	qse_httpd_setserveroptstd (httpd, xserver, QSE_HTTPD_SERVERSTD_QUERY, query_server); | ||||
|  | ||||
| 	qse_httpd_getserveroptstd (httpd, xserver, QSE_HTTPD_SERVERSTD_MAKERSRC, &server_xtn->orgmakersrc); | ||||
| 	qse_httpd_setserveroptstd (httpd, xserver, QSE_HTTPD_SERVERSTD_MAKERSRC, make_resource); | ||||
|  | ||||
| 	qse_httpd_getserveroptstd (httpd, xserver, QSE_HTTPD_SERVERSTD_FREERSRC, &server_xtn->orgfreersrc); | ||||
| 	qse_httpd_setserveroptstd (httpd, xserver, QSE_HTTPD_SERVERSTD_FREERSRC, free_resource); | ||||
|  | ||||
| 	/* --------------------------------------------------------------------- */ | ||||
| 	pair = qse_xli_findpairbyname (xli, list, QSE_T("host['*'].location['/'].docroot")); | ||||
| 	if (!pair) pair = qse_xli_findpairbyname (xli, QSE_NULL, QSE_T("default.docroot")); | ||||
| 	if (pair && pair->val->type == QSE_XLI_STR) | ||||
| 	{ | ||||
| 		/* TODO: use a table */ | ||||
|  | ||||
| 		server_xtn->docroot = qse_httpd_strtombsdup (httpd, ((qse_xli_str_t*)pair->val)->ptr); | ||||
| 		if (server_xtn->docroot == QSE_NULL)  | ||||
| 		{ | ||||
| 			qse_printf (QSE_T("WARNING: fail to copy docroot - %s\n"), ((qse_xli_str_t*)pair->val)->ptr); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	pair = qse_xli_findpairbyname (xli, list, QSE_T("host['*'].location['/'].realm")); | ||||
| 	if (!pair) pair = qse_xli_findpairbyname (xli, QSE_NULL, QSE_T("default.realm")); | ||||
| 	if (pair && pair->val->type == QSE_XLI_STR) | ||||
| 	{ | ||||
| 		/* TODO: use a table */ | ||||
|  | ||||
| 		server_xtn->realm = qse_httpd_strtombsdup (httpd, ((qse_xli_str_t*)pair->val)->ptr); | ||||
| 		if (server_xtn->realm == QSE_NULL)  | ||||
| 		{ | ||||
| 			qse_printf (QSE_T("WARNING: fail to copy realm - %s\n"), ((qse_xli_str_t*)pair->val)->ptr); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	pair = qse_xli_findpairbyname (xli, list, QSE_T("host['*'].location['/'].auth")); | ||||
| 	if (!pair) pair = qse_xli_findpairbyname (xli, QSE_NULL, QSE_T("default.auth")); | ||||
| 	if (pair && pair->val->type == QSE_XLI_STR) | ||||
| 	{ | ||||
| 		/* TODO: use a table */ | ||||
| 		server_xtn->auth = qse_httpd_strtombsdup (httpd, ((qse_xli_str_t*)pair->val)->ptr); | ||||
| 		if (server_xtn->auth == QSE_NULL)  | ||||
| 		{ | ||||
| 			qse_printf (QSE_T("WARNING: fail to copy auth - %s\n"), ((qse_xli_str_t*)pair->val)->ptr); | ||||
| 			return -1; | ||||
| 		} | ||||
|  | ||||
| 		if (qse_mbschr (server_xtn->auth, QSE_MT(':')) == QSE_NULL) | ||||
| 		{ | ||||
| 			qse_printf (QSE_T("WARNING: no colon in the auth string - [%hs]\n"), server_xtn->auth); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	pair = qse_xli_findpairbyname (xli, list, QSE_T("host['*'].location['/'].css.dir")); | ||||
| 	if (!pair) pair = qse_xli_findpairbyname (xli, QSE_NULL, QSE_T("default.css.dir")); | ||||
| 	if (pair && pair->val->type == QSE_XLI_STR) | ||||
| 	{ | ||||
| 		/* TODO: use a table */ | ||||
| 		server_xtn->dircss = qse_httpd_strtombsdup (httpd, ((qse_xli_str_t*)pair->val)->ptr); | ||||
| 		if (server_xtn->dircss == QSE_NULL)  | ||||
| 		{ | ||||
| 			qse_printf (QSE_T("WARNING: fail to copy dircss - %s\n"), ((qse_xli_str_t*)pair->val)->ptr); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	pair = qse_xli_findpairbyname (xli, list, QSE_T("host['*'].location['/'].css.error")); | ||||
| 	if (!pair) pair = qse_xli_findpairbyname (xli, QSE_NULL, QSE_T("default.css.error")); | ||||
| 	if (pair && pair->val->type == QSE_XLI_STR) | ||||
| 	{ | ||||
| 		/* TODO: use a table */ | ||||
|  | ||||
| 		server_xtn->errcss = qse_httpd_strtombsdup (httpd, ((qse_xli_str_t*)pair->val)->ptr); | ||||
| 		if (server_xtn->errcss == QSE_NULL)  | ||||
| 		{ | ||||
| 			qse_printf (QSE_T("WARNING: fail to copy dircss - %s\n"), ((qse_xli_str_t*)pair->val)->ptr); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	pair = qse_xli_findpairbyname (xli, list, QSE_T("host['*'].location['/'].index")); | ||||
| 	if (!pair) pair = qse_xli_findpairbyname (xli, QSE_NULL, QSE_T("default.index")); | ||||
| 	if (pair && pair->val->type == QSE_XLI_STR) | ||||
| 	{ | ||||
| 		const qse_char_t* tmpptr, * tmpend; | ||||
| 		qse_size_t count; | ||||
|  | ||||
| 		tmpptr = ((qse_xli_str_t*)pair->val)->ptr; | ||||
| 		tmpend = tmpptr + ((qse_xli_str_t*)pair->val)->len; | ||||
| 	 | ||||
| 		for (count = 0; tmpptr < tmpend; count++) tmpptr += qse_strlen (tmpptr) + 1; | ||||
|  | ||||
| 		server_xtn->index.count = count; | ||||
| 		server_xtn->index.files = qse_httpd_strntombsdup ( | ||||
| 			httpd, ((qse_xli_str_t*)pair->val)->ptr, ((qse_xli_str_t*)pair->val)->len); | ||||
| 		if (server_xtn->index.files == QSE_NULL)  | ||||
| 		{ | ||||
| 			qse_printf (QSE_T("WARNING: fail to copy index\n")); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int load_config (qse_httpd_t* httpd, qse_xli_t* xli, const qse_char_t* file) | ||||
| { | ||||
| 	qse_xli_iostd_t xli_in; | ||||
| 	qse_xli_pair_t* pair; | ||||
| 	int i; | ||||
|  | ||||
| 	xli_in.type = QSE_XLI_IOSTD_FILE; | ||||
| 	xli_in.u.file.path = file; | ||||
| 	xli_in.u.file.cmgr = QSE_NULL; | ||||
|  | ||||
| 	if (qse_xli_readstd (xli, &xli_in) <= -1) | ||||
| 	{ | ||||
| 		qse_fprintf (QSE_STDERR, QSE_T("Cannot load %s - %s\n"), xli_in.u.file.path, qse_xli_geterrmsg(xli)); | ||||
| 		return - 1; | ||||
| 	} | ||||
|  | ||||
| 	for (i = 0; ; i++) | ||||
| 	{ | ||||
| 		qse_char_t buf[32]; | ||||
| 		qse_sprintf (buf, QSE_COUNTOF(buf), QSE_T("server[%d]"), i); | ||||
| 		pair = qse_xli_findpairbyname (xli, QSE_NULL, buf); | ||||
| 		if (pair == QSE_NULL) break; | ||||
|  | ||||
| 		if (pair->val->type != QSE_XLI_LIST) | ||||
| 		{ | ||||
| 			qse_fprintf (QSE_STDERR, QSE_T("WARNING: non-list value for server\n")); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			load_server (httpd, xli, (qse_xli_list_t*)pair->val); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (i == 0) | ||||
| 	{ | ||||
| 		qse_fprintf (QSE_STDERR, QSE_T("No valid server specified in %s\n"), xli_in.u.file.path); | ||||
| 		return - 1; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* --------------------------------------------------------------------- */ | ||||
| static int httpd_main (int argc, qse_char_t* argv[]) | ||||
| { | ||||
| 	qse_httpd_t* httpd = QSE_NULL; | ||||
| 	qse_xli_t* xli = QSE_NULL; | ||||
| 	qse_ntime_t tmout; | ||||
| 	int ret = -1, i; | ||||
| 	int trait; | ||||
|  | ||||
| 	if (argc != 2) | ||||
| 	{ | ||||
| 		/* TODO: proper check... */ | ||||
| 		qse_fprintf (QSE_STDERR, QSE_T("Usage: %s -f config-file\n"), argv[0]); | ||||
| 		goto oops; | ||||
| 	} | ||||
|  | ||||
| 	httpd = qse_httpd_openstd (QSE_SIZEOF(server_xtn_t)); | ||||
| 	if (httpd == QSE_NULL) | ||||
| 	{ | ||||
| 		qse_fprintf (QSE_STDERR, QSE_T("Cannot open httpd\n")); | ||||
| 		goto oops; | ||||
| 	} | ||||
|  | ||||
| 	xli = qse_xli_openstd (0); | ||||
| 	if (xli == QSE_NULL) | ||||
| 	{ | ||||
| 		qse_fprintf (QSE_STDERR, QSE_T("Cannot open xli\n")); | ||||
| 		goto oops; | ||||
| 	} | ||||
|  | ||||
| 	qse_xli_getopt (xli, QSE_XLI_TRAIT, &trait); | ||||
| 	trait |= QSE_XLI_KEYNAME; | ||||
| 	qse_xli_setopt (xli, QSE_XLI_TRAIT, &trait); | ||||
| 	if (load_config (httpd, xli, argv[1]) <= -1) goto oops; | ||||
|  | ||||
|  | ||||
| 	g_httpd = httpd; | ||||
| 	setup_signal_handlers (); | ||||
|  | ||||
| 	qse_httpd_setname (httpd, QSE_MT("qsehttpd 1.0")); | ||||
|  | ||||
| 	qse_httpd_getopt (httpd, QSE_HTTPD_TRAIT, &trait); | ||||
| 	trait |= QSE_HTTPD_CGIERRTONUL; | ||||
| 	qse_httpd_setopt (httpd, QSE_HTTPD_TRAIT, &trait); | ||||
|  | ||||
| 	tmout.sec = 10; | ||||
| 	tmout.nsec = 0; | ||||
| 	ret = qse_httpd_loopstd (httpd, &tmout); | ||||
|  | ||||
| 	restore_signal_handlers (); | ||||
| 	g_httpd = QSE_NULL; | ||||
|  | ||||
| 	if (ret <= -1) qse_fprintf (QSE_STDERR, QSE_T("Httpd error - %d\n"), qse_httpd_geterrnum (httpd)); | ||||
|  | ||||
| oops: | ||||
| 	if (xli) qse_xli_close (xli); | ||||
| 	if (httpd) qse_httpd_close (httpd); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| int qse_main (int argc, qse_achar_t* argv[]) | ||||
| { | ||||
| 	int ret; | ||||
|  | ||||
| #if defined(_WIN32) | ||||
| 	char locale[100]; | ||||
| 	UINT codepage; | ||||
| 	WSADATA wsadata; | ||||
|  | ||||
| 	codepage = GetConsoleOutputCP(); | ||||
| 	if (codepage == CP_UTF8) | ||||
| 	{ | ||||
| 		/*SetConsoleOUtputCP (CP_UTF8);*/ | ||||
| 		qse_setdflcmgrbyid (QSE_CMGR_UTF8); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		sprintf (locale, ".%u", (unsigned int)codepage); | ||||
| 		setlocale (LC_ALL, locale); | ||||
| 		qse_setdflcmgrbyid (QSE_CMGR_SLMB); | ||||
| 	} | ||||
|  | ||||
| 	if (WSAStartup (MAKEWORD(2,0), &wsadata) != 0) | ||||
| 	{ | ||||
| 		qse_fprintf (QSE_STDERR, QSE_T("Failed to start up winsock\n")); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| #else | ||||
| 	setlocale (LC_ALL, ""); | ||||
| 	qse_setdflcmgrbyid (QSE_CMGR_SLMB); | ||||
| #endif | ||||
|  | ||||
| #if defined(HAVE_SSL)     | ||||
| 	SSL_load_error_strings (); | ||||
| 	SSL_library_init (); | ||||
| #endif | ||||
|  | ||||
| 	ret = qse_runmain (argc, argv, httpd_main); | ||||
|  | ||||
| #if defined(HAVE_SSL) | ||||
| 	/*ERR_remove_state ();*/ | ||||
| 	ENGINE_cleanup (); | ||||
| 	ERR_free_strings (); | ||||
| 	EVP_cleanup (); | ||||
| 	CRYPTO_cleanup_all_ex_data (); | ||||
| #endif | ||||
|  | ||||
| #if defined(_WIN32) | ||||
| 	WSACleanup ();	 | ||||
| #endif | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
							
								
								
									
										44
									
								
								qse/cmd/http/httpd.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								qse/cmd/http/httpd.conf
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | ||||
| # | ||||
| # this is a sample configuration file for qsehttpd. | ||||
| # | ||||
|  | ||||
| server { | ||||
| 	bind = "0.0.0.0:80"; | ||||
|  | ||||
| 	ssl { | ||||
| 		certificate { | ||||
| 			private = "xxxx"; | ||||
| 			public = "xxxx"; | ||||
| 		}		 | ||||
| 	} | ||||
|  | ||||
| 	root = "/home/www/default"; | ||||
| 	option = "xxxx,xxx,xxxx"; | ||||
|  | ||||
| 	realm { | ||||
| 		name = "xxxxxx"; | ||||
| 		password = "zzzzzzzzzzzzzzzz"; | ||||
| 	} | ||||
|  | ||||
| 	cgi { | ||||
| 		suffix ".ant" = "xxxxx"; | ||||
| 		suffix ".cgi"; | ||||
| 		suffix ".nph"; | ||||
| 	} | ||||
|  | ||||
| 	mime { | ||||
| 		suffix ".jpg" = "image/picture"; | ||||
| 		suffix ".txt" = "text/plain"; | ||||
| 	} | ||||
|  | ||||
| 	# virtual host | ||||
| 	host "www.google.com" { | ||||
| 		root = "/home/www/google";	 | ||||
| 		option = "xxx, xxx, xxxx"; | ||||
|  | ||||
| 		realm { | ||||
| 			name = "www.google.com"; | ||||
| 			password = "zzzzzzzzzzzzzzzz"; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @ -1,296 +0,0 @@ | ||||
|  | ||||
| #include <qse/net/httpd.h> | ||||
| #include <qse/cmn/stdio.h> | ||||
| #include <qse/cmn/main.h> | ||||
| #include <qse/cmn/str.h> | ||||
| #include <qse/cmn/mem.h> | ||||
| #include <qse/cmn/mbwc.h> | ||||
| #include <qse/cmn/time.h> | ||||
|  | ||||
| #include <signal.h> | ||||
| #include <locale.h> | ||||
|  | ||||
| #if defined(_WIN32) | ||||
| #	include <winsock2.h> | ||||
| #	include <windows.h> | ||||
| #	include <tchar.h> | ||||
| #	include <process.h> | ||||
| #elif defined(__OS2__) | ||||
| #	define INCL_DOSPROCESS | ||||
| #	define INCL_DOSEXCEPTIONS | ||||
| #	define INCL_ERRORS | ||||
| #	include <os2.h> | ||||
| #elif defined(__DOS__) | ||||
| #	include <dos.h> | ||||
| #else | ||||
| #	include <unistd.h> | ||||
| #	include <errno.h> | ||||
| #endif | ||||
|  | ||||
|  | ||||
| #if defined(HAVE_SSL) | ||||
| #	include <openssl/ssl.h> | ||||
| #	include <openssl/err.h> | ||||
| #	include <openssl/engine.h> | ||||
| #endif | ||||
|  | ||||
| /* --------------------------------------------------------------------- */ | ||||
|  | ||||
| static qse_httpd_t* g_httpd = QSE_NULL; | ||||
|  | ||||
| static void sigint (int sig) | ||||
| { | ||||
| 	if (g_httpd) qse_httpd_stop (g_httpd); | ||||
| } | ||||
|  | ||||
| /* --------------------------------------------------------------------- */ | ||||
|  | ||||
| typedef struct server_xtn_t server_xtn_t; | ||||
| struct server_xtn_t | ||||
| { | ||||
| 	int tproxy; | ||||
| 	int nodir; /* no directory listing */ | ||||
| 	qse_httpd_server_cbstd_t* orgcbstd; | ||||
| }; | ||||
|  | ||||
| static int makersrc ( | ||||
| 	qse_httpd_t* httpd, qse_httpd_client_t* client, | ||||
| 	qse_htre_t* req, qse_httpd_rsrc_t* rsrc) | ||||
| { | ||||
| 	server_xtn_t* server_xtn; | ||||
|  | ||||
| 	server_xtn = qse_httpd_getserverxtnstd (httpd, client->server); | ||||
|  | ||||
| 	if (server_xtn->tproxy) | ||||
| 	{ | ||||
| 		if (qse_nwadequal(&client->orgdst_addr, &client->local_addr)) /* both equal and error */ | ||||
| 		{ | ||||
| 			/* TODO: implement a better check that the | ||||
| 			 *       destination is not one of the local addresses */ | ||||
|  | ||||
| 			rsrc->type = QSE_HTTPD_RSRC_ERR; | ||||
| 			rsrc->u.err.code = 500; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			rsrc->type = QSE_HTTPD_RSRC_PROXY; | ||||
| 			rsrc->u.proxy.dst = client->orgdst_addr; | ||||
| 			rsrc->u.proxy.src = client->remote_addr; | ||||
| 	 | ||||
| 			if (rsrc->u.proxy.src.type == QSE_NWAD_IN4) | ||||
| 				rsrc->u.proxy.src.u.in4.port = 0; /* reset the port to 0. */ | ||||
| 			else if (rsrc->u.proxy.src.type == QSE_NWAD_IN6) | ||||
| 				rsrc->u.proxy.src.u.in6.port = 0; /* reset the port to 0. */ | ||||
| 		} | ||||
|  | ||||
| 		return 0; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		if (server_xtn->orgcbstd->makersrc (httpd, client, req, rsrc) <= -1) return -1; | ||||
| 		if (server_xtn->nodir && rsrc->type == QSE_HTTPD_RSRC_DIR) | ||||
| 		{ | ||||
| 			/* prohibit no directory listing */ | ||||
| 			if (server_xtn->orgcbstd->freersrc) | ||||
| 				server_xtn->orgcbstd->freersrc (httpd, client, req, rsrc); | ||||
| 			rsrc->type = QSE_HTTPD_RSRC_ERR; | ||||
| 			rsrc->u.err.code = 403; | ||||
| 		} | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void freersrc ( | ||||
| 	qse_httpd_t* httpd, qse_httpd_client_t* client,  | ||||
| 	qse_htre_t* req, qse_httpd_rsrc_t* rsrc) | ||||
| { | ||||
| 	server_xtn_t* server_xtn; | ||||
|  | ||||
| 	server_xtn = qse_httpd_getserverxtnstd (httpd, client->server); | ||||
|  | ||||
| 	if (server_xtn->tproxy) | ||||
| 	{ | ||||
| 		/* nothing to do */ | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		if (server_xtn->orgcbstd->freersrc)  | ||||
| 			server_xtn->orgcbstd->freersrc (httpd, client, req, rsrc); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* --------------------------------------------------------------------- */ | ||||
|  | ||||
| static qse_httpd_server_t* attach_server ( | ||||
| 	qse_httpd_t* httpd, qse_char_t* uri, qse_httpd_server_cbstd_t* cbstd) | ||||
| { | ||||
| 	qse_httpd_server_t* server; | ||||
| 	server_xtn_t* server_xtn; | ||||
| 	int tproxy = 0; | ||||
|  | ||||
| 	static qse_httpd_server_idxstd_t idxstd[] = | ||||
| 	{ | ||||
| 		{ QSE_MT("index.cgi")  }, | ||||
| 		{ QSE_MT("index.html") }, | ||||
| 		{ QSE_NULL             } | ||||
| 	}; | ||||
|  | ||||
| 	if (qse_strzcasecmp (uri, QSE_T("http-tproxy://"), 14) == 0) | ||||
| 	{ | ||||
| 		tproxy = 1; | ||||
| 		qse_strcpy (&uri[4], &uri[11]);  | ||||
| 	} | ||||
|  | ||||
| 	server = qse_httpd_attachserverstd ( | ||||
| 		httpd, uri, QSE_NULL, QSE_SIZEOF(server_xtn_t)); | ||||
| 	if (server == QSE_NULL) return QSE_NULL; | ||||
|  | ||||
| 	/* qse_httpd_getserverxtnstd() returns the pointer to  | ||||
| 	 * the extension space requested above, of the size | ||||
| 	 * QSE_SIZEOF(server_xtn_t) */ | ||||
| 	server_xtn = qse_httpd_getserverxtnstd (httpd, server); | ||||
| 	server_xtn->tproxy = tproxy; | ||||
|  | ||||
| 	/* qse_httpd_getserverxtn() returns the pointer to the | ||||
| 	 * extension space created by qse_httpd_attachserverstd() | ||||
| 	 * internally. | ||||
| 	 */ | ||||
| 	/* remember the callback set in qse_httpd_attachserverstd() */ | ||||
| 	qse_httpd_getserveroptstd ( | ||||
| 		httpd, server,  | ||||
| 		QSE_HTTPD_SERVER_CBSTD, (void**)&server_xtn->orgcbstd); | ||||
| 	/* override it with a new callback for chaining */ | ||||
| 	qse_httpd_setserveroptstd ( | ||||
| 		httpd, server, | ||||
| 		QSE_HTTPD_SERVER_CBSTD, cbstd); | ||||
|  | ||||
| 	/* totally override idxstd without remembering the old idxstd */ | ||||
| 	qse_httpd_setserveroptstd ( | ||||
| 		httpd, server, | ||||
| 		QSE_HTTPD_SERVER_IDXSTD, idxstd); | ||||
|  | ||||
| 	qse_httpd_setserveroptstd ( | ||||
| 		httpd, server, QSE_HTTPD_SERVER_DIRCSS,  | ||||
| 		QSE_MT("<style type='text/css'>body { background-color:#d0e4fe; font-size: 0.9em; } div.header { font-weight: bold; margin-bottom: 5px; } div.footer { border-top: 1px solid #99AABB; text-align: right; } table { font-size: 0.9em; } td { white-space: nowrap; } td.size { text-align: right; }</style>")); | ||||
|  | ||||
| 	qse_httpd_setserveroptstd ( | ||||
| 		httpd, server, QSE_HTTPD_SERVER_ERRCSS,  | ||||
| 		QSE_MT("<style type='text/css'>body { background-color:#d0e4fe; font-size: 0.9em; } div.header { font-weight: bold; margin-bottom: 5px; } div.footer { border-top: 1px solid #99AABB; text-align: right; }</style>")); | ||||
| 	 | ||||
| 	return server; | ||||
| } | ||||
| /* --------------------------------------------------------------------- */ | ||||
| static int httpd_main (int argc, qse_char_t* argv[]) | ||||
| { | ||||
| 	qse_httpd_t* httpd = QSE_NULL; | ||||
| 	qse_ntime_t tmout; | ||||
| 	int ret = -1, i; | ||||
| 	int trait; | ||||
| 	static qse_httpd_server_cbstd_t cbstd = { makersrc, freersrc }; | ||||
|  | ||||
| 	if (argc <= 1) | ||||
| 	{ | ||||
| 		qse_fprintf (QSE_STDERR, QSE_T("Usage: %s <listener_uri> ...\n"), argv[0]); | ||||
| 		goto oops; | ||||
| 	} | ||||
|  | ||||
| 	httpd = qse_httpd_openstd (QSE_SIZEOF(server_xtn_t)); | ||||
| 	if (httpd == QSE_NULL) | ||||
| 	{ | ||||
| 		qse_fprintf (QSE_STDERR, QSE_T("Cannot open httpd\n")); | ||||
| 		goto oops; | ||||
| 	} | ||||
|  | ||||
| 	for (i = 1; i < argc; i++) | ||||
| 	{ | ||||
| 		if (attach_server (httpd, argv[i], &cbstd) == QSE_NULL) | ||||
| 		{ | ||||
| 			qse_fprintf (QSE_STDERR, | ||||
| 				QSE_T("Failed to add httpd listener - %s\n"), argv[i]); | ||||
| 			goto oops; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	g_httpd = httpd; | ||||
| 	signal (SIGINT, sigint); | ||||
| #if defined(SIGPIPE) | ||||
| 	signal (SIGPIPE, SIG_IGN); | ||||
| #endif | ||||
|  | ||||
| 	qse_httpd_setname (httpd, QSE_MT("qsehttpd 1.0")); | ||||
|  | ||||
| 	trait = QSE_HTTPD_CGIERRTONUL; | ||||
| 	qse_httpd_setopt (httpd, QSE_HTTPD_TRAIT, &trait); | ||||
|  | ||||
| 	tmout.sec = 10; | ||||
| 	tmout.nsec = 0; | ||||
| 	ret = qse_httpd_loopstd (httpd, &tmout); | ||||
|  | ||||
| 	signal (SIGINT, SIG_DFL); | ||||
| #if defined(SIGPIPE) | ||||
| 	signal (SIGPIPE, SIG_DFL); | ||||
| #endif | ||||
| 	g_httpd = QSE_NULL; | ||||
|  | ||||
| 	if (ret <= -1) qse_fprintf (QSE_STDERR, QSE_T("Httpd error\n")); | ||||
|  | ||||
| oops: | ||||
| 	if (httpd) qse_httpd_close (httpd); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| int qse_main (int argc, qse_achar_t* argv[]) | ||||
| { | ||||
| 	int ret; | ||||
|  | ||||
| #if defined(_WIN32) | ||||
| 	char locale[100]; | ||||
| 	UINT codepage; | ||||
| 	WSADATA wsadata; | ||||
|  | ||||
| 	codepage = GetConsoleOutputCP(); | ||||
| 	if (codepage == CP_UTF8) | ||||
| 	{ | ||||
| 		/*SetConsoleOUtputCP (CP_UTF8);*/ | ||||
| 		qse_setdflcmgrbyid (QSE_CMGR_UTF8); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		sprintf (locale, ".%u", (unsigned int)codepage); | ||||
| 		setlocale (LC_ALL, locale); | ||||
| 		qse_setdflcmgrbyid (QSE_CMGR_SLMB); | ||||
| 	} | ||||
|  | ||||
| 	if (WSAStartup (MAKEWORD(2,0), &wsadata) != 0) | ||||
| 	{ | ||||
| 		qse_fprintf (QSE_STDERR, QSE_T("Failed to start up winsock\n")); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| #else | ||||
| 	setlocale (LC_ALL, ""); | ||||
| 	qse_setdflcmgrbyid (QSE_CMGR_SLMB); | ||||
| #endif | ||||
|  | ||||
| #if defined(HAVE_SSL)     | ||||
| 	SSL_load_error_strings (); | ||||
| 	SSL_library_init (); | ||||
| #endif | ||||
|  | ||||
| 	ret = qse_runmain (argc, argv, httpd_main); | ||||
|  | ||||
| #if defined(HAVE_SSL) | ||||
| 	/*ERR_remove_state ();*/ | ||||
| 	ENGINE_cleanup (); | ||||
| 	ERR_free_strings (); | ||||
| 	EVP_cleanup (); | ||||
| 	CRYPTO_cleanup_all_ex_data (); | ||||
| #endif | ||||
|  | ||||
| #if defined(_WIN32) | ||||
| 	WSACleanup ();	 | ||||
| #endif | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| @ -52,6 +52,7 @@ | ||||
|  | ||||
| static qse_char_t* g_input_file = QSE_NULL; | ||||
| static qse_char_t* g_output_file = QSE_NULL; | ||||
| static qse_char_t* g_lookup_key = QSE_NULL; | ||||
| static qse_ulong_t g_memlimit = 0; | ||||
| static int g_trait = 0; | ||||
|  | ||||
| @ -122,7 +123,7 @@ static void print_usage (QSE_FILE* out, int argc, qse_char_t* argv[]) | ||||
| { | ||||
| 	const qse_char_t* b = qse_basename (argv[0]); | ||||
|  | ||||
| 	qse_fprintf (out, QSE_T("USAGE: %s [options] -f input-file\n"), b); | ||||
| 	qse_fprintf (out, QSE_T("USAGE: %s [options] -f input-file [key]\n"), b); | ||||
|  | ||||
| 	qse_fprintf (out, QSE_T("options as follows:\n")); | ||||
| 	qse_fprintf (out, QSE_T(" -h/--help                 show this message\n")); | ||||
| @ -201,7 +202,7 @@ static int handle_args (int argc, qse_char_t* argv[]) | ||||
| 				break; | ||||
|  | ||||
| 			case QSE_T('n'): | ||||
| 				g_trait |= QSE_XLI_NAMEDKEY; | ||||
| 				g_trait |= QSE_XLI_KEYNAME; | ||||
| 				break; | ||||
|  | ||||
| 			case QSE_T('m'): | ||||
| @ -251,6 +252,14 @@ static int handle_args (int argc, qse_char_t* argv[]) | ||||
| 		goto oops; | ||||
| 	} | ||||
|  | ||||
| 	if (opt.ind < argc) g_lookup_key = argv[opt.ind++]; | ||||
|  | ||||
| 	if (opt.ind < argc) | ||||
| 	{ | ||||
| 		print_usage (QSE_STDERR, argc, argv); | ||||
| 		goto oops; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
|  | ||||
| oops: | ||||
| @ -287,7 +296,6 @@ static int xli_main (int argc, qse_char_t* argv[]) | ||||
| { | ||||
| 	qse_mmgr_t* mmgr = QSE_MMGR_GETDFL(); | ||||
| 	qse_xli_t* xli = QSE_NULL; | ||||
| 	qse_fs_t* fs = QSE_NULL; | ||||
| 	qse_xli_iostd_t in, out; | ||||
| 	int ret = -1; | ||||
|  | ||||
| @ -361,11 +369,41 @@ static int xli_main (int argc, qse_char_t* argv[]) | ||||
| 		goto oops; | ||||
| 	} | ||||
|  | ||||
| 	if (g_lookup_key) | ||||
| 	{ | ||||
| 		qse_xli_pair_t* pair; | ||||
| 		pair = qse_xli_findpairbyname (xli, QSE_NULL, g_lookup_key); | ||||
| 		if (pair == QSE_NULL) | ||||
| 		{ | ||||
| 			qse_fprintf (QSE_STDERR,  | ||||
| 				QSE_T("ERROR: cannot find %s - %s \n"), | ||||
| 				g_lookup_key, | ||||
| 				qse_xli_geterrmsg(xli) | ||||
| 			); | ||||
| 			goto oops; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			if (pair->val->type == QSE_XLI_STR) | ||||
| 			{ | ||||
| 				qse_xli_str_t* str = (qse_xli_str_t*)pair->val; | ||||
| 				qse_printf (QSE_T("[%.*s]\n"), (int)str->len, str->ptr); | ||||
| 			} | ||||
| 			else if (pair->val->type == QSE_XLI_NIL) | ||||
| 			{ | ||||
| 				qse_printf (QSE_T("#NIL\n")); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				qse_printf (QSE_T("#LIST\n")); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	ret = 0; | ||||
|  | ||||
| oops: | ||||
| 	if (xli) qse_xli_close (xli); | ||||
| 	if (fs) qse_fs_close (fs); | ||||
| 	if (xma_mmgr.ctx) qse_xma_close (xma_mmgr.ctx); | ||||
|  | ||||
| #if defined(QSE_BUILD_DEBUG) | ||||
|  | ||||
							
								
								
									
										10
									
								
								qse/configure
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								qse/configure
									
									
									
									
										vendored
									
									
								
							| @ -20893,7 +20893,7 @@ QSE_PROJECT_AUTHOR="${PACKAGE_BUGREPORT}" | ||||
| QSE_PROJECT_URL="${PACKAGE_URL}" | ||||
|  | ||||
|  | ||||
| ac_config_files="$ac_config_files Makefile README include/Makefile include/qse/Makefile include/qse/cmn/Makefile include/qse/awk/Makefile include/qse/sed/Makefile include/qse/xli/Makefile include/qse/net/Makefile lib/Makefile lib/cmn/Makefile lib/awk/Makefile lib/sed/Makefile lib/xli/Makefile lib/net/Makefile cmd/Makefile cmd/awk/Makefile cmd/sed/Makefile cmd/xli/Makefile cmd/net/Makefile samples/Makefile samples/cmn/Makefile samples/awk/Makefile samples/sed/Makefile samples/net/Makefile regress/Makefile regress/awk/Makefile regress/awk/regress.sh regress/sed/Makefile regress/sed/regress.sh doc/Makefile doc/Doxyfile tools/Makefile" | ||||
| ac_config_files="$ac_config_files Makefile README include/Makefile include/qse/Makefile include/qse/cmn/Makefile include/qse/awk/Makefile include/qse/sed/Makefile include/qse/xli/Makefile include/qse/http/Makefile lib/Makefile lib/cmn/Makefile lib/awk/Makefile lib/sed/Makefile lib/xli/Makefile lib/http/Makefile cmd/Makefile cmd/awk/Makefile cmd/sed/Makefile cmd/xli/Makefile cmd/http/Makefile samples/Makefile samples/cmn/Makefile samples/awk/Makefile samples/sed/Makefile samples/http/Makefile regress/Makefile regress/awk/Makefile regress/awk/regress.sh regress/sed/Makefile regress/sed/regress.sh doc/Makefile doc/Doxyfile tools/Makefile" | ||||
|  | ||||
| cat >confcache <<\_ACEOF | ||||
| # This file is a shell script that caches the results of configure | ||||
| @ -22073,23 +22073,23 @@ do | ||||
|     "include/qse/awk/Makefile") CONFIG_FILES="$CONFIG_FILES include/qse/awk/Makefile" ;; | ||||
|     "include/qse/sed/Makefile") CONFIG_FILES="$CONFIG_FILES include/qse/sed/Makefile" ;; | ||||
|     "include/qse/xli/Makefile") CONFIG_FILES="$CONFIG_FILES include/qse/xli/Makefile" ;; | ||||
|     "include/qse/net/Makefile") CONFIG_FILES="$CONFIG_FILES include/qse/net/Makefile" ;; | ||||
|     "include/qse/http/Makefile") CONFIG_FILES="$CONFIG_FILES include/qse/http/Makefile" ;; | ||||
|     "lib/Makefile") CONFIG_FILES="$CONFIG_FILES lib/Makefile" ;; | ||||
|     "lib/cmn/Makefile") CONFIG_FILES="$CONFIG_FILES lib/cmn/Makefile" ;; | ||||
|     "lib/awk/Makefile") CONFIG_FILES="$CONFIG_FILES lib/awk/Makefile" ;; | ||||
|     "lib/sed/Makefile") CONFIG_FILES="$CONFIG_FILES lib/sed/Makefile" ;; | ||||
|     "lib/xli/Makefile") CONFIG_FILES="$CONFIG_FILES lib/xli/Makefile" ;; | ||||
|     "lib/net/Makefile") CONFIG_FILES="$CONFIG_FILES lib/net/Makefile" ;; | ||||
|     "lib/http/Makefile") CONFIG_FILES="$CONFIG_FILES lib/http/Makefile" ;; | ||||
|     "cmd/Makefile") CONFIG_FILES="$CONFIG_FILES cmd/Makefile" ;; | ||||
|     "cmd/awk/Makefile") CONFIG_FILES="$CONFIG_FILES cmd/awk/Makefile" ;; | ||||
|     "cmd/sed/Makefile") CONFIG_FILES="$CONFIG_FILES cmd/sed/Makefile" ;; | ||||
|     "cmd/xli/Makefile") CONFIG_FILES="$CONFIG_FILES cmd/xli/Makefile" ;; | ||||
|     "cmd/net/Makefile") CONFIG_FILES="$CONFIG_FILES cmd/net/Makefile" ;; | ||||
|     "cmd/http/Makefile") CONFIG_FILES="$CONFIG_FILES cmd/http/Makefile" ;; | ||||
|     "samples/Makefile") CONFIG_FILES="$CONFIG_FILES samples/Makefile" ;; | ||||
|     "samples/cmn/Makefile") CONFIG_FILES="$CONFIG_FILES samples/cmn/Makefile" ;; | ||||
|     "samples/awk/Makefile") CONFIG_FILES="$CONFIG_FILES samples/awk/Makefile" ;; | ||||
|     "samples/sed/Makefile") CONFIG_FILES="$CONFIG_FILES samples/sed/Makefile" ;; | ||||
|     "samples/net/Makefile") CONFIG_FILES="$CONFIG_FILES samples/net/Makefile" ;; | ||||
|     "samples/http/Makefile") CONFIG_FILES="$CONFIG_FILES samples/http/Makefile" ;; | ||||
|     "regress/Makefile") CONFIG_FILES="$CONFIG_FILES regress/Makefile" ;; | ||||
|     "regress/awk/Makefile") CONFIG_FILES="$CONFIG_FILES regress/awk/Makefile" ;; | ||||
|     "regress/awk/regress.sh") CONFIG_FILES="$CONFIG_FILES regress/awk/regress.sh" ;; | ||||
|  | ||||
| @ -529,23 +529,23 @@ AC_CONFIG_FILES([ | ||||
| 	include/qse/awk/Makefile | ||||
| 	include/qse/sed/Makefile | ||||
| 	include/qse/xli/Makefile | ||||
| 	include/qse/net/Makefile | ||||
| 	include/qse/http/Makefile | ||||
| 	lib/Makefile  | ||||
| 	lib/cmn/Makefile  | ||||
| 	lib/awk/Makefile  | ||||
| 	lib/sed/Makefile  | ||||
| 	lib/xli/Makefile  | ||||
| 	lib/net/Makefile  | ||||
| 	lib/http/Makefile  | ||||
| 	cmd/Makefile  | ||||
| 	cmd/awk/Makefile  | ||||
| 	cmd/sed/Makefile  | ||||
| 	cmd/xli/Makefile  | ||||
| 	cmd/net/Makefile  | ||||
| 	cmd/http/Makefile  | ||||
| 	samples/Makefile  | ||||
| 	samples/cmn/Makefile  | ||||
| 	samples/awk/Makefile  | ||||
| 	samples/sed/Makefile  | ||||
| 	samples/net/Makefile | ||||
| 	samples/http/Makefile | ||||
| 	regress/Makefile | ||||
| 	regress/awk/Makefile | ||||
| 	regress/awk/regress.sh | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| SUBDIRS = cmn awk sed net | ||||
| SUBDIRS = cmn awk sed http | ||||
|  | ||||
| pkgincludedir = $(includedir)/qse | ||||
|  | ||||
|  | ||||
| @ -306,7 +306,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 http | ||||
| pkginclude_HEADERS = conf-msw.h conf-os2.h conf-dos.h conf-vms.h \ | ||||
| 	conf-mac.h conf-inf.h types.h macros.h pack1.h unpack.h \ | ||||
| 	$(am__append_1) | ||||
|  | ||||
| @ -24,22 +24,37 @@ | ||||
| #include <qse/types.h> | ||||
| #include <qse/macros.h> | ||||
|  | ||||
| typedef struct qse_muri_t qse_muri_t; | ||||
| typedef struct qse_wuri_t qse_wuri_t; | ||||
|  | ||||
| typedef struct qse_uri_t qse_uri_t; | ||||
|  | ||||
| struct qse_uri_t | ||||
| struct qse_muri_t | ||||
| { | ||||
| 	qse_cptl_t scheme; | ||||
| 	qse_mcstr_t scheme; | ||||
| 	struct | ||||
| 	{ | ||||
| 		qse_cptl_t user; | ||||
| 		qse_cptl_t pass; | ||||
| 		qse_mcstr_t user; | ||||
| 		qse_mcstr_t pass; | ||||
| 	} auth; | ||||
| 	qse_cptl_t host; | ||||
| 	qse_cptl_t port; | ||||
| 	qse_cptl_t path; | ||||
| 	qse_cptl_t query; | ||||
| 	qse_cptl_t frag; | ||||
| 	qse_mcstr_t host; | ||||
| 	qse_mcstr_t port; | ||||
| 	qse_mcstr_t path; | ||||
| 	qse_mcstr_t query; | ||||
| 	qse_mcstr_t frag; | ||||
| }; | ||||
|  | ||||
| struct qse_wuri_t | ||||
| { | ||||
| 	qse_wcstr_t scheme; | ||||
| 	struct | ||||
| 	{ | ||||
| 		qse_wcstr_t user; | ||||
| 		qse_wcstr_t pass; | ||||
| 	} auth; | ||||
| 	qse_wcstr_t host; | ||||
| 	qse_wcstr_t port; | ||||
| 	qse_wcstr_t path; | ||||
| 	qse_wcstr_t query; | ||||
| 	qse_wcstr_t frag; | ||||
| }; | ||||
|  | ||||
| enum qse_mbstouri_flag_t | ||||
| @ -60,10 +75,12 @@ enum qse_wcstouri_flag_t | ||||
| #	define QSE_STRTOURI_NOAUTH  QSE_MBSTOURI_NOAUTH | ||||
| #	define QSE_STRTOURI_NOQUERY QSE_MBSTOURI_NOQUERY | ||||
| #	define QSE_STRTOURI_NOFRAG  QSE_MBSTOURI_NOFRAG | ||||
| 	typedef qse_muri_t qse_uri_t; | ||||
| #else | ||||
| #	define QSE_STRTOURI_NOAUTH  QSE_WCSTOURI_NOAUTH | ||||
| #	define QSE_STRTOURI_NOQUERY QSE_WCSTOURI_NOQUERY | ||||
| #	define QSE_STRTOURI_NOFRAG  QSE_WCSTOURI_NOFRAG | ||||
| 	typedef qse_wuri_t qse_uri_t; | ||||
| #endif | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| @ -72,13 +89,13 @@ extern "C" { | ||||
|  | ||||
| QSE_EXPORT int qse_mbstouri ( | ||||
| 	const qse_mchar_t* str, | ||||
| 	qse_uri_t*         uri, | ||||
| 	qse_muri_t*        uri, | ||||
| 	int                flags | ||||
| ); | ||||
|  | ||||
| QSE_EXPORT int qse_wcstouri ( | ||||
| 	const qse_wchar_t* str, | ||||
| 	qse_uri_t*         uri, | ||||
| 	qse_wuri_t*        uri, | ||||
| 	int                flags | ||||
| ); | ||||
|  | ||||
|  | ||||
							
								
								
									
										4
									
								
								qse/include/qse/http/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								qse/include/qse/http/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| pkgincludedir= $(includedir)/qse/http | ||||
| pkginclude_HEADERS = http.h htre.h htrd.h httpd.h std.h upxd.h | ||||
|  | ||||
|  | ||||
| @ -33,7 +33,7 @@ PRE_UNINSTALL = : | ||||
| POST_UNINSTALL = : | ||||
| build_triplet = @build@ | ||||
| host_triplet = @host@ | ||||
| subdir = include/qse/net | ||||
| subdir = include/qse/http | ||||
| DIST_COMMON = $(pkginclude_HEADERS) $(srcdir)/Makefile.am \
 | ||||
| 	$(srcdir)/Makefile.in | ||||
| ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 | ||||
| @ -89,7 +89,7 @@ HEADERS = $(pkginclude_HEADERS) | ||||
| ETAGS = etags | ||||
| CTAGS = ctags | ||||
| DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) | ||||
| pkgincludedir = $(includedir)/qse/net | ||||
| pkgincludedir = $(includedir)/qse/http | ||||
| ACLOCAL = @ACLOCAL@ | ||||
| AMTAR = @AMTAR@ | ||||
| AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ | ||||
| @ -264,7 +264,7 @@ target_alias = @target_alias@ | ||||
| top_build_prefix = @top_build_prefix@ | ||||
| top_builddir = @top_builddir@ | ||||
| top_srcdir = @top_srcdir@ | ||||
| pkginclude_HEADERS = http.h htre.h htrd.h httpd.h upxd.h | ||||
| pkginclude_HEADERS = http.h htre.h htrd.h httpd.h std.h upxd.h | ||||
| all: all-am | ||||
| 
 | ||||
| .SUFFIXES: | ||||
| @ -277,9 +277,9 @@ $(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps) | ||||
| 	      exit 1;; \
 | ||||
| 	  esac; \
 | ||||
| 	done; \
 | ||||
| 	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign include/qse/net/Makefile'; \
 | ||||
| 	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign include/qse/http/Makefile'; \
 | ||||
| 	$(am__cd) $(top_srcdir) && \
 | ||||
| 	  $(AUTOMAKE) --foreign include/qse/net/Makefile | ||||
| 	  $(AUTOMAKE) --foreign include/qse/http/Makefile | ||||
| .PRECIOUS: Makefile | ||||
| Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status | ||||
| 	@case '$?' in \
 | ||||
| @ -18,11 +18,11 @@ | ||||
|     License along with QSE. If not, see <htrd://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _QSE_NET_HTRD_H_ | ||||
| #define _QSE_NET_HTRD_H_ | ||||
| #ifndef _QSE_HTTP_HTRD_H_ | ||||
| #define _QSE_HTTP_HTRD_H_ | ||||
| 
 | ||||
| #include <qse/net/http.h> | ||||
| #include <qse/net/htre.h> | ||||
| #include <qse/http/http.h> | ||||
| #include <qse/http/htre.h> | ||||
| 
 | ||||
| typedef struct qse_htrd_t qse_htrd_t; | ||||
| 
 | ||||
| @ -18,10 +18,10 @@ | ||||
|     License along with QSE. If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _QSE_NET_HTRE_H_ | ||||
| #define _QSE_NET_HTRE_H_ | ||||
| #ifndef _QSE_HTTP_HTRE_H_ | ||||
| #define _QSE_HTTP_HTRE_H_ | ||||
| 
 | ||||
| #include <qse/net/http.h> | ||||
| #include <qse/http/http.h> | ||||
| #include <qse/cmn/htb.h> | ||||
| #include <qse/cmn/str.h> | ||||
| 
 | ||||
| @ -18,8 +18,8 @@ | ||||
|     License along with QSE. If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _QSE_NET_HTTP_H_ | ||||
| #define _QSE_NET_HTTP_H_ | ||||
| #ifndef _QSE_HTTP_HTTP_H_ | ||||
| #define _QSE_HTTP_HTTP_H_ | ||||
| 
 | ||||
| /** @file
 | ||||
|  * This file provides basic data types and functions for the http protocol. | ||||
| @ -18,17 +18,18 @@ | ||||
|     License along with QSE. If not, see <htrd://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _QSE_NET_HTTPD_H_ | ||||
| #define _QSE_NET_HTTPD_H_ | ||||
| #ifndef _QSE_HTTP_HTTPD_H_ | ||||
| #define _QSE_HTTP_HTTPD_H_ | ||||
| 
 | ||||
| #include <qse/types.h> | ||||
| #include <qse/macros.h> | ||||
| #include <qse/net/htre.h> | ||||
| #include <qse/net/htrd.h> | ||||
| #include <qse/http/htre.h> | ||||
| #include <qse/http/htrd.h> | ||||
| #include <qse/cmn/nwad.h> | ||||
| #include <qse/cmn/time.h> | ||||
| 
 | ||||
| typedef struct qse_httpd_t        qse_httpd_t; | ||||
| typedef struct qse_httpd_mate_t   qse_httpd_mate_t; | ||||
| typedef struct qse_httpd_server_t qse_httpd_server_t; | ||||
| typedef struct qse_httpd_client_t qse_httpd_client_t; | ||||
| 
 | ||||
| @ -300,17 +301,28 @@ struct qse_httpd_task_t | ||||
| 	qse_httpd_task_t*     next; | ||||
| }; | ||||
| 
 | ||||
| enum qse_httpd_sctype_t  | ||||
| enum qse_httpd_mate_type_t  | ||||
| { | ||||
| 	QSE_HTTPD_SERVER, | ||||
| 	QSE_HTTPD_CLIENT | ||||
| }; | ||||
| typedef enum qse_httpd_sctype_t  qse_httpd_sctype_t; | ||||
| typedef enum qse_httpd_mate_type_t  qse_httpd_mate_type_t; | ||||
| 
 | ||||
| /* it contains header fields common between 
 | ||||
|  * qse_httpd_cleint_t and qse_httpd_server_t. */ | ||||
| #define QSE_HTTPD_MATE_HDR \ | ||||
| 	qse_httpd_mate_type_t type | ||||
| 
 | ||||
| struct qse_httpd_mate_t | ||||
| { | ||||
| 	/* == PRIVATE == */ | ||||
| 	QSE_HTTPD_MATE_HDR; | ||||
| }; | ||||
| 
 | ||||
| struct qse_httpd_client_t | ||||
| { | ||||
| 	/* == PRIVATE == */ | ||||
| 	qse_httpd_sctype_t       type; | ||||
| 	QSE_HTTPD_MATE_HDR; | ||||
| 
 | ||||
| 	/* == PUBLIC  == */ | ||||
| 	qse_ubi_t                handle; | ||||
| @ -352,20 +364,34 @@ typedef void (*qse_httpd_server_predetach_t) ( | ||||
| 	qse_httpd_server_t* server | ||||
| ); | ||||
| 
 | ||||
| struct qse_httpd_server_t | ||||
| { | ||||
| 	qse_httpd_sctype_t       type; | ||||
| typedef void (*qse_httpd_server_reconfig_t) ( | ||||
| 	qse_httpd_t*        httpd, | ||||
| 	qse_httpd_server_t* server | ||||
| ); | ||||
| 
 | ||||
| 	/* ---------------------------------------------- */ | ||||
| 	int          flags; | ||||
| typedef struct qse_httpd_server_dope_t qse_httpd_server_dope_t; | ||||
| 
 | ||||
| struct qse_httpd_server_dope_t | ||||
| { | ||||
| 	int          flags; /* bitwise-ORed of qse_httpd_server_flag_t */ | ||||
| 	qse_nwad_t   nwad; /* binding address */ | ||||
| 	unsigned int nwif; /* interface number to bind to */ | ||||
| 	qse_httpd_server_predetach_t predetach; /* executed when the server is detached */ | ||||
| 	qse_httpd_server_reconfig_t reconfig; /* executed when reconfiguration is requested */ | ||||
| }; | ||||
| 
 | ||||
| struct qse_httpd_server_t | ||||
| { | ||||
| 	/* == PRIVATE == */ | ||||
| 	QSE_HTTPD_MATE_HDR; | ||||
| 
 | ||||
| 	/* provided by a user for attaching */ | ||||
| 	qse_httpd_server_dope_t dope; | ||||
| 
 | ||||
| 	/* set by server.open callback */ | ||||
| 	qse_ubi_t  handle; | ||||
| 
 | ||||
| 	/* private  */ | ||||
| 	qse_httpd_server_predetach_t predetach; | ||||
| 	qse_httpd_server_t*   next; | ||||
| 	qse_httpd_server_t*   prev; | ||||
| }; | ||||
| @ -480,54 +506,6 @@ struct qse_httpd_ecb_t | ||||
| 	qse_httpd_ecb_t* next; | ||||
| }; | ||||
| 
 | ||||
| typedef struct qse_httpd_server_cbstd_t qse_httpd_server_cbstd_t; | ||||
| struct qse_httpd_server_cbstd_t | ||||
| { | ||||
| 	int (*makersrc) (qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req, qse_httpd_rsrc_t* rsrc); /* required */ | ||||
| 	void (*freersrc) (qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req, qse_httpd_rsrc_t* rsrc); /* optional */ | ||||
| }; | ||||
| 
 | ||||
| typedef struct qse_httpd_server_cgistd_t qse_httpd_server_cgistd_t; | ||||
| struct qse_httpd_server_cgistd_t | ||||
| { | ||||
| 	const qse_mchar_t*  ext; | ||||
| 	qse_size_t          len; | ||||
| 	int                 nph; | ||||
| 	const qse_mchar_t*  shebang; /* optional, can be #QSE_NULL */ | ||||
| }; | ||||
| 
 | ||||
| typedef struct qse_httpd_server_mimestd_t qse_httpd_server_mimestd_t; | ||||
| struct qse_httpd_server_mimestd_t | ||||
| { | ||||
| 	const qse_mchar_t* ext; | ||||
| 	const qse_mchar_t* type; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * The qse_httpd_server_idxstd_t type defines a structure to hold | ||||
|  * an index file name. | ||||
|  */ | ||||
| typedef struct qse_httpd_server_idxstd_t qse_httpd_server_idxstd_t; | ||||
| struct qse_httpd_server_idxstd_t | ||||
| { | ||||
| 	const qse_mchar_t* name; | ||||
| }; | ||||
| 
 | ||||
| enum qse_httpd_server_optstd_t | ||||
| { | ||||
| 	QSE_HTTPD_SERVER_DOCROOT = 0, /* const qse_mchar_t* */ | ||||
| 	QSE_HTTPD_SERVER_REALM,       /* const qse_mchar_t* */ | ||||
| 	QSE_HTTPD_SERVER_AUTH,        /* const qse_mchar_t* */ | ||||
| 	QSE_HTTPD_SERVER_DIRCSS,      /* const qse_mchar_t* */ | ||||
| 	QSE_HTTPD_SERVER_ERRCSS,      /* const qse_mchar_t* */ | ||||
| 
 | ||||
| 	QSE_HTTPD_SERVER_CBSTD,       /* qse_httpd_server_cbstd_t* */ | ||||
| 	QSE_HTTPD_SERVER_CGISTD,      /* qse_httpd_server_cgistd_t[] */ | ||||
| 	QSE_HTTPD_SERVER_MIMESTD,     /* qse_httpd_server_mimestd_t[] */ | ||||
| 	QSE_HTTPD_SERVER_IDXSTD       /* qse_httpd_server_idxstd_t[] */ | ||||
| }; | ||||
| typedef enum qse_httpd_server_optstd_t qse_httpd_server_optstd_t; | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| @ -610,12 +588,15 @@ QSE_EXPORT void qse_httpd_stop ( | ||||
| 	qse_httpd_t* httpd | ||||
| ); | ||||
| 
 | ||||
| QSE_EXPORT void qse_httpd_reconfig ( | ||||
| 	qse_httpd_t* httpd | ||||
| ); | ||||
| 
 | ||||
| #define qse_httpd_getserverxtn(httpd,server) ((void*)(server+1)) | ||||
| 
 | ||||
| QSE_EXPORT qse_httpd_server_t* qse_httpd_attachserver ( | ||||
| 	qse_httpd_t*                   httpd, | ||||
| 	const qse_httpd_server_t*    tmpl, | ||||
| 	qse_httpd_server_predetach_t predetach,	 | ||||
| 	const qse_httpd_server_dope_t* dope, | ||||
| 	qse_size_t                     xtnsize | ||||
| ); | ||||
| 
 | ||||
| @ -804,6 +785,11 @@ QSE_EXPORT void* qse_httpd_allocmem ( | ||||
| 	qse_size_t   size | ||||
| ); | ||||
| 
 | ||||
| QSE_EXPORT void* qse_httpd_callocmem ( | ||||
| 	qse_httpd_t* httpd,  | ||||
| 	qse_size_t   size | ||||
| ); | ||||
| 
 | ||||
| QSE_EXPORT void* qse_httpd_reallocmem ( | ||||
| 	qse_httpd_t* httpd, | ||||
| 	void*        ptr, | ||||
| @ -815,53 +801,17 @@ QSE_EXPORT void qse_httpd_freemem ( | ||||
| 	void*        ptr | ||||
| ); | ||||
| 
 | ||||
| /* -------------------------------------------- */ | ||||
| 
 | ||||
| QSE_EXPORT qse_httpd_t* qse_httpd_openstd ( | ||||
| 	qse_size_t xtnsize | ||||
| ); | ||||
| 
 | ||||
| QSE_EXPORT qse_httpd_t* qse_httpd_openstdwithmmgr ( | ||||
| 	qse_mmgr_t* mmgr, | ||||
| 	qse_size_t  xtnsize | ||||
| ); | ||||
| 
 | ||||
| QSE_EXPORT void* qse_httpd_getxtnstd ( | ||||
| 	qse_httpd_t* httpd | ||||
| ); | ||||
| 
 | ||||
| QSE_EXPORT qse_httpd_server_t* qse_httpd_attachserverstd ( | ||||
| QSE_EXPORT qse_mchar_t* qse_httpd_strtombsdup ( | ||||
| 	qse_httpd_t* httpd,  | ||||
| 	const qse_char_t*            uri, | ||||
| 	qse_httpd_server_predetach_t predetach,	 | ||||
| 	qse_size_t                   xtnsize | ||||
| 	const qse_char_t*  str | ||||
| ); | ||||
| 
 | ||||
| QSE_EXPORT int qse_httpd_getserveroptstd ( | ||||
| QSE_EXPORT qse_mchar_t* qse_httpd_strntombsdup ( | ||||
| 	qse_httpd_t*       httpd,  | ||||
| 	qse_httpd_server_t*       server, | ||||
| 	qse_httpd_server_optstd_t id, | ||||
| 	void*                     value | ||||
| 	const qse_char_t*  str, | ||||
| 	qse_size_t         len | ||||
| ); | ||||
| 
 | ||||
| QSE_EXPORT int qse_httpd_setserveroptstd ( | ||||
| 	qse_httpd_t*              httpd, | ||||
| 	qse_httpd_server_t*       server, | ||||
| 	qse_httpd_server_optstd_t id, | ||||
| 	const void*               value | ||||
| ); | ||||
| 
 | ||||
| QSE_EXPORT void* qse_httpd_getserverxtnstd ( | ||||
| 	qse_httpd_t*         httpd, | ||||
| 	qse_httpd_server_t*  server | ||||
| ); | ||||
| 
 | ||||
| QSE_EXPORT int qse_httpd_loopstd ( | ||||
| 	qse_httpd_t*       httpd,  | ||||
| 	const qse_ntime_t* tmout | ||||
| ); | ||||
| 
 | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| @ -18,8 +18,8 @@ | ||||
|     License along with QSE. If not, see <htrd://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _QSE_NET_UPXD_H_ | ||||
| #define _QSE_NET_UPXD_H_ | ||||
| #ifndef _QSE_HTTP_UPXD_H_ | ||||
| #define _QSE_HTTP_UPXD_H_ | ||||
| 
 | ||||
| #include <qse/types.h> | ||||
| #include <qse/macros.h> | ||||
| @ -1,4 +0,0 @@ | ||||
| pkgincludedir= $(includedir)/qse/net | ||||
| pkginclude_HEADERS = http.h htre.h htrd.h httpd.h upxd.h | ||||
|  | ||||
|  | ||||
							
								
								
									
										137
									
								
								qse/include/qse/xli/std.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								qse/include/qse/xli/std.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,137 @@ | ||||
| /* | ||||
|  * $Id$ | ||||
|  * | ||||
|     Copyright 2006-2012 Chung, Hyung-Hwan. | ||||
|     This file is part of QSE. | ||||
|  | ||||
|     QSE is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU Lesser General Public License as  | ||||
|     published by the Free Software Foundation, either version 3 of  | ||||
|     the License, or (at your option) any later version. | ||||
|  | ||||
|     QSE 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 Lesser General Public License for more details. | ||||
|  | ||||
|     You should have received a copy of the GNU Lesser General Public  | ||||
|     License along with QSE. If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| #ifndef _QSE_XLI_STD_H_ | ||||
| #define _QSE_XLI_STD_H_ | ||||
|  | ||||
| #include <qse/xli/xli.h> | ||||
| #include <qse/cmn/sio.h> | ||||
|  | ||||
| /** \file | ||||
|  * This file defines functions and data types that help you create | ||||
|  * an xli interpreter with less effort. It is designed to be as close | ||||
|  * to conventional xli implementations as possible. | ||||
|  *  | ||||
|  * The source script handler does not evaluate a file name of the "var=val" | ||||
|  * form as an assignment expression. Instead, it just treats it as a | ||||
|  * normal file name. | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * The qse_xli_iostd_type_t type defines standard source I/O types. | ||||
|  */ | ||||
| enum qse_xli_iostd_type_t | ||||
| { | ||||
| 	QSE_XLI_IOSTD_NULL  = 0, /**< pseudo-value to indicate no script */ | ||||
| 	QSE_XLI_IOSTD_FILE  = 1, /**< file */ | ||||
| 	QSE_XLI_IOSTD_STR   = 2  /**< length-bounded string */ | ||||
| }; | ||||
| typedef enum qse_xli_iostd_type_t qse_xli_iostd_type_t; | ||||
|  | ||||
| /** | ||||
|  * The qse_xli_iostd_t type defines a source I/O. | ||||
|  */ | ||||
| struct qse_xli_iostd_t | ||||
| { | ||||
| 	qse_xli_iostd_type_t type; | ||||
|  | ||||
| 	union | ||||
| 	{ | ||||
| 		struct | ||||
| 		{ | ||||
| 			/** file path to open. #QSE_NULL or '-' for stdin/stdout. */ | ||||
| 			const qse_char_t*  path;  | ||||
|  | ||||
| 			/** a stream created with the file path is set with this | ||||
| 			 * cmgr if it is not #QSE_NULL. */ | ||||
| 			qse_cmgr_t*  cmgr;  | ||||
| 		} file; | ||||
|  | ||||
| 		/**  | ||||
| 		 * input string or dynamically allocated output string | ||||
| 		 * | ||||
| 		 * For input, the ptr and the len field of str indicates the  | ||||
| 		 * pointer and the length of a string to read. You must set | ||||
| 		 * these fields before calling qse_xli_readstd(). | ||||
| 		 * | ||||
| 		 * For output, the ptr and the len field of str indicates the | ||||
| 		 * pointer and the length of a dereadd source string. The output | ||||
| 		 * string is dynamically allocated. You don't need to set these | ||||
| 		 * fields before calling qse_xli_readstd() because they are set | ||||
| 		 * by qse_xli_readstd() and valid while the relevant xli object | ||||
| 		 * is alive. You must free the memory chunk pointed to by the | ||||
| 		 * ptr field with qse_xli_freemem() once you're done with it to  | ||||
| 		 * avoid memory leaks.  | ||||
| 		 */ | ||||
| 		qse_xstr_t str; | ||||
| 	} u; | ||||
| }; | ||||
|  | ||||
| typedef struct qse_xli_iostd_t qse_xli_iostd_t; | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|  * The qse_xli_openstd() function creates an xli object using the default  | ||||
|  * memory manager and primitive functions. Besides, it adds a set of | ||||
|  * standard intrinsic functions like atan, system, etc. Use this function | ||||
|  * over qse_xli_open() if you don't need finer-grained customization. | ||||
|  */ | ||||
| QSE_EXPORT qse_xli_t* qse_xli_openstd ( | ||||
| 	qse_size_t xtnsize  /**< extension size in bytes */ | ||||
| ); | ||||
|  | ||||
| /** | ||||
|  * The qse_xli_openstdwithmmgr() function creates an xli object with a  | ||||
|  * user-defined memory manager. It is equivalent to qse_xli_openstd(),  | ||||
|  * except that you can specify your own memory manager. | ||||
|  */ | ||||
| QSE_EXPORT qse_xli_t* qse_xli_openstdwithmmgr ( | ||||
| 	qse_mmgr_t* mmgr,    /**< memory manager */ | ||||
| 	qse_size_t  xtnsize  /**< extension size in bytes */ | ||||
| ); | ||||
|  | ||||
| /** | ||||
|  * The qse_xli_getxtnstd() gets the pointer to extension area created with  | ||||
|  * qse_xli_openstd() or qse_xli_openstdwithmmgr(). You must not call  | ||||
|  * qse_xli_getxtn() for sunch an object. | ||||
|  */ | ||||
| QSE_EXPORT void* qse_xli_getxtnstd ( | ||||
| 	qse_xli_t* xli | ||||
| ); | ||||
|  | ||||
| QSE_EXPORT int qse_xli_readstd ( | ||||
| 	qse_xli_t*       xli, | ||||
| 	qse_xli_iostd_t* in | ||||
| ); | ||||
|  | ||||
| QSE_EXPORT int qse_xli_writestd ( | ||||
| 	qse_xli_t*       xli, | ||||
| 	qse_xli_iostd_t* out | ||||
| ); | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | ||||
|  | ||||
| #endif | ||||
| @ -39,10 +39,17 @@ enum qse_xli_errnum_t | ||||
| 	QSE_XLI_EEXIST,  /**< '${0}' already exists */ | ||||
| 	QSE_XLI_EIOFIL,  /**< io error with file '${0}' */ | ||||
| 	QSE_XLI_EIOUSR,  /**< i/o handler error */ | ||||
|  | ||||
| 	QSE_XLI_ESYNTAX, /**< syntax error */ | ||||
| 	QSE_XLI_ESCOLON, /**< semicolon expected in place of '${0}' */ | ||||
| 	QSE_XLI_ELBREQ,  /**< { or = expected in place of '${0}' */ | ||||
| 	QSE_XLI_ERBRCE,  /**< } expected in place of '${0}' */ | ||||
| 	QSE_XLI_EPAVAL   /**< pair value expected in place of '${0}' */ | ||||
| 	QSE_XLI_EPAVAL,  /**< pair value expected in place of '${0}' */ | ||||
| 	QSE_XLI_ESTRNC,  /**< string not closed */ | ||||
| 	QSE_XLI_EINCLSTR,/**< '@include' not followed by a string */ | ||||
| 	QSE_XLI_ELXCHR,  /**< invalid character '${0} */ | ||||
| 	QSE_XLI_EXKWNR,  /**< @word '${0}' not recognized */ | ||||
| 	QSE_XLI_EXKWEM   /**< @ not followed by a valid word  */ | ||||
| }; | ||||
| typedef enum qse_xli_errnum_t qse_xli_errnum_t; | ||||
|  | ||||
| @ -66,13 +73,14 @@ typedef enum qse_xli_opt_t qse_xli_opt_t; | ||||
|  | ||||
| enum qse_xli_trait_t | ||||
| { | ||||
| 	QSE_XLI_NAMEDKEY = (1 << 0), | ||||
| 	QSE_XLI_NODUPKEY = (1 << 1), | ||||
| 	QSE_XLI_KEYNODUP = (1 << 0), | ||||
| 	QSE_XLI_KEYNAME  = (1 << 1), | ||||
| 	QSE_XLI_NOTEXT   = (1 << 10) | ||||
| }; | ||||
| typedef enum qse_xli_trait_t qse_xli_trait_t; | ||||
|  | ||||
| typedef struct qse_xli_val_t qse_xli_val_t; | ||||
| typedef struct qse_xli_nil_t qse_xli_nil_t; | ||||
| typedef struct qse_xli_str_t qse_xli_str_t; | ||||
| typedef struct qse_xli_list_t qse_xli_list_t; | ||||
|  | ||||
| @ -83,6 +91,7 @@ typedef struct qse_xli_file_t qse_xli_file_t; | ||||
|  | ||||
| enum qse_xli_val_type_t | ||||
| { | ||||
| 	QSE_XLI_NIL, | ||||
| 	QSE_XLI_STR, | ||||
| 	QSE_XLI_LIST, | ||||
| }; | ||||
| @ -104,6 +113,11 @@ struct qse_xli_val_t | ||||
| 	QSE_XLI_VAL_HDR; | ||||
| }; | ||||
|  | ||||
| struct qse_xli_nil_t | ||||
| { | ||||
| 	QSE_XLI_VAL_HDR; | ||||
| }; | ||||
|  | ||||
| struct qse_xli_list_t | ||||
| { | ||||
| 	QSE_XLI_VAL_HDR; | ||||
| @ -114,9 +128,8 @@ struct qse_xli_list_t | ||||
| struct qse_xli_str_t | ||||
| { | ||||
| 	QSE_XLI_VAL_HDR; | ||||
| 	int verbatim; | ||||
| 	const qse_char_t* ptr; | ||||
| 	qse_size_t        len; | ||||
| 	qse_size_t        len; /* take note that qse_strlen(ptr) != len */ | ||||
| }; | ||||
|  | ||||
| #define QSE_XLI_ATOM_HDR \ | ||||
| @ -142,7 +155,6 @@ struct qse_xli_text_t | ||||
| { | ||||
| 	QSE_XLI_ATOM_HDR; | ||||
| 	const qse_char_t* ptr; | ||||
| 	qse_size_t len; | ||||
| }; | ||||
|  | ||||
| struct qse_xli_file_t | ||||
| @ -461,9 +473,18 @@ QSE_EXPORT void qse_xli_freemem ( | ||||
| 	void*      ptr | ||||
| ); | ||||
|  | ||||
| QSE_EXPORT qse_xli_pair_t* qse_xli_insertpair ( | ||||
| 	qse_xli_t*        xli, | ||||
| 	qse_xli_list_t*   list, | ||||
| 	qse_xli_atom_t*   peer, | ||||
| 	const qse_char_t* key, | ||||
| 	const qse_char_t* name, | ||||
| 	qse_xli_val_t*    val | ||||
| ); | ||||
|  | ||||
| QSE_EXPORT qse_xli_pair_t* qse_xli_insertpairwithemptylist ( | ||||
| 	qse_xli_t*        xli, | ||||
| 	qse_xli_list_t*   parent, | ||||
| 	qse_xli_list_t*   list, | ||||
| 	qse_xli_atom_t*   peer, | ||||
| 	const qse_char_t* key, | ||||
| 	const qse_char_t* name | ||||
| @ -471,12 +492,18 @@ QSE_EXPORT qse_xli_pair_t* qse_xli_insertpairwithemptylist ( | ||||
|  | ||||
| QSE_EXPORT qse_xli_pair_t* qse_xli_insertpairwithstr ( | ||||
| 	qse_xli_t*        xli,  | ||||
| 	qse_xli_list_t*   parent, | ||||
| 	qse_xli_list_t*   list, | ||||
| 	qse_xli_atom_t*   peer, | ||||
| 	const qse_char_t* key, | ||||
| 	const qse_char_t* name, | ||||
| 	const qse_char_t* value, | ||||
| 	int               verbatim | ||||
| 	const qse_cstr_t* value | ||||
| ); | ||||
|  | ||||
|  | ||||
| qse_xli_pair_t* qse_xli_findpairbyname ( | ||||
|      qse_xli_t*            xli, | ||||
| 	const qse_xli_list_t* list, | ||||
| 	const qse_char_t*     name | ||||
| ); | ||||
|  | ||||
|  | ||||
|  | ||||
| @ -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 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) | ||||
| { | ||||
| 	/* 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); | ||||
| } | ||||
| 
 | ||||
| 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, 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) | ||||
| 	qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req,  | ||||
| 	struct rsrc_tmp_t* tmp, qse_httpd_rsrc_t* target) | ||||
| { | ||||
| 	qse_mchar_t* ext; | ||||
| 	qse_mchar_t* script, * suffix; | ||||
| 	qse_size_t i; | ||||
| 	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; | ||||
| 
 | ||||
| 	if (idxfile) | ||||
| 	{ | ||||
| 		for (i = 0; cgistd[i].ext; i++) | ||||
| 		{ | ||||
| 			if (qse_mbsend (idxfile, cgistd[i].ext)) | ||||
| 			{ | ||||
| 				script = merge_paths (httpd, qpath, idxfile); | ||||
| 				if (script == QSE_NULL) return -1; | ||||
| 	server_xtn = qse_httpd_getserverxtn (httpd, client->server); | ||||
| 
 | ||||
| 				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; | ||||
| 	if (tmp->final_match) | ||||
| 	{ | ||||
| 		/* 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 (tmp->idxfile) | ||||
| 			{ | ||||
| 				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++) | ||||
| 		{ | ||||
| 	/* 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); | ||||
| 		/* inspect each segment from the head. */ | ||||
| 		const qse_mchar_t* ptr; | ||||
| 		const qse_mchar_t* slash; | ||||
| 
 | ||||
| 			if (ext && (ext[cgistd[i].len] == QSE_MT('/') ||  | ||||
| 			            ext[cgistd[i].len] == QSE_MT('\0')))  | ||||
| 		QSE_ASSERT (tmp->qpath[0] == QSE_T('/')); | ||||
| 
 | ||||
| 		ptr = tmp->qpath + 1; | ||||
| 		while (*ptr != QSE_MT('\0')) | ||||
| 		{ | ||||
| 				if (ext[cgistd[i].len] == QSE_MT('/'))  | ||||
| 			slash = qse_mbschr (ptr, QSE_MT('/')); | ||||
| 			if (slash) | ||||
| 			{ | ||||
| 					/* 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) | ||||
| 				if (slash > ptr) | ||||
| 				{ | ||||
| 						if (suffix) QSE_MMGR_FREE (httpd->mmgr, suffix); | ||||
| 						if (script) QSE_MMGR_FREE (httpd->mmgr, script); | ||||
| 						httpd->errnum = QSE_HTTPD_ENOMEM; | ||||
| 						return -1; | ||||
| 					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 (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; | ||||
| 							} | ||||
| 
 | ||||
| 					/* drop the suffix part */ | ||||
| 					xpath[qse_mbslen(xpath) - qse_mbslen(suffix)] = QSE_MT('\0'); | ||||
| 							goto bingo; | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				ptr = slash + 1; | ||||
| 			} | ||||
| 			else  | ||||
| 			{ | ||||
| 					/* it has no path suffix  */ | ||||
| 					script = qpath; | ||||
| 					suffix = QSE_NULL; | ||||
| 				/* 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; /* not a cgi */ | ||||
| 
 | ||||
| bingo: | ||||
| 	target->type = QSE_HTTPD_RSRC_CGI; | ||||
| 				target->u.cgi.nph = cgistd[i].nph; | ||||
| 				target->u.cgi.path = xpath; | ||||
| 	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 = docroot; | ||||
| 				target->u.cgi.shebang = cgistd[i].shebang; | ||||
| 	target->u.cgi.docroot = tmp->docroot; | ||||
| 	target->u.cgi.shebang = shebang; | ||||
| 	return 1; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| 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,85 +2288,123 @@ 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) | ||||
| 		{ | ||||
| 			while (auth->next) auth = auth->next; | ||||
| 	/* default to the root directory. */ | ||||
| 	if (!tmp.docroot) tmp.docroot = QSE_MT("/");  | ||||
| 
 | ||||
| 			if (qse_mbszcasecmp(auth->ptr, QSE_MT("Basic "), 6) == 0)  | ||||
| 	if (tmp.realm && tmp.auth) | ||||
| 	{ | ||||
| 				if (qse_mbscmp (&auth->ptr[6], server_xtn->cfg[SERVER_XTN_CFG_AUTH]) == 0) goto auth_ok; | ||||
| 		const qse_htre_hdrval_t* authv; | ||||
| 
 | ||||
| 		authv = qse_htre_getheaderval (req, QSE_MT("Authorization")); | ||||
| 		if (authv) | ||||
| 		{ | ||||
| 			while (authv->next) authv = authv->next; | ||||
| 
 | ||||
| 			if (qse_mbszcasecmp(authv->ptr, QSE_MT("Basic "), 6) == 0)  | ||||
| 			{ | ||||
| 				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) | ||||
| 	{ | ||||
| 		/* 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 (server_xtn->cfg2.s.idxstd) | ||||
| 			if (tmp.index.count > 0) | ||||
| 			{ | ||||
| 				/* try to locate an index file */ | ||||
| 			for (i = 0; server_xtn->cfg2.s.idxstd[i].name; i++) | ||||
| 				qse_size_t i; | ||||
| 				const qse_mchar_t* ptr; | ||||
| 
 | ||||
| 				ptr = tmp.index.files; | ||||
| 				for (i = 0; i < tmp.index.count; i++, ptr += qse_mbslen(ptr) + 1) | ||||
| 				{ | ||||
| 					qse_mchar_t* tpath; | ||||
| 
 | ||||
| 				tpath = merge_paths (httpd, xpath, server_xtn->cfg2.s.idxstd[i].name); | ||||
| 					tpath = merge_paths (httpd, tmp.xpath, ptr); | ||||
| 					if (tpath == QSE_NULL)  | ||||
| 					{ | ||||
| 					QSE_MMGR_FREE (httpd->mmgr, xpath); | ||||
| 						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, xpath); | ||||
| 					xpath = tpath; | ||||
| 					idxfile = server_xtn->cfg2.s.idxstd[i].name; | ||||
| 						QSE_MMGR_FREE (httpd->mmgr, tmp.xpath); | ||||
| 						tmp.xpath = tpath; | ||||
| 						tmp.idxfile = ptr; | ||||
| 						goto attempt_file; | ||||
| 					}	 | ||||
| 
 | ||||
| @ -2333,127 +2413,183 @@ auth_ok: | ||||
| 			} | ||||
| 
 | ||||
| 			target->type = QSE_HTTPD_RSRC_DIR; | ||||
| 		target->u.dir.path = xpath; | ||||
| 			target->u.dir.path = tmp.xpath; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 	attempt_file: | ||||
| 		if (server_xtn->cfg2.s.cgistd) | ||||
| 			/* 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: | ||||
| 		/* 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); | ||||
| 		n = attempt_cgi (httpd, client, req, &tmp, target); | ||||
| 		if (n <= -1)  | ||||
| 		{ | ||||
| 				QSE_MMGR_FREE (httpd->mmgr, xpath); | ||||
| 			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.path = tmp.xpath; | ||||
| 
 | ||||
| 		if (server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_MIME, &target->u.file.mime) <= -1) | ||||
| 		{ | ||||
| 			/* don't care about failure */ | ||||
| 			target->u.file.mime = QSE_NULL; | ||||
| 		if (server_xtn->cfg2.s.mimestd) | ||||
| 		{ | ||||
| 			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;  | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	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) | ||||
| { | ||||
| 		if (server_xtn->cfg[--i]) | ||||
| 	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 | ||||
| { | ||||
| 			QSE_MMGR_FREE (httpd->mmgr, server_xtn->cfg[i]); | ||||
| 			server_xtn->cfg[i] = QSE_NULL; | ||||
| 	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) | ||||
| 	{ | ||||
| 		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_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) | ||||
| @ -1165,13 +1215,22 @@ int qse_httpd_loop ( | ||||
| 
 | ||||
| 		count = httpd->scb->mux.poll (httpd, httpd->mux, tmout); | ||||
| 		if (count <= -1)  | ||||
| 		{ | ||||
| 			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,11 +314,14 @@ 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 != &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, 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,7 +52,9 @@ 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 | ||||
| 	{ | ||||
|  | ||||
| @ -7,8 +7,8 @@ AM_CPPFLAGS = \ | ||||
| 	 | ||||
| bin_PROGRAMS = httpd01 httpd02 upxd01 | ||||
| 
 | ||||
| LDFLAGS += -L../../lib/cmn -L../../lib/net  | ||||
| LDADD = -lqsenet -lqsecmn $(PTHREAD_LIBS) $(SOCKET_LIBS) $(SENDFILE_LIBS) | ||||
| LDFLAGS += -L../../lib/cmn -L../../lib/http  | ||||
| LDADD = -lqsehttp -lqsecmn $(PTHREAD_LIBS) $(SOCKET_LIBS) $(SENDFILE_LIBS) | ||||
| 
 | ||||
| if WIN32  | ||||
| if WCHAR | ||||
| @ -36,7 +36,7 @@ build_triplet = @build@ | ||||
| host_triplet = @host@ | ||||
| bin_PROGRAMS = httpd01$(EXEEXT) httpd02$(EXEEXT) upxd01$(EXEEXT) | ||||
| @WCHAR_TRUE@@WIN32_TRUE@am__append_1 = $(UNICOWS_LIBS) | ||||
| subdir = samples/net | ||||
| subdir = samples/http | ||||
| DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in | ||||
| ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 | ||||
| am__aclocal_m4_deps = $(top_srcdir)/m4/argz.m4 \
 | ||||
| @ -144,7 +144,7 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ | ||||
| INSTALL_SCRIPT = @INSTALL_SCRIPT@ | ||||
| INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ | ||||
| LD = @LD@ | ||||
| LDFLAGS = @LDFLAGS@ -L../../lib/cmn -L../../lib/net | ||||
| LDFLAGS = @LDFLAGS@ -L../../lib/cmn -L../../lib/http | ||||
| LIBADD_DL = @LIBADD_DL@ | ||||
| LIBADD_DLD_LINK = @LIBADD_DLD_LINK@ | ||||
| LIBADD_DLOPEN = @LIBADD_DLOPEN@ | ||||
| @ -282,7 +282,7 @@ AM_CPPFLAGS = \ | ||||
| 	-I$(top_srcdir)/include \
 | ||||
| 	-I$(includedir)  | ||||
| 
 | ||||
| LDADD = -lqsenet -lqsecmn $(PTHREAD_LIBS) $(SOCKET_LIBS) \
 | ||||
| LDADD = -lqsehttp -lqsecmn $(PTHREAD_LIBS) $(SOCKET_LIBS) \
 | ||||
| 	$(SENDFILE_LIBS) $(am__append_1) | ||||
| httpd01_SOURCES = httpd01.c | ||||
| httpd02_SOURCES = httpd02.c | ||||
| @ -302,9 +302,9 @@ $(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps) | ||||
| 	      exit 1;; \
 | ||||
| 	  esac; \
 | ||||
| 	done; \
 | ||||
| 	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign samples/net/Makefile'; \
 | ||||
| 	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign samples/http/Makefile'; \
 | ||||
| 	$(am__cd) $(top_srcdir) && \
 | ||||
| 	  $(AUTOMAKE) --foreign samples/net/Makefile | ||||
| 	  $(AUTOMAKE) --foreign samples/http/Makefile | ||||
| .PRECIOUS: Makefile | ||||
| Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status | ||||
| 	@case '$?' in \
 | ||||
| @ -1,5 +1,5 @@ | ||||
| 
 | ||||
| #include <qse/net/httpd.h> | ||||
| #include <qse/http/httpd.h> | ||||
| #include <qse/cmn/stdio.h> | ||||
| #include <qse/cmn/main.h> | ||||
| #include <qse/cmn/str.h> | ||||
| @ -1,5 +1,5 @@ | ||||
| 
 | ||||
| #include <qse/net/httpd.h> | ||||
| #include <qse/http/httpd.h> | ||||
| #include <qse/cmn/stdio.h> | ||||
| #include <qse/cmn/main.h> | ||||
| #include <qse/cmn/str.h> | ||||
| @ -1,4 +1,4 @@ | ||||
| #include <qse/net/upxd.h> | ||||
| #include <qse/http/upxd.h> | ||||
| #include <qse/cmn/stdio.h> | ||||
| #include <qse/cmn/main.h> | ||||
| #include <qse/cmn/str.h> | ||||
		Reference in New Issue
	
	Block a user