From ff4b919683537625f693eedf53006364d0f8444d Mon Sep 17 00:00:00 2001 From: Michael Krelin Date: Sat, 29 Jan 2005 20:14:37 +0000 Subject: initial commit into repository --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..02145e3 --- a/dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +configure +Makefile.in +kingate.pc +Doxyfile +config.log +depcomp +kingate-fcgi.pc +config.guess +config.h +ltmain.sh +config.sub +INSTALL +Makefile +config.status +stamp-h1 +config.h.in +libtool +autom4te.cache +missing +aclocal.m4 +install-sh +NEWS +doxydox diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..a9fb0c7 --- a/dev/null +++ b/AUTHORS @@ -0,0 +1,3 @@ +Klever dissected: + Michael 'hacker' Krelin + Leonid Ivanov diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..96befc6 --- a/dev/null +++ b/COPYING @@ -0,0 +1,19 @@ +Copyright (c) 2004-2005 Klever Group (http://www.klever.net/) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 --- a/dev/null +++ b/ChangeLog diff --git a/Doxyfile.in b/Doxyfile.in new file mode 100644 index 0000000..2d072ce --- a/dev/null +++ b/Doxyfile.in @@ -0,0 +1,247 @@ +# Doxyfile 1.3.9.1 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = @PACKAGE@ +PROJECT_NUMBER = @VERSION@ +OUTPUT_DIRECTORY = @builddir@/doxydox +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +USE_WINDOWS_ENCODING = NO +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = include +STRIP_FROM_INC_PATH = include +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +DISTRIBUTE_GROUP_DOC = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_BY_SCOPE_NAME = YES +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = NO +SHOW_DIRECTORIES = YES + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +INPUT = \ + @srcdir@/include/kingate/ +FILE_PATTERNS = *.h +RECURSIVE = NO +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 2 +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = NO +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +GENERATE_XML = YES +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +CLASS_DIAGRAMS = YES +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = @HAVE_DOT@ +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = YES +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = @DOT@ +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +MAX_DOT_GRAPH_DEPTH = 0 +GENERATE_LEGEND = YES +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +SEARCHENGINE = NO diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..9833732 --- a/dev/null +++ b/Makefile.am @@ -0,0 +1,27 @@ +SUBDIRS=include src +EXTRA_DIST= NEWS NEWS.xml NEWS.xsl + +DISTCHECK_CONFIGURE_FLAGS=--with-pkgconfigdir=$${dc_install_base}/lib/pkgconfig +if HAVE_PKGCONFIG +pkgconfigdir=@PKGCONFIG_DIR@ +pkgconfig_DATA=kingate.pc kingate-fcgi.pc +endif + +LOCAL_TARGETS= +if HAVE_DOXYGEN +LOCAL_TARGETS+=doxygen +endif + +all-local: NEWS $(addprefix all-lota-,${LOCAL_TARGETS}) +clean-local: $(addprefix clean-lota-,${LOCAL_TARGETS}) + +NEWS: NEWS.xsl NEWS.xml + ${XSLTPROC} -o $@ NEWS.xsl NEWS.xml + +all-lota-doxygen: doxydox/built +doxydox/built: $(wildcard ${top_srcdir}/include/kingate/*.h) + ${DOXYGEN} + touch $@ + +clean-lota-doxygen: + rm -rf doxydox diff --git a/NEWS.xml b/NEWS.xml new file mode 100644 index 0000000..d1d89f6 --- a/dev/null +++ b/NEWS.xml @@ -0,0 +1,6 @@ + + + + Initial release + + diff --git a/NEWS.xsl b/NEWS.xsl new file mode 100644 index 0000000..7c71307 --- a/dev/null +++ b/NEWS.xsl @@ -0,0 +1,24 @@ + + + + + + + + + + + + + - + + + + + + diff --git a/README b/README new file mode 100644 index 0000000..e69de29 --- a/dev/null +++ b/README diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 0000000..ec2c50f --- a/dev/null +++ b/acinclude.m4 @@ -0,0 +1,80 @@ +dnl AC_WITH_PKGCONFIG([ACTION-IF-FOUND[,ACTION-IF-NOT-FOUND]]) +dnl Outputs: +dnl AC_SUBST: PKGCONFIG_PKGCONFIG PKGCONFIG_DIR +dnl AM_CONDTIONAL: HAVE_PKGCONFIG +AC_DEFUN([AC_WITH_PKGCONFIG],[ + PKGCONFIG_PKGCONFIG="" + PKGCONFIG_DIR="" + HAVE_PKGCONFIG="no" + EXPLICIT_PKGCONFIGDIR="no" + test -z "${WANT_PKGCONFIG}" && WANT_PKGCONFIG="" + AC_PATH_PROG([PKGCONFIG_PKGCONFIG],[pkg-config],[false]) + if test "${PKGCONFIG_PKGCONFIG}" != "false" ; then + AC_ARG_WITH([pkgconfigdir], + AC_HELP_STRING([--with-pkgconfigdir=dir],[Specify pkgconfig directory]), + [ + if test "${withval}" = "no" ; then + WANT_PKGCONFIG="no" + else + PKGCONFIG_DIR="${withval}" + EXPLICIT_PKGCONFIGDIR="yes" + fi + ],[ + AC_MSG_CHECKING([for pkgconfig directory]) + PKGCONFIG_DIR="`${PKGCONFIG_PKGCONFIG} --debug 2>&1 | grep '^Scanning'| head -n 1 | cut -d\' -f2-|cut -d\' -f1`" + AC_MSG_RESULT([${PKGCONFIG_DIR}]) + ] + ) + if test -d "${PKGCONFIG_DIR}" ; then + HAVE_PKGCONFIG=yes + AC_SUBST([PKGCONFIG_PKGCONFIG]) + AC_SUBST([PKGCONFIG_DIR]) + else + AC_MSG_NOTICE([unexistent pkgconfig directory: ${PKGCONFIG_DIR}]) + if test "${EXPLICIT_PKGCONFIGDIR}" = "yes" ; then + HAVE_PKGCONFIG=yes + AC_SUBST([PKGCONFIG_PKGCONFIG]) + AC_SUBST([PKGCONFIG_DIR]) + else + ifelse([$2], , :, [$2]) + fi + fi + fi + AM_CONDITIONAL([HAVE_PKGCONFIG],[test "${HAVE_PKGCONFIG}" = "yes"]) +]) + +dnl AC_WITH_DOXYGEN([ACTION-IF-FOUND[,ACTION-IF-NOT-FOUND]]) +dnl Outputs: +dnl AC_SUBST: DOXYGEN HAVE_DOXYGEN +dnl AM_CONDTIONAL: HAVE_DOXYGEN +AC_DEFUN([AC_WITH_DOXYGEN],[ + HAVE_DOXYGEN="no" + AC_PATH_PROG([DOXYGEN],[doxygen],[false]) + if test "${DOXYGEN}" = "false" ; then + ifelse([$2], , :, [$2]) + else + HAVE_DOXYGEN="yes" + AC_SUBST([DOXYGEN]) + $1 + fi + AC_SUBST([HAVE_DOXYGEN]) + AM_CONDITIONAL([HAVE_DOXYGEN],[test "${HAVE_DOXYGEN}" = "yes"]) +]) + +dnl AC_WITH_DOT([ACTION-IF-FOUND[,ACTION-IF-NOT-FOUND]]) +dnl Outputs: +dnl AC_SUBST: DOT HAVE_DOT +dnl AM_CONDITIONAL: HAVE_DOT +AC_DEFUN([AC_WITH_DOT],[ + HAVE_DOT="no" + AC_PATH_PROG([DOT],[dot],[false]) + if test "${DOT}" = "false" ; then + ifelse([$2], , :, [$2]) + else + HAVE_DOT="yes" + AC_SUBST([DOT]) + $1 + fi +AC_SUBST([HAVE_DOT]) + AM_CONDITIONAL([HAVE_DOT],[test "${HAVE_DOT}" = "yes"]) +]) diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..ba27501 --- a/dev/null +++ b/autogen.sh @@ -0,0 +1,9 @@ +#!/bin/sh +WANT_AUTOMAKE=1.8 +export WANT_AUTOMAKE +libtoolize -f \ +&& aclocal \ +&& autoheader \ +&& automake -a \ +&& autoconf \ +&& ./configure "$@" diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..109eddf --- a/dev/null +++ b/configure.ac @@ -0,0 +1,62 @@ +AC_INIT([kingate], [0.0], [kingate-bugs@klever.net]) +AC_CONFIG_SRCDIR([include/kingate/cgi_gateway.h]) +AC_CONFIG_HEADER([config.h]) +AM_INIT_AUTOMAKE([dist-bzip2]) + +AC_PROG_INSTALL +AC_PROG_AWK +AC_PROG_CXX +AC_PROG_CC +AC_PROG_LIBTOOL + +AC_HEADER_STDC +AC_CHECK_HEADERS([sys/types.h sys/stat.h]) + +AC_LANG_PUSH(C++) +AC_CHECK_HEADERS([fcgio.h],,[ + exit 1 +]) +AC_CHECK_LIB(fcgi,FCGX_Init,,[ + exit 1 +]) +AC_CHECK_LIB([fcgi++],[main],,[ + exit 1 +]) +AC_LANG_POP(C++) + +AC_C_CONST + +AC_FUNC_MALLOC +AC_FUNC_REALLOC + +AC_PATH_PROG([XSLTPROC],[xsltproc],[true]) + +AC_WITH_PKGCONFIG + +PKG_CHECK_MODULES([KONFORKA],[konforka],,[ + AC_MSG_ERROR([no konforka library found. get one from http://kin.klever.net/konforka/]) +]) + +WANT_DOXYGEN="yes" +AC_ARG_ENABLE([doxygen], + AC_HELP_STRING([--disable-doxygen],[do not generate documentation]), + [ + test "${enableval}" = "no" && WANT_DOXYGEN="no" + ] +) +if test "${WANT_DOXYGEN}" = "yes" ; then + AC_WITH_DOXYGEN + AC_WITH_DOT +else + AM_CONDITIONAL([HAVE_DOXYGEN],[false]) + AM_CONDITIONAL([HAVE_DOT],[false]) +fi + +AC_CONFIG_FILES([ + Makefile + kingate.pc kingate-fcgi.pc + Doxyfile + include/Makefile + src/Makefile +]) +AC_OUTPUT diff --git a/include/.gitignore b/include/.gitignore new file mode 100644 index 0000000..3dda729 --- a/dev/null +++ b/include/.gitignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/include/Makefile.am b/include/Makefile.am new file mode 100644 index 0000000..c1ec36e --- a/dev/null +++ b/include/Makefile.am @@ -0,0 +1,6 @@ +nobase_include_HEADERS = \ + kingate/cgi_gateway.h \ + kingate/cgi_interface.h \ + kingate/fastcgi.h \ + kingate/exception.h \ + kingate/util.h diff --git a/include/kingate/cgi_gateway.h b/include/kingate/cgi_gateway.h new file mode 100644 index 0000000..f683580 --- a/dev/null +++ b/include/kingate/cgi_gateway.h @@ -0,0 +1,164 @@ +#ifndef __KINGATE_CGI_GATEWAY_H +#define __KINGATE_CGI_GATEWAY_H + +#include +#include "kingate/cgi_interface.h" + +/** + * @file + * @brief the cgi_gateway -- main interface to CGI. + */ + +namespace kingate { + using namespace std; + + /** + * The main class interfacing with the CGI environment. + */ + class cgi_gateway { + public: + /** + * The interface to CGI environment (e.g. fastcgi). + */ + cgi_interface& iface; + /** + * The type describing map holding parameters parsed from query string or input. + */ + typedef multimap params_t; + /** + * The GET-passed parameters. + */ + params_t get; + /** + * The POST-passed parameters. + */ + params_t post; + /** + * Was the stdin content parsed? + */ + bool b_parsed_content; + + /** + * @param ci the interface to use. + */ + cgi_gateway(cgi_interface& ci); + + /** + * Check whether there is an 'environment' meta-variable with specific name + * passed to CGI. + * @param n variable name. + * @return true if yes. + * @see cgi_interface::has_meta() + * @see get_meta() + */ + bool has_meta(const string& n) const { return iface.has_meta(n); } + /** + * Retrieve the 'environment' meta-variable value. + * @param n variable name. + * @return variable contents. + * @see exception_notfound + * @see cgi_interface::get_meta() + */ + string get_meta(const string& n) const { return iface.get_meta(n); } + + /** + * fetch reference to the 'stdin' stream. + * @return the reference to the corresponding istream object. + * @see cgi_interface::in() + */ + istream& in() { return iface.in(); } + /** + * fetch reference to the 'stdout' stream. + * @return the reference to the corresponding ostream object. + * @see cgi_interface::out() + */ + ostream& out() { return iface.out(); } + /** + * fetch reference to the 'stderr' stream. + * @return the reference to the corresponding ostream object. + * @see cgi_interface::err() + */ + ostream& err() { return iface.err(); } + /** + * cast to the ostream -- fetches the reference to the 'stdout' + * stream. + * @see out() + */ + operator ostream& (void) { return out(); } + + /** + * Check to see whether the parameter was passed via GET. + * @param n the parameter name. + * @return true if yes. + */ + bool has_GET(const string& n) const; + /** + * Retrieve the parameter passed via GET. + * @param n the parameter name. + * @return the parameter contents. + * @see exception_notfound + */ + string get_GET(const string& n) const; + /** + * Check to see whether the parameter was passed via POST. + * @param n the parameter name. + * @return true if yes. + */ + bool has_POST(const string& n) const; + /** + * Retrieve the POST-parameter. + * @param n the parameter name. + * @return the parameter contents. + * @see exception_notfound + */ + string get_POST(const string& n) const; + /** + * Check to see whether the parameter was passed either via POST or + * GET. + * @param n the parameter name. + * @return true if yes. + */ + bool has_param(const string& n) const; + /** + * Retrieve the parameter passed either via POST or GET + * (GET-parameter takes precedence). + * @param n the parameter name. + * @return true if yes. + * @see exception_notfound. + */ + string get_param(const string& n) const; + + /** + * Retrieve the POST content-type (as passed via CONTENT_TYPE + * environment variable). + * @return the content type. + */ + const string& get_content_type() const; + /** + * Retrieve the POST content length (as passed via the + * CONTENT_LENGTH environment variable). + * @return the content length. + */ + unsigned long get_content_length() const; + + /** + * Check to see whether the content from stdin stream was parsed. + * @return true if yes. + */ + bool is_content_parsed() const { return b_parsed_content; } + private: + /** + * Parse the query string, putting the parameters into the map + * specified. + * @param q the query string. + * @param p destination parameters map. + */ + static void parse_query(string& q,params_t& p); + }; + +} + +#endif /* __KINGATE_CGI_GATEWAY_H */ +/* + * vim:set ft=cpp: + */ diff --git a/include/kingate/cgi_interface.h b/include/kingate/cgi_interface.h new file mode 100644 index 0000000..84ea6dd --- a/dev/null +++ b/include/kingate/cgi_interface.h @@ -0,0 +1,70 @@ +#ifndef __KINGATE_CGI_INTERFACE_H +#define __KINGATE_CGI_INTERFACE_H + +#include +#include +#include + +/** + * @file + * @brief the abstract base for various interfaces to CGI. + */ + +namespace kingate { + using namespace std; + + /** + * The abstract base class for interface to CGI subsystem. + */ + class cgi_interface { + public: + /** + * The type for map holding 'environment' meta-variables. + */ + typedef map metavars_t; + /** + * The environment variables. + */ + metavars_t metavars; + + cgi_interface() { } + virtual ~cgi_interface() { } + + /** + * Check to see whether there is a particular 'environment' + * meta-variable passed. + * @param n the variable name. + * @return true if yes. + */ + bool has_meta(const string& n) const; + /** + * Retrieve the 'environment' variable. + * @param n the variable name. + * @return the variable contents. + * @see exception_notfound + */ + const string& get_meta(const string& n) const; + + /** + * Fetch reference to CGI 'stdout' stream. + * @return reference to the corresponding ostream object. + */ + virtual ostream& out() = 0; + /** + * Fetch reference to CGI 'stdin' stream. + * @return reference to the corresponding istream object. + */ + virtual istream& in() = 0; + /** + * Fetch reference to CGI 'stderr' stream. + * @return reference to the corresponding ostream object. + */ + virtual ostream& err() = 0; + }; + +} + +#endif /* __KINGATE_CGI_INTERFACE_H */ +/* + * vim:set ft=cpp: + */ diff --git a/include/kingate/exception.h b/include/kingate/exception.h new file mode 100644 index 0000000..6ebb361 --- a/dev/null +++ b/include/kingate/exception.h @@ -0,0 +1,44 @@ +#ifndef __KINGATE_EXCEPTION_H +#define __KINGATE_EXCEPTION_H + +#include +#include + +/** + * @file + * @brief The kingate-specific exceptions. + */ + +/** + * @brief the main kingate namespace. + */ +namespace kingate { + using namespace std; + + /** + * The base for kingate-specific exception. + */ + class exception : public konforka::exception { + public: + explicit exception(const string& w) + : konforka::exception(NOCODEPOINT,w) { } + exception(const string& fi,const string& fu,int l,const string &w) + : konforka::exception(fi,fu,l,w) { } + }; + + /** + * Thrown if the specified variable or parameter wasn't found. + */ + class exception_notfound : public exception { + public: + explicit exception_notfound(const string& w) + : exception(w) { } + exception_notfound(const string& fi,const string& fu,int l,const string& w) + : exception(fi,fu,l,w) { } + }; +} + +#endif /* __KINGATE_EXCEPTION_H */ +/* + * vim:set ft=cpp: + */ diff --git a/include/kingate/fastcgi.h b/include/kingate/fastcgi.h new file mode 100644 index 0000000..fd293b9 --- a/dev/null +++ b/include/kingate/fastcgi.h @@ -0,0 +1,98 @@ +#ifndef __KINGATE_FASTCGI_H +#define __KINGATE_FASTCGI_H + +#include "kingate/cgi_interface.h" +#include + +/** + * @file + * @brief the fastcgi-specific implementation. + */ + +namespace kingate { + + /** + * The fcgi listening socket. + */ + class fcgi_socket { + static bool _initialized; + public: + /** + * socket file descriptor. + */ + int sock; + + /** + * @param s the socket name. (if the socket starts with a colon, + * then it is interpreted as a tcp port number. + * @param bl backlog listen queue depth. + */ + fcgi_socket(const char* s,int bl); + /** + * @param s the file descriptor of preopened socket. + */ + fcgi_socket(int s); + ~fcgi_socket(); + }; + + /** + * The implementation of the interface to the FastCGI. + */ + class fcgi_interface : public cgi_interface { + public: + /** + * stdin fcgi streambuf. + */ + fcgi_streambuf sbin; + /** + * stdout fcgi streambuf. + */ + fcgi_streambuf sbout; + /** + * stderr fcgi streambuf. + */ + fcgi_streambuf sberr; + /** + * stdin istream. + */ + istream sin; + /** + * stdout ostream. + */ + ostream sout; + /** + * stderr ostream. + */ + ostream serr; + /** + * The FCGI request. + */ + FCGX_Request request; + + /** + * @param s the socked used for interfacing with the server. + * @param f the request flags (e.g. FCGI_FAIL_ON_INTR). + */ + fcgi_interface(fcgi_socket& s,int f=0); + virtual ~fcgi_interface(); + + /** + * @overload cgi_interface::in() + */ + istream& in() { return sin; } + /** + * @overload cgi_interface::out() + */ + ostream& out() { return sout; } + /** + * @overload cgi_interface::out() + */ + ostream& err() { return serr; } + }; + +} + +#endif /* __KINGATE_FASTCGI_H */ +/* + * vim:set ft=cpp: + */ diff --git a/include/kingate/util.h b/include/kingate/util.h new file mode 100644 index 0000000..4b0dca8 --- a/dev/null +++ b/include/kingate/util.h @@ -0,0 +1,26 @@ +#ifndef __KINGATE_UTIL_H +#define __KINGATE_UTIL_H + +#include + +namespace kingate { + using namespace std; + + /** + * Escape string for passing via URL. + * @param str string unescaped. + * @return the escaped string. + */ + string url_escape(const string& str); + /** + * Remove URL-encoding from the string. + * @param str the URL-encoded string. + * @return the unescaped string. + */ + string url_unescape(const string& str); +} + +#endif /* __KINGATE_UTIL_H */ +/* + * vim:set ft=cpp: + */ diff --git a/kingate-fcgi.pc.in b/kingate-fcgi.pc.in new file mode 100644 index 0000000..03e0155 --- a/dev/null +++ b/kingate-fcgi.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: kingate-fcgi +Description: FastCGI implementation of kingate interface +Version: @VERSION@ +Requires: kingate = @VERSION@ +Libs: -L${libdir} -lkingate-fcgi +Cflags: -I${includedir} diff --git a/kingate.pc.in b/kingate.pc.in new file mode 100644 index 0000000..671faac --- a/dev/null +++ b/kingate.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: kingate +Description: C++ CGI support library +Version: @VERSION@ +Requires: konforka +Libs: -L${libdir} -lkingate +Cflags: -I${includedir} diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..af4f4dd --- a/dev/null +++ b/src/.gitignore @@ -0,0 +1,7 @@ +Makefile.in +.deps +Makefile +.libs +*.o +*.lo +*.la diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..8a5447b --- a/dev/null +++ b/src/Makefile.am @@ -0,0 +1,15 @@ +lib_LTLIBRARIES = libkingate.la libkingate-fcgi.la + +INCLUDES = -I${top_srcdir}/include +AM_CXXFLAGS = ${KONFORKA_CFLAGS} +LDADD = ${KONFORKA_LIBS} + +libkingate_la_SOURCES = \ + cgi_gateway.cc \ + cgi_interface.cc \ + util.cc +libkingate_la_LDFLAGS = -version-info 1:0:0 + +libkingate_fcgi_la_SOURCES = \ + fastcgi.cc +libkingate_fcgi_la_LDFLAGS = -version-info 1:0:0 diff --git a/src/cgi_gateway.cc b/src/cgi_gateway.cc new file mode 100644 index 0000000..eae7a03 --- a/dev/null +++ b/src/cgi_gateway.cc @@ -0,0 +1,88 @@ +#include "kingate/cgi_gateway.h" +#include "kingate/util.h" +#include "kingate/exception.h" + +namespace kingate { + + cgi_gateway::cgi_gateway(cgi_interface& ci) + : iface(ci), b_parsed_content(false) { + // Fetch GET content + if(iface.has_meta("QUERY_STRING")) { + string qs = iface.get_meta("QUERY_STRING"); + parse_query(qs,get); + } + // Fetch POST content + if(!strcasecmp(get_content_type().c_str(),"application/x-www-form-urlencoded")) { + unsigned long cl = get_content_length(); + if(cl) { + char * tmp = new char[cl]; + iface.in().read(tmp,cl); + string qs(tmp,cl); + delete tmp; + parse_query(qs,post); + } + b_parsed_content = true; + } + } + + bool cgi_gateway::has_GET(const string& n) const { + return get.find(n) != get.end(); + } + string cgi_gateway::get_GET(const string& n) const { + params_t::const_iterator i = get.find(n); + if(i==get.end()) + throw exception_notfound(CODEPOINT,"no such parameter"); + return i->second; + } + bool cgi_gateway::has_POST(const string& n) const { + return post.find(n) != post.end(); + } + string cgi_gateway::get_POST(const string& n) const { + params_t::const_iterator i = post.find(n); + if(i==post.end()) + throw exception_notfound(CODEPOINT,"no such parameter"); + return i->second; + } + bool cgi_gateway::has_param(const string& n) const { + return has_GET(n) || has_POST(n); + } + string cgi_gateway::get_param(const string& n) const { + params_t::const_iterator i = get.find(n); + if(i!=get.end()) + return i->second; + i = post.find(n); + if(i!=post.end()) + return i->second; + throw exception_notfound(CODEPOINT,"no such parameter"); + } + + const string& cgi_gateway::get_content_type() const { + if(!has_meta("CONTENT_TYPE")) + return ""; // XXX: + return get_meta("CONTENT_TYPE"); + } + unsigned long cgi_gateway::get_content_length() const { + if(!has_meta("CONTENT_LENGTH")) + return 0; + string cl = get_meta("CONTENT_LENGTH"); + return strtol(cl.c_str(),NULL,10); + } + + void cgi_gateway::parse_query(string& q,params_t& p) { + while(!q.empty()) { + string::size_type amp = q.find('&'); + string pp = (amp==string::npos)?q:q.substr(0,amp); + if(amp==string::npos) + q.clear(); + else + q.erase(0,amp+1); + string::size_type eq = pp.find('='); + if(eq == string::npos) { + p.insert(params_t::value_type("",url_unescape(pp))); + }else{ + p.insert(params_t::value_type(url_unescape(pp.substr(0,eq)),url_unescape(pp.substr(eq+1)))); + } + } + } + +} diff --git a/src/cgi_interface.cc b/src/cgi_interface.cc new file mode 100644 index 0000000..ffbd2bf --- a/dev/null +++ b/src/cgi_interface.cc @@ -0,0 +1,16 @@ +#include "kingate/cgi_interface.h" +#include "kingate/exception.h" + +namespace kingate { + + bool cgi_interface::has_meta(const string& n) const { + return metavars.find(n) != metavars.end(); + } + const string& cgi_interface::get_meta(const string& n) const { + metavars_t::const_iterator rv = metavars.find(n); + if(rv == metavars.end()) + throw exception_notfound(CODEPOINT,"no such meta-variable"); + return rv->second; + } + +} diff --git a/src/fastcgi.cc b/src/fastcgi.cc new file mode 100644 index 0000000..7484449 --- a/dev/null +++ b/src/fastcgi.cc @@ -0,0 +1,67 @@ +#include +#include +#include +#include "kingate/fastcgi.h" +#include "kingate/exception.h" + +namespace kingate { + + bool fcgi_socket::_initialized = false; + + fcgi_socket::fcgi_socket(const char *s,int bl) + : sock(-1) { + if(!_initialized) { + if( FCGX_Init() ) + throw exception(CODEPOINT,"failed to FCGX_Init()"); + _initialized = true; + } + sock = FCGX_OpenSocket(s,bl); + if(sock<0) + throw exception(CODEPOINT,"failed to FCGX_OpenSocket("); + // TODO: check if there is a ':', not if it starts with ':' + if(*s != ':') + if(chmod(s,0777)) // XXX: configurable. + throw exception(CODEPOINT,"failed to chmod()"); + } + fcgi_socket::fcgi_socket(int s) + : sock(0) { + if(!_initialized) { + if( FCGX_Init() ) + throw exception(CODEPOINT,"failed to FCGX_Init()"); + _initialized = true; + } + } + fcgi_socket::~fcgi_socket() { + if(sock>=0) + close(sock); + } + + fcgi_interface::fcgi_interface(fcgi_socket& s,int f) + : sin(&sbin), sout(&sbout), serr(&sberr) { + if( FCGX_InitRequest(&request,s.sock,f) ) + throw exception(CODEPOINT,"failed to FCGX_InitRequest()"); + if( FCGX_Accept_r(&request) ) + throw exception(CODEPOINT,"failed to FCGX_Accept_r()"); + sbin.attach(request.in); + sbout.attach(request.out); + sberr.attach(request.err); + metavars.clear(); // XXX: redundant. + for(char **p = request.envp; *p; p++) { + const char *e = strchr(*p,'='); + if(!e){ + // XXX: check if we have it already? + metavars[*p] = string(0); + }else{ + int l = e-*p; e++; + // XXX: check if we have it already? + metavars[string(*p,l)]=e; + } + } + } + fcgi_interface::~fcgi_interface() { + sout.flush(); + serr.flush(); + FCGX_Finish_r(&request); + } + +} diff --git a/src/util.cc b/src/util.cc new file mode 100644 index 0000000..2e2d305 --- a/dev/null +++ b/src/util.cc @@ -0,0 +1,53 @@ +#include "kingate/util.h" +#include "kingate/exception.h" + +namespace kingate { + + static const char *safeChars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "_-" ; + + string url_escape(const string& str) { + string rv = str; + string::size_type screwed = 0; + for(;;) { + screwed = rv.find_first_not_of(safeChars,screwed); + if(screwed == string::npos) + break; + while(screwed