/* flower: a universal web service * **************************************************************** * Copyright (C) 2007 Thomas Lord * Licensed under the Open Software License version 3.0 * Patents Pending * * The "Open Software License version 3.0" refers to the * license that should be included with this software as a file * named "LICENSE-FOR-SOURCE-CODE" but that is * in any event the license recognized by that name by the * Open Source Initiative (opensource.org). * */ #include "dbxml/DbXml.hpp" #include #undef _GNU_SOURCE #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace DbXml; using namespace __gnu_cxx; using namespace std; using namespace cgicc; namespace __gnu_cxx { template<> struct hash< string > { size_t operator()( const string& x ) const { const unsigned char * chr = (const unsigned char *)x.c_str(); size_t result = 0; while (*chr) { result = *chr + (result << 3) + (result >> (8 * sizeof (result) - 3)); ++chr; } return result; } }; } // A handy exception class. // class XqvmException { public: const char * msg; XqvmException (const char * m); const char * what() const; }; XqvmException::XqvmException (const char * m) { msg = m; } const char * XqvmException::what () const { return msg; } // This class encapsulates a Berkeley DB "DbEnv" // object, currently just to simplify initialization. // class XqvmDbEnv { public: DbEnv * db_env; XqvmDbEnv (u_int32_t cache_gbytes, u_int32_t cache_bytes, int ncache, const char *db_home, u_int32_t flags, int mode); ~XqvmDbEnv (); }; XqvmDbEnv::XqvmDbEnv(u_int32_t cache_gbytes, u_int32_t cache_bytes, int ncache, const char *db_home, u_int32_t flags, int mode) { db_env = new DbEnv (0); db_env->set_cachesize (cache_gbytes, cache_bytes, ncache); db_env->set_lk_max_lockers (50000); db_env->set_lk_max_locks (50000); db_env->set_lk_max_objects (50000); db_env->open (db_home, flags, mode); }; XqvmDbEnv::~XqvmDbEnv () { delete db_env; } // This class (XqvmVM) encapsulate the state of an XQVM // virtual machine. // // Mostly this is a wrapper for sever DB XML classes -- // we only need a single transaction object, update context, // etc. so here they are all managed in one place. // // Additionally, the "known_query" method is used to cache // the results of compiling queries. // class XqvmVm { public: XqvmVm(const char * db_home); ~XqvmVm(); XqvmDbEnv env; XmlManager xml_mgr; XmlTransaction txn; XmlUpdateContext update_context; XmlQueryContext query_context; XmlContainer container; XmlQueryExpression known_query (string query); private: hash_map > query_memo; }; XqvmVm::XqvmVm (const char * db_home) : env (0, 8 * 1024 * 1024, 1, db_home, (DB_REGISTER | DB_RECOVER | DB_CREATE | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN), 0), xml_mgr (env.db_env, (DBXML_ALLOW_EXTERNAL_ACCESS | DBXML_ALLOW_AUTO_OPEN)), txn (xml_mgr.createTransaction (DB_TXN_SYNC | DB_TXN_SNAPSHOT)), update_context (xml_mgr.createUpdateContext ()), query_context (xml_mgr.createQueryContext (XmlQueryContext::LiveValues, XmlQueryContext::Lazy)), container () { query_context.setNamespace ("xqvm", "http://www.dasht-exp-1a.com/Namespaces/xqvm"); query_context.setNamespace ("doc-soup", "http://www.dasht-exp-1a.com/Namespaces/doc-soup"); query_context.setNamespace ("flower", "http://www.dasht-exp-1a.com/Namespaces/flower"); query_context.setNamespace ("html", "http://www.w3.org/TR/REC-html40"); container = xml_mgr.openContainer ("docsoup.dbxml", DB_CREATE | DB_MULTIVERSION | DBXML_TRANSACTIONAL); query_context.setDefaultCollection ("docsoup.dbxml"); } XqvmVm::~XqvmVm () { } XmlQueryExpression XqvmVm::known_query (string query) { if (!query_memo.count(query)) query_memo[query] = xml_mgr.prepare (txn, query, query_context); return query_memo[query]; } // A binding between the CgiCC IO API and the // fcgi layer. // class CGICC_API FastCgiIO : public cgicc::CgiInput, public std::ostream { private: FCGX_Request& request; fcgi_streambuf output_buffer; std::map environment; public: FastCgiIO (FCGX_Request& rqst) : std::ostream (&output_buffer), request (rqst), output_buffer (request.out) { for(char ** e = request.envp; *e != NULL; ++e) { std::string s = *e; std::string::size_type i = s.find('='); if (i != std::string::npos) { environment[s.substr(0, i)] = s.substr (i + 1); } } } FastCgiIO (const FastCgiIO& to_copy) : CgiInput (to_copy), std::ostream (&output_buffer), request (to_copy.request), environment (to_copy.environment) { rdbuf(&output_buffer); } virtual inline ~FastCgiIO () {} virtual inline size_t read (char * buf, size_t length) { return FCGX_GetStr (buf, length, request.in); } virtual inline std::string getenv(const char * name) { return environment[name]; } }; //////////////////////////////////////////////////////////////// // // "main" // //////////////////////////////////////////////////////////////// static struct ::option long_options[] = { {"help", 0, 0, 'h'}, {"version", 0, 0, 'V'}, {"db-home", 1, 0, 'd'}, {"xslt", 1, 0, 'x'}, {"style", 1, 0, 's'}, {"url", 1, 0, 'u'}, {0, 0, 0, 0} }; int main(int argc, char **argv) { char * db_home = "."; // The directory containing the database. char * dflt_xslt_dir = "/xslt"; // A base URL (perhaps relative) for stylesheets etc. char * dflt_style = 0; // A default relative (to the dflt_xslt_dir) stylesheet char * url = ""; // A base URL for requests to this server int option_index = 0; // Boilerplate options processing. int status_after_usage = 0; // Two paths (error and normal) print a usage message and exit. //////////////////////////////////////////////////////////////// // // Parse Command Line Arguments // //////////////////////////////////////////////////////////////// while (1) { int c; c = getopt_long (argc, argv, "hVd:c:x:u:", long_options, &option_index); if (c == -1) break; switch (c) { usage: case 'h': { (status_after_usage ? cerr : cout) << "Usage flower [options]\n" << "Options:\n" << "-h, --help Display this message and exit\n" << "-V, --version Display version information\n" << "-d, --db-home DIR Use DIR as database home directory\n" << "-x, --xslt DIR Find xslt files in DIR\n" << "-s, --style STYLE default styleseet STYLE, relative to --xslt\n" << "-u, --url URL URL of this service\n"; exit (status_after_usage); } case 'V': { cout << "flower 0.5\n" << "Copyright (C) 2007 Thomas Lord (lord@emf.net)\n" << "Licensed under the Open Software License version 3.0\n" << "See the source for copying conditions.\n" << "\n" << "There is NO warranty; \n" << "not even for MERCHANTABILITY\n" << "or FITNESS FOR A PARTICULAR PURPOSE.\n"; exit (0); } case 'x': { size_t len = strlen (optarg); dflt_xslt_dir = (char *)malloc (len + 1); strcpy (dflt_xslt_dir, optarg); break; } case 's': { size_t len = strlen (optarg); dflt_style = (char *)malloc (len + 1); strcpy (dflt_style, optarg); break; } case 'd': { size_t len = strlen (optarg); db_home = (char *)malloc (len + 1); strcpy (db_home, optarg); break; } case 'u': { size_t len = strlen (optarg); url = (char *)malloc (len + 1); strcpy (url, optarg); break; } default: cerr << "?? getopt returned " << c << "\n"; exit (2); } } if (optind != argc) { status_after_usage = 2; cerr << "usage error\n"; goto usage; } //////////////////////////////////////////////////////////////// // // The Server Proper // //////////////////////////////////////////////////////////////// try { XqvmVm xqvm (db_home); // Initialize three standard XQVM registers: // xqvm.query_context.setVariableValue ("xqvm:r_thread", ""); xqvm.query_context.setVariableValue ("xqvm:r_results", ""); xqvm.query_context.setVariableValue ("xqvm:r_gestalt", ""); //////////////////////////////////////////////////////////////// // // This server is a "hand compiled" XQVM program which implements // an interpreter for "flower". It will, during operation, // make heavy use of a set of "canned" XQueries, all of which // we initialize here. // //////////////////////////////////////////////////////////////// // q_nil_query // // Return an empty results list. // XmlQueryExpression q_nil_query = xqvm.known_query ("()"); // q_get_session_by_id // // Retrieve from the DB all persistent session objects whose ID // matches the value in the $results register. // XmlQueryExpression q_get_session_by_id = xqvm.known_query ("collection()/flower:session[@flower:id = $xqvm:r_results]"); // q_get_account_by_login_name // // Retrieve from the DB the singular registered account record // whose login name matches the value in the $results register. // // Should there be no or more than one such account, return // an empty list of results. // XmlQueryExpression q_get_account_by_login_name = xqvm.known_query ("\n" "declare ordering ordered;\n" "let $acct := collection()/flower:account[@flower:login-name/string() = $xqvm:r_results]\n" "return\n" "if (count ($acct) != 1)\n" "then ()\n" "else ($acct,\n" " $acct/@flower:salt/string()\n," " $acct/@flower:password/string()\n," " $acct/@flower:id/string())\n" "\n"); // q_make_session_from_account // // Given an account record in the $results register, // return a new session record. // XmlQueryExpression q_make_session_from_account = xqvm.known_query ("\n" "declare ordering ordered;\n" "\n" "\n" " {\n" " $xqvm:r_results/@flower:login-name,\n" " $xqvm:r_results/@flower:id\n" " }\n" "\n" "\n"); // q_make_anonymous_session // // Given an (optional) error message in the $results register, // return a new session record for the anonymous user. // XmlQueryExpression q_make_anonymous_session = xqvm.known_query (""); // q_fold_params // // On entry, $results should contain a list whose // odd numbered elements (starting at 1) are parameter // names, with the curresponding values in the even // positions. // // This returns an element of the form: // // // {VALUE1} // {VALUE2} // {VALUE3} // {...} // // XmlQueryExpression q_fold_params = xqvm.known_query ("\n" "declare ordering ordered;\n" "\n" "declare function flower:fold-params ($l)\n" "{\n" " if (count ($l) lt 2) \n" " then ()\n" " else\n" " (\n" " {$l[2]},\n" " flower:fold-params ($l[position() gt 2])\n" " )\n" "};\n" "\n" "{flower:fold-params ($xqvm:r_results)}\n" "\n"); // q_finish_making_thread // // On entry, $results shoudld contain a list of values of the form: // // {FORM-PARAMS} {SESSION} {ENV-PARAM-NAME1} {ENV-PARAM-VAL1} {ENV-PARAM-NAME2} ... // // The FORM-PARAMS value is as returned by q_fold_params. // The SESSION value is as returned by q_make_anonymous_session or q_make_session_from_account. // The ENV-PARAM-NAME* values are strings // The ENV-PARAM-VAL* values are arbitrary // // This returns an XQVM thread: // // // // {FORM-PARAMS} // {ENV-PARAM-VALUE1} // {ENV-PARAM-VALUE2} // {...} // // {SESSION} // // import module namespace flower = 'http://www.dasht-exp-1a.com/Namespaces/flower'; // flower:filter() // // // // (The "flower:filter()" function called by the query is // not actually used. A definition of that function is not // provided with this software. Rather, the flower interpreter // in this file is a "hand compiled" implementation of that // function.) // XmlQueryExpression q_finish_making_thread; try { q_finish_making_thread = xqvm.known_query ("\n" "declare ordering ordered;\n" "\n" "declare function flower:build-params ($n)\n" "{\n" " if (($n + 1) gt count ($xqvm:r_results)) then ()\n" " else (\n" " {$xqvm:r_results[$n + 1]},\n" " flower:build-params($n + 2)\n" " )\n" "};\n" "\n" "\n" " \n" " {$xqvm:r_results[1]}\n" " {flower:build-params (3)}\n" " \n" " \n" " import module namespace flower = 'http://www.dasht-exp-1a.com/Namespaces/flower';\n" " flower:filter()\n" " \n" " {$xqvm:r_results[2]}\n" "\n" "\n"); } catch(const XmlException& e) { cout << e.what(); exit (2); } catch(const DbException& e) { cout << e.what(); exit (3); } catch(const XqvmException& e) { cout << e.what(); exit (4); } // q_get_base_doc_for_request // // On entry, the $thread register should contain // a thread object as returned by q_finish_making_thread // // This returns a new doc soup message: the "initial message" // to be processed by the request. // // See "The Flower Programming Language" for details. // // Briefly, in the basis of form parameters, a desired // document is identified and an attempt made to retrieve // it from the database. If that fails, an error document // is created to substitute for it. // // In either case the envelope meta-data of the resulting // document is update, initializing a filter program. // The output will have the general form: // // // // { ... first step of the flower program ...} // { ... default continuation of the flower program ...} // // // {...} // // // // XmlQueryExpression q_get_base_doc_for_request; try { q_get_base_doc_for_request = xqvm.known_query ("\n" "declare ordering ordered;\n" "\n" "declare function flower:get-doc ()\n" "{\n" " let $raw := flower:get-raw-doc ()\n" " return flower:init-filter ($raw)\n" "};\n" "\n" "declare function flower:init-filter ($msg)\n" "{\n" " let $form := $xqvm:r_thread/xqvm:params/xqvm:param[@xqvm:name/string() = 'FormParams'],\n" " $edit := $form/xqvm:param[@xqvm:name/string() = 'flower:edit']/text(),\n" " $msg := if (not ($edit)) then $msg else flower:edit ($form, $msg),\n" " $copy := $form/xqvm:param[@xqvm:name/string() = 'flower:copy']/text(),\n" " $update := $form/xqvm:param[@xqvm:name/string() = 'flower:update']/text(),\n" " $delete := $form/xqvm:param[@xqvm:name/string() = 'flower:delete']/text(),\n" " $source := $form/xqvm:param[@xqvm:name/string() = 'flower:source']/text(),\n" " $doc-filter := $msg/doc-soup:payload/doc-soup:meta/flower:filter,\n" " $doc-filter-service := $doc-filter/flower:service,\n" " $doc-subfilter := $doc-filter/flower:filter,\n" " $f1 := if (not ($delete))\n" " then $doc-subfilter\n" " else \n" " {$delete}\n" " {$doc-subfilter}\n" " ,\n" " $f2 := if (not ($update))\n" " then $f1\n" " else \n" " \n" " {$f1}\n" " ,\n" " $f3 := if (not ($doc-filter-service))\n" " then $f2\n" " else \n" " {$doc-filter-service}\n" " {$f2}\n" " \n" " return\n" " {\n" " $msg/@*,\n" " {\n" " $msg/doc-soup:envelope/@*,\n" " $xqvm:r_thread/flower:session,\n" " {$xqvm:r_thread/xqvm:params/xqvm:param[@xqvm:name/string() = 'FormParams']/*},\n" " {$xqvm:r_gestalt},\n" " {$xqvm:r_thread/xqvm:params/xqvm:param[@xqvm:name/string() = 'MyURL']/text()},\n" " {$msg/doc-soup:payload/doc-soup:meta/flower:mimetype/node()},\n" " {$msg/doc-soup:payload/doc-soup:meta/flower:stylesheet/node()},\n" " {$xqvm:r_thread/xqvm:params/xqvm:param[@xqvm:name/string() = 'XsltBase']/text()},\n" " if ($f3) then $f3/flower:service else (),\n" " $f3/flower:filter,\n" " for $e in $msg/doc-soup:envelope/*\n" " return typeswitch ($e)\n" " case element (flower:params) return ()\n" " case element (flower:session) return ()\n" " case element (flower:msg-id) return ()\n" " case element (flower:service) return ()\n" " case element (flower:filter) return ()\n" " case element (flower:url) return ()\n" " case element (flower:stylesheet) return ()\n" " case element (flower:style-base) return ()\n" " case element (flower:mimetype) return ()\n" " default return $e\n" " },\n" " {\n" " $msg/doc-soup:payload/@*,\n" " {\n" " $msg/doc-soup:payload/doc-soup:meta/@*,\n" " if ($copy)\n" " then\n" " (\n" " {$xqvm:r_gestalt},\n" " $msg/doc-soup:payload/doc-soup:meta/flower:title\n" " )\n" " else\n" " (\n" " $msg/doc-soup:payload/doc-soup:meta/flower:form-id,\n" " $msg/doc-soup:payload/doc-soup:meta/flower:title\n" " ),\n" " for $m in $msg/doc-soup:payload/doc-soup:meta/*\n" " return typeswitch ($m)\n" " case element (flower:form-id) return ()\n" " case element (flower:title) return ()\n" " default return $m\n" " },\n" " $msg/doc-soup:payload/doc-soup:body\n" " }\n" " }\n" "};\n" "\n" "declare function flower:edit ($form, $msg)\n" "{\n" " \n" " {$msg/doc-soup:envelope}\n" " \n" " {$msg/doc-soup:payload/@*}\n" " {flower:edit-meta ($form, $msg/doc-soup:payload/doc-soup:meta)}\n" " {flower:edit-this ($form, ($msg/doc-soup:payload/doc-soup:body, )[1], '_root')}\n" " \n" " \n" "};\n" "\n" "declare function flower:edit-meta ($form, $meta)\n" "{\n" " {\n" " for $x in $meta/node()\n" " return\n" " typeswitch ($x)\n" " case element (flower:title) return ()\n" " case element (flower:summary) return ()\n" " case element (flower:filter) return ()\n" " case element (flower:folder) return ()\n" " case element (flower:stylesheet) return ()\n" " default return $x,\n" "\n" " flower:edit-this ($form, ($meta/flower:title, )[1], '_title'),\n" " flower:edit-this ($form, ($meta/flower:summary, )[1], '_summary'),\n" " flower:edit-this ($form, ($meta/flower:filter, )[1], '_filter'),\n" " flower:edit-this ($form, ($meta/flower:folder, )[1], '_folder'),\n" " flower:edit-this ($form, ($meta/flower:stylesheet, )[1], '_stylesheet')\n" " }\n" "};\n" "\n" "declare function flower:edit-this ($form, $tree, $pos)\n" "{\n" " if (not ($tree/name()))\n" " then $tree\n" " else if (not ($pos)) \n" " then\n" " element { QName ($tree/namespace-uri(), $tree/name()) }\n" " {\n" " $tree/@*,\n" " for $child in $tree/node()\n" " return flower:edit-this ($form, $child, $child/@flower:id/string())\n" " }\n" " else if ($form/*\n" " [\n" " (text() = $pos)\n" " and\n" " ends-with (@xqvm:name/string(), '.flower:delete-node')\n" " ])\n" " then\n" " ()\n" " else\n" " (\n" " if ($pos = ('_root', '_title', '_summary', '_filter', '_folder', '_stylesheet'))\n" " then ()\n" " else flower:insert-for-around ($form, $pos, '.flower:insert-before'),\n" "\n" " flower:edit-node ($form, $tree, $pos, (), ()),\n" "\n" " if ($pos = ('_root', '_title', '_summary', '_filter', '_folder', '_stylesheet'))\n" " then ()\n" " else flower:insert-for-around ($form, $pos, '.flower:insert-after')\n" " )\n" "};\n" "\n" "declare function flower:insert-for-around ($form, $pos, $suffix)\n" "{\n" " for $insert in $form/*\n" " [\n" " (text() = $pos)\n" " and\n" " ends-with (@xqvm:name/string(), $suffix)\n" " ]\n" " let $prefix := substring ($insert/@xqvm:name/string(), 1, string-length($insert/@xqvm:name/string()) - string-length ($suffix))\n" " return flower:build-element ($form, $prefix)\n" "};\n" "\n" "declare function flower:edit-node ($form, $tree, $pos, $errmsg, $note)\n" "{\n" " let $replacements := $form/*\n" " [\n" " (text() = $pos)\n" " and\n" " ends-with (@xqvm:name/string(), '.flower:replace')\n" " ]\n" " return\n" " if ($replacements and not ($pos = ('_root', '_title', '_summary', '_filter', '_folder', '_stylesheet')))\n" " then\n" " for $r in $replacements\n" " let $prefix := substring ($r/@xqvm:name/string(), 1, string-length($r/@xqvm:name/string()) - string-length ('.flower:replace'))\n" " return flower:build-element ($form, $prefix)\n" " else\n" " let $child-repls := $form/*\n" " [\n" " (text() = $pos)\n" " and\n" " ends-with (@xqvm:name/string(), '.flower:replace-children')\n" " ]\n" " return\n" " if ($child-repls)\n" " then\n" " for $r in $child-repls\n" " let $prefix := substring ($r/@xqvm:name/string(), 1, string-length($r/@xqvm:name/string()) - string-length ('.flower:replace-children')),\n" " $prototype := flower:build-element ($form, $prefix)\n" " return\n" " element { QName ($tree/namespace-uri(), $tree/name()) }\n" " {\n" " $prototype/@*, $prototype/node()\n" " }\n" " else\n" " element { QName ($tree/namespace-uri(), $tree/name()) }\n" " {\n" " flower:build-attributes ($form, $pos, $tree, $errmsg, $note),\n" " flower:insert-for-around ($form, $pos, '.flower:add-first'),\n" " for $child in $tree/node()\n" " return flower:edit-this ($form, $child, $child/@flower:id/string()),\n" " flower:insert-for-around ($form, $pos, '.flower:add-last'),\n" " flower:insert-strays ($form, $pos, $tree)\n" " }\n" "};\n" "\n" "\n" "declare function flower:insert-strays ($form, $pos, $for-tree)\n" "{\n" " for $maybe in $form/*\n" " [\n" " (text() = $pos)\n" " and\n" " ends-with (@xqvm:name/string(), '.flower:in-parent')\n" " ]\n" " let $prefix := substring ($maybe/@xqvm:name/string(), 1, string-length($maybe/@xqvm:name/string()) - string-length ('.flower:in-parent')),\n" " $sibs := (\n" " $form/*[@xqvm:name/string() = concat ($prefix, '.flower:insert-before')],\n" " $form/*[@xqvm:name/string() = concat ($prefix, '.flower:insert-after')]\n" " ),\n" " $names := $sibs/text()\n" " return\n" " if ($for-tree/*[@flower:id/string() = $names])\n" " then ()\n" " else flower:build-element ($form, $prefix)\n" "};\n" "\n" "\n" "declare function flower:build-attributes ($form, $pos, $dflts-from, $errmsg, $note)\n" "{\n" " let $edits := $form/*\n" " [\n" " (text() = $pos)\n" " and\n" " ends-with (@xqvm:name/string(), '.flower:on-node')\n" " ],\n" " $clobbered := (\n" " for $e in $edits\n" " let $prefix := substring ($e/@xqvm:name/string(), 1, string-length($e/@xqvm:name/string()) - string-length ('.flower:on-node'))\n" " return $form/*[@xqvm:name/string() = concat ($prefix, '.flower:attribute')]\n" " )\n" " return\n" " (\n" " $dflts-from/@*[\n" " not (name() = $clobbered/text())\n" " and\n" " not ($pos and (name() = 'flower:id'))\n" " and\n" " not ($errmsg and (name() = 'flower:errmsg'))\n" " and \n" " not ($note and (name() = 'flower:note'))\n" " ],\n" "\n" " if (not ($pos) or ($pos = ('_root', '_title', '_summary', '_filter', '_folder', '_stylesheet')))\n" " then ()\n" " else attribute { 'flower:id' } { $pos },\n" "\n" " if (not ($errmsg))\n" " then ()\n" " else attribute { 'flower:errmsg' } { $errmsg },\n" "\n" " if (not ($note))\n" " then ()\n" " else attribute { 'flower:note' } { $note },\n" "\n" " for $e in $edits\n" " let $prefix := substring ($e/@xqvm:name/string(), 1, string-length($e/@xqvm:name/string()) - string-length ('.flower:on-node')),\n" " $attribute := $form/*[@xqvm:name/string() = concat ($prefix, '.flower:attribute')],\n" " $namespace := $form/*[@xqvm:name/string() = concat ($prefix, '.flower:ns')],\n" " $value := $form/*[@xqvm:name/string() = concat ($prefix, '.flower:attribute-value')],\n" " $type := $form/*[@xqvm:name/string() = concat ($prefix, '.flower:attribute-type')]\n" " return\n" " if (not ($type) or ($type/text() = 'string'))\n" " then \n" " if (not ($attribute/text() and $value/text()))\n" " then ()\n" " else attribute {QName ($namespace/text(), $attribute/text())} {$value}\n" " else if ($type/text() = 'boolean')\n" " then\n" " if (not ($attribute/text() and $value/text()) or ($value/text() = ('no', 'false', '0')))\n" " then ()\n" " else attribute {QName ($namespace/text(), $attribute/text())} {$value}\n" " else\n" " ()\n" " )\n" "};\n" "\n" "declare function flower:build-element ($form, $prefix)\n" "{\n" " let $lit := $form/*[@xqvm:name/string() = concat ($prefix, '.flower:literal')]\n" " return if ($lit) then $form/*[@xqvm:name/string() = $lit]/* else\n" " let $str := $form/*[@xqvm:name/string() = concat ($prefix, '.flower:text')],\n" " $non-empty := $form/*[@xqvm:name/string() = concat ($prefix, '.flower:non-empty')],\n" " $elt := $form/*[@xqvm:name/string() = concat ($prefix, '.flower:element')],\n" " $ns := $form/*[@xqvm:name/string() = concat ($prefix, '.flower:ns')],\n" " $id := $form/*[@xqvm:name/string() = concat ($prefix, '.flower:new-id')]/text(),\n" " $id := if ($id)\n" " then $id\n" " else \n" " let $prefix := $form/*[@xqvm:name/string() = concat ($prefix, '.flower:new-id-prefix')]/text()\n" " return\n" " if ($prefix)\n" " then concat ($prefix, $xqvm:r_gestalt)\n" " else (),\n" " $populate-as := $form/*[@xqvm:name/string() = concat ($prefix, '.flower:populate-as')]/text()\n" " return\n" " if (not ($elt/text()))\n" " then ()\n" " else if ($non-empty/text() and not ($form/xqvm:param[@xqvm:name/string() = $non-empty/text()]/text()))\n" " then ()\n" " else\n" " let $computed-id := if (not ($id)) then () else attribute { 'flower:id' } { $id },\n" " $prototype-attrs := if ($populate-as)\n" " then flower:build-attributes ($form, $populate-as, , (), ())\n" " else ()\n" " return\n" " element { QName ($ns/text(), $elt/text()) }\n" " {\n" " $prototype-attrs[not (./name() = 'flower:id')],\n" " $computed-id,\n" " $str/text(),\n" " if (not ($populate-as))\n" " then ()\n" " else\n" " let $added := {\n" " flower:insert-for-around ($form, $populate-as, '.flower:add-first'),\n" " flower:insert-for-around ($form, $populate-as, '.flower:add-last')\n" " },\n" " $strays := flower:insert-strays ($form, $populate-as, $added)\n" " return ($added/node(), $strays/node())\n" " }\n" "};\n" "\n" "declare function flower:get-raw-doc ()\n" "{\n" " let $form := $xqvm:r_thread/xqvm:params/xqvm:param[@xqvm:name/string() = 'FormParams'],\n" " $id := $form/xqvm:param[@xqvm:name/string() = 'flower:form-id']/text()\n" " return if ($id) then flower:get-raw-doc-by-id ($id)\n" " else let\n" " $tag-ns := $form/xqvm:param[@xqvm:name/string() = 'flower:form-tag-ns'],\n" " $tag-name := $form/xqvm:param[@xqvm:name/string() = 'flower:form-tag-name']\n" " return\n" " if ($tag-name/text()) then flower:get-raw-doc-by-tag ($tag-ns, $tag-name)\n" " else flower:get-raw-doc-by-tag ('http://www.dasht-exp-1a.com/Namespaces/flower', 'home')\n" "};\n" "\n" "declare function flower:get-raw-doc-by-id ($id)\n" "{\n" " if ($id = '_blank')\n" " then\n" " \n" " \n" " \n" " \n" " _blank\n" " _blank\n" " \n" " \n" " \n" " \n" " else\n" " let $found := collection()/doc-soup:msg[doc-soup:payload/doc-soup:meta/flower:form-id/text() = $id]\n" " return if (count ($found) = 1) then $found\n" " else if (count ($found) gt 1)\n" " then\n" " \n" " \n" " 501\n" " \n" " \n" " \n" " ambiguous form id ({$id})\n" " \n" " \n" " else\n" " \n" " \n" " no such form id ({$id})\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "};\n" "\n" "declare function flower:get-raw-doc-by-tag ($ns, $name)\n" "{\n" " let $found := collection()/doc-soup:msg\n" " [doc-soup:payload//@*\n" " [(string() = 'relation:') and (namespace-uri() = $ns) and (local-name() = $name)]]\n" " return if (count ($found) = 1) then $found\n" " else if (count ($found) gt 1)\n" " then\n" " \n" " \n" " ambiguous form tag ({$ns} :: {$name})\n" " \n" " \n" " \n" " {\n" " {\n" " for $f in $found\n" " return\n" " {\n" " $f/doc-soup:payload/doc-soup:meta/flower:form-id,\n" " $f/doc-soup:payload/doc-soup:meta/flower:folder,\n" " $f/doc-soup:payload/doc-soup:meta/flower:title,\n" " $f/doc-soup:payload/doc-soup:meta/flower:summary\n" " }\n" " }\n" " }\n" " \n" " \n" " else\n" " \n" " \n" " no document found with tag ({$ns} :: {$name})\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "};\n" "\n" "flower:get-doc ()\n" "\n"); } catch(const XmlException& e) { cout << e.what(); exit (2); } catch(const DbException& e) { cout << e.what(); exit (3); } catch(const XqvmException& e) { cout << e.what(); exit (4); } // q_get_filter_step_data // // Give a doc-soup message in position 1 of $results, // if it contains "next step" for a flower program then // return a list: // // SERVICE-URI TEXT-OF-SERVICE-REQUEST THE-WHOLE-MSG // // This query is used by the flower interpreter, below. // XmlQueryExpression q_get_filter_step_data; try { q_get_filter_step_data = xqvm.known_query ("\n" "declare ordering ordered;\n" "\n" "if (not ($xqvm:r_results[1]/doc-soup:envelope/flower:service)) then $xqvm:r_results[1]\n" "else\n" "(($xqvm:r_results[1]/doc-soup:envelope/flower:service/@flower:uri/string(), '')[1],\n" " ($xqvm:r_results[1]/doc-soup:envelope/flower:service/text()/string(), '')[1],\n" " $xqvm:r_results[1])\n" "\n"); } catch(const XmlException& e) { cout << e.what(); exit (2); } catch(const DbException& e) { cout << e.what(); exit (3); } catch(const XqvmException& e) { cout << e.what(); exit (4); } // q_get_reply_data // // After the flower program of the message has been run, // the final state of the message is returned as a reply. // // Given a message in position 1 of $results, this returns: // // STATUS MIMETYPE STYLESHEET WHOLE-MSG // // used in formulating an HTTP reply. // XmlQueryExpression q_get_reply_data; try { q_get_reply_data = xqvm.known_query ("\n" "declare ordering ordered;\n" "\n" "let $envelope := $xqvm:r_results[1]/doc-soup:envelope,\n" " $status := ($envelope/flower:status/text(), '0')[1],\n" " $mimetype := ($envelope/flower:mimetype/text(), 'text/xml')[1],\n" " $stylesheet := ($envelope/flower:stylesheet/text(), '')[1]\n" "\n" "return ($status, $mimetype, $stylesheet, $xqvm:r_results[1])\n" "\n"); } catch(const XmlException& e) { cout << e.what(); exit (2); } catch(const DbException& e) { cout << e.what(); exit (3); } catch(const XqvmException& e) { cout << e.what(); exit (4); } //////////////////////////////////////////////////////////////// // // Still more "canned" XQueries. // // The remaining queries all help implement host-specific // "services" that flower programs can invoke. // // In call cases, on entry $thread is initialized as described // above, $results[1] contains the message being processed, // and $results may contain additional application-specific // parameters. // //////////////////////////////////////////////////////////////// //////////////// // Service: flower:delete // Query: q_deletes_from_delete // // The text in the body of the service should contain a // "form id". // // Return from the database all documents whhich have that // id in their payload meta-data. // // (These are then deleted from the database.) // //////////////// XmlQueryExpression q_deletes_from_delete; try { q_deletes_from_delete = xqvm.known_query ("\n" "declare ordering ordered;\n" "\n" "let $id := $xqvm:r_results[1]/doc-soup:envelope/flower:service/text()\n" "return\n" " if (not ($id) or ($id = '_blank')) then ()\n" " else collection()[doc-soup:msg/doc-soup:payload/doc-soup:meta/flower:form-id/text() = $id]" "\n"); } catch(const XmlException& e) { cout << e.what(); exit (2); } catch(const DbException& e) { cout << e.what(); exit (3); } catch(const XqvmException& e) { cout << e.what(); exit (4); } //////////////// // Service: flower:delete // Query: q_after_delete // // $results[1] contains a message with a "flower:delete" // request that has just been processed. // // This rewrites the message, single stepping the flower // program past the "flower:delete" request by invoking // its continuation. // // This returns the rewritten message. // //////////////// XmlQueryExpression q_after_delete; try { q_after_delete = xqvm.known_query ("\n" "declare ordering ordered;\n" "\n" "{\n" " $xqvm:r_results[1]/@*,\n" " {\n" " $xqvm:r_results[1]/doc-soup:envelope/@*,\n" " $xqvm:r_results[1]/doc-soup:envelope/*[not (name() = ('flower:service', 'flower:filter'))],\n" " $xqvm:r_results[1]/doc-soup:envelope/flower:filter/flower:service,\n" " $xqvm:r_results[1]/doc-soup:envelope/flower:filter/flower:filter\n" " },\n" " $xqvm:r_results[1]/doc-soup:payload\n" "}\n" "\n"); } catch(const XmlException& e) { cout << e.what(); exit (2); } catch(const DbException& e) { cout << e.what(); exit (3); } catch(const XqvmException& e) { cout << e.what(); exit (4); } //////////////// // Service: flower:update // Query: q_deletes_from_update // // $results[1] contains a message with a "flower:update" // request. // // Return all documents in the database which share a form // id with this message. // // (These are then deleted from the database.) // //////////////// XmlQueryExpression q_deletes_from_update; try { q_deletes_from_update = xqvm.known_query ("\n" "declare ordering ordered;\n" "\n" "let $id := $xqvm:r_results[1]/doc-soup:payload/doc-soup:meta/flower:form-id/text()\n" "return\n" " if (not ($id) or ($id = '_blank')) then ()\n" " else collection()[doc-soup:msg/doc-soup:payload/doc-soup:meta/flower:form-id/text() = $id]" "\n"); } catch(const XmlException& e) { cout << e.what(); exit (2); } catch(const DbException& e) { cout << e.what(); exit (3); } catch(const XqvmException& e) { cout << e.what(); exit (4); } //////////////// // Service: flower:update // Query: q_after_add_for_update // // $results[1] contains a message with a "flower:update" // request. // // This rewrites the message, single stepping the flower // program past the "flower:delete" request by invoking // its continuation. // // This returns the rewritten message followed by any // values already in $resuls. // // This detects the (errant) case of attempting // to modify the "_blank" document and inserts an // error message ("flower:ambiguity") in that case. // // If the final message contains no ambiguities, // it is stored. // //////////////// XmlQueryExpression q_after_add_for_update; try { q_after_add_for_update = xqvm.known_query ("\n" "declare ordering ordered;\n" "\n" "let $id := $xqvm:r_results[1]/doc-soup:payload/doc-soup:meta/flower:form-id/text()\n" "return\n" " if ($id and not ($id = '_blank'))\n" "then\n" " ({\n" " $xqvm:r_results[1]/@*,\n" " {\n" " $xqvm:r_results[1]/doc-soup:envelope/@*,\n" " $xqvm:r_results[1]/doc-soup:envelope/*[not (name() = ('flower:service', 'flower:filter'))],\n" " $xqvm:r_results[1]/doc-soup:envelope/flower:filter/flower:service,\n" " $xqvm:r_results[1]/doc-soup:envelope/flower:filter/flower:filter\n" " },\n" " $xqvm:r_results[1]/doc-soup:payload\n" " },\n" " $xqvm:r_results[position() gt 1])\n" "else\n" " ({\n" " $xqvm:r_results[1]/@*,\n" " {\n" " $xqvm:r_results[1]/doc-soup:envelope/@*,\n" " $xqvm:r_results[1]/doc-soup:envelope/*[not (name() = ('flower:ambiguity', 'flower:service'))],\n" " {\n" " $xqvm:r_results[1]/doc-soup:envelope/flower:ambiguity/node(),\n" "
,\n" " text {'the _blank document may not be modified'}\n" " }
\n" " }
,\n" " $xqvm:r_results[1]/doc-soup:payload\n" " }
,\n" " $xqvm:r_results[position() gt 1])\n" "\n"); } catch(const XmlException& e) { cout << e.what(); exit (2); } catch(const DbException& e) { cout << e.what(); exit (3); } catch(const XqvmException& e) { cout << e.what(); exit (4); } //////////////// // Service: flower:update // Query: q_check_for_add // // Return a non-empty result if and only if $results[1] // is a non-ambiguous message. // //////////////// XmlQueryExpression q_check_for_add; try { q_check_for_add = xqvm.known_query ("\n" "declare ordering ordered;\n" "\n" "if ($xqvm:r_results[1]/doc-soup:payload/flower:ambiguous)\n" "then ()\n" "else \n"); } catch(const XmlException& e) { cout << e.what(); exit (2); } catch(const DbException& e) { cout << e.what(); exit (3); } catch(const XqvmException& e) { cout << e.what(); exit (4); } //////////////// // Service: flower:compute-body // Query: q_computed_body_assemble // // // (this appears to be buggy at the moment) // //////////////// XmlQueryExpression q_computed_body_assemble; try { q_computed_body_assemble = xqvm.known_query ("\n" "declare ordering ordered;\n" "\n" "\n" " {$xqvm:r_results[1]/@*}\n" " {\n" " $xqvm:r_results[1]/doc-soup:envelope/@*,\n" " $xqvm:r_results[1]/doc-soup:envelope/node()[not (name() = ('flower:filter', 'flower:service'))],\n" " $xqvm:r_results[1]/doc-soup:envelope/flower:filter/flower:service,\n" " $xqvm:r_results[1]/doc-soup:envelope/flower:filter/flower:filter\n" " }\n" " \n" " {$xqvm:r_results[1]/doc-soup:payload/@*}\n" " {$xqvm:r_results[1]/doc-soup:payload/doc-soup:meta}\n" " \n" " {$xqvm:r_gestalt}\n" " \n" " \n" ",\n" "\n" "$xqvm:r_results[position() gt 1]\n" "\n"); } catch(const XmlException& e) { cout << e.what(); exit (2); } catch(const DbException& e) { cout << e.what(); exit (3); } catch(const XqvmException& e) { cout << e.what(); exit (4); } //////////////////////////////////////////////////////////////// // // The Fast CGI Loop // //////////////////////////////////////////////////////////////// FCGX_Request request; FCGX_Init(); FCGX_InitRequest(&request, 0, 0); while (FCGX_Accept_r(&request) == 0) { struct timeval started; struct timeval started_thread; struct timeval ended_thread; struct timeval started_edit; struct timeval ended_edit; gettimeofday (&started, 0); try { FastCgiIO IO (request); Cgicc cgi (&IO); const CgiEnvironment &cgi_env = cgi.getEnvironment(); xqvm.query_context.setReturnType (XmlQueryContext::DeadValues); xqvm.query_context.setEvaluationType (XmlQueryContext::Eager); //////////////////////////////////////////////////////////////// // // Authenticate the User and Retrieve or Construct a Session Object // //////////////////////////////////////////////////////////////// form_iterator f_login_request = cgi.getElement("flower:login-request"); form_iterator f_login_name = cgi.getElement("flower:login-name"); form_iterator f_login_password = cgi.getElement("flower:login-password"); string login_request = ""; string login_name = ""; string login_password = ""; if (f_login_request != cgi.getElements().end()) login_request = f_login_request->getValue(); if (f_login_name != cgi.getElements().end()) login_name = f_login_name->getValue(); if (f_login_password != cgi.getElements().end()) login_password = f_login_password->getValue(); std::vector::const_iterator cookies; string session_cookie = ""; for (cookies = cgi_env.getCookieList().begin(); cookies != cgi_env.getCookieList().end(); ++cookies) { string name = cookies->getName(); if (name == "flower:session-cookie") { session_cookie = cookies->getValue(); break; } } int use_session = 0; int logout_session = 0; int new_session = 0; int anonymous_session = 0; if (login_request == "login") { use_session = 0; logout_session = 1; new_session = 1; anonymous_session = 0; } else if (login_request != "") { use_session = 0; logout_session = 1; new_session = 0; anonymous_session = 1; } else if (session_cookie != "") { use_session = 1; logout_session = 0; new_session = 0; anonymous_session = 0; } else { use_session = 0; logout_session = 0; new_session = 0; anonymous_session = 1; } if (logout_session && (session_cookie != "")) { xqvm.query_context.setVariableValue ("xqvm:r_results", session_cookie); XmlResults gonzo_sessions = q_get_session_by_id.execute (xqvm.txn, xqvm.query_context, 0); XmlDocument doc; while (gonzo_sessions.next (doc)) { xqvm.container.deleteDocument (xqvm.txn, doc, xqvm.update_context); } session_cookie = ""; use_session = 0; } int have_session = 0; XmlValue session; if (use_session) { xqvm.query_context.setVariableValue ("xqvm:r_results", session_cookie); XmlResults found_sessions = q_get_session_by_id.execute (xqvm.txn, xqvm.query_context, 0); if (found_sessions.next(session)) have_session = 1; } string errant_attempt = ""; string errant_msg = ""; if (new_session) { if (login_name == "") { have_session = 0; new_session = 0; use_session = 0; anonymous_session = 1; } else { xqvm.query_context.setVariableValue ("xqvm:r_results", login_name); XmlResults found_account = q_get_account_by_login_name.execute (xqvm.txn, xqvm.query_context, 0); XmlValue account; XmlValue salt; XmlValue password; XmlValue id; if (!found_account.next (account) || !found_account.next (salt) || !found_account.next (password) || !found_account.next (id)) { have_session = 0; new_session = 0; use_session = 0; anonymous_session = 1; errant_attempt = login_name; errant_msg = "no such user account"; } else { string salty_password = salt.asString() + login_password; unsigned char raw_sha1[20] = {0}; char hex_sha1[41] = {0}; SHA1 ((const unsigned char *)salty_password.c_str(), salty_password.length(), raw_sha1); int x; for (x = 0; x < 20; ++x) { static const char hex[] = "0123456789abcdef"; hex_sha1[x * 2] = hex[raw_sha1[x] >> 4]; hex_sha1[(x * 2) + 1] = hex[raw_sha1[x] &0xf]; } hex_sha1[40] = '\0'; if (password.asString() != hex_sha1) { have_session = 0; new_session = 0; use_session = 0; anonymous_session = 1; errant_attempt = login_name; errant_msg = "incorrect password"; } else { xqvm.query_context.setVariableValue ("xqvm:r_results", account); uuid gestalt; gestalt.make (UUID_MAKE_V4); xqvm.query_context.setVariableValue ("xqvm:r_gestalt", gestalt.string()); XmlResults built_session = q_make_session_from_account.execute (xqvm.txn, xqvm.query_context, 0); (void)built_session.next (session); xqvm.container.putDocument (xqvm.txn, "session-", session.asString (), xqvm.update_context, DbXml::DBXML_GEN_NAME); have_session = 1; new_session = 1; use_session = 0; anonymous_session = 0; } } } } if (anonymous_session) { if ((errant_attempt != "") || (errant_msg != "")) xqvm.query_context.setVariableValue ("xqvm:r_results", (errant_attempt + ": " + errant_msg)); else xqvm.query_context.setVariableValue ("xqvm:r_results", ""); XmlResults anonymous_session = q_make_anonymous_session.execute (xqvm.txn, xqvm.query_context, 0); (void)anonymous_session.next (session); have_session = 1; } //////////////////////////////////////////////////////////////// // // Construct an XQVM Thread State // // The code to this point is a "hand compiled" version of an // XQVM program. Now we need to continue that program, // briefly, in "interpreted mode". So, here, construct a // thread state for that interpretation. // // Note that an optimization applies here: while "flower" // runs the interpreted part, the step rule in the thread // (the "query" element) is always the same and, in fact, // can be implemented more efficiently by hand-compiling into // the "filter loop" further below. // //////////////////////////////////////////////////////////////// XmlResults form_param_results = q_nil_query.execute (xqvm.txn, xqvm.query_context, 0); std::vector::const_iterator params; for (params = cgi.getElements().begin(); params != cgi.getElements().end(); ++params) { string name = params->getName(); string value = params->getValue(); if ((name.length() >= 4) && (name.substr (0, 4) == "xml:")) { form_param_results.add (XmlValue (name)); XmlDocument doc = xqvm.xml_mgr.createDocument (); XmlInputStream * is = xqvm.xml_mgr.createMemBufInputStream (value.c_str(), value.length(), 1); doc.setContentAsXmlInputStream (is); form_param_results.add (XmlValue (doc)); } else if ((name.length() >= 5) && (name.substr (0, 5) == "xmlb:")) { form_param_results.add (XmlValue (name)); XmlDocument doc = xqvm.xml_mgr.createDocument (); string wrapped_value = (string)"" + value + ""; XmlInputStream * is = xqvm.xml_mgr.createMemBufInputStream (wrapped_value.c_str(), wrapped_value.length(), 1); doc.setContentAsXmlInputStream (is); form_param_results.add (XmlValue (doc)); } else { form_param_results.add (XmlValue (name)); form_param_results.add (XmlValue (value)); } } xqvm.query_context.setVariableValue ("xqvm:r_results", form_param_results); XmlResults form_param_param_results = q_fold_params.execute (xqvm.txn, xqvm.query_context, 0); form_param_param_results.add (session); string xslt_dir; form_iterator style_root = cgi.getElement("flower:style-root"); if (style_root != cgi.getElements().end()) { xslt_dir = style_root->getValue() + "/"; } else if (dflt_style) { xslt_dir = (string)dflt_xslt_dir + "/"; } else { xslt_dir = ""; } string fallback_style; form_iterator requested_style = cgi.getElement("flower:default-style"); if (requested_style != cgi.getElements().end()) { fallback_style = requested_style->getValue(); } else if (dflt_style) { fallback_style = dflt_style; } else { fallback_style = ""; } form_param_param_results.add ("XsltBase"); form_param_param_results.add (xslt_dir); form_param_param_results.add ("DefaultStyle"); form_param_param_results.add (fallback_style); form_param_param_results.add ("RequestMethod"); form_param_param_results.add (cgi.getEnvironment().getRequestMethod()); form_param_param_results.add ("PathInfo"); form_param_param_results.add (cgi.getEnvironment().getPathInfo()); form_param_param_results.add ("PathTranslated"); form_param_param_results.add (cgi.getEnvironment().getPathTranslated()); form_param_param_results.add ("ScriptName"); form_param_param_results.add (cgi.getEnvironment().getScriptName()); form_param_param_results.add ("Referrer"); form_param_param_results.add (cgi.getEnvironment().getReferrer()); form_param_param_results.add ("Cookies"); form_param_param_results.add (cgi.getEnvironment().getCookies()); form_param_param_results.add ("QueryString"); form_param_param_results.add (cgi.getEnvironment().getQueryString()); form_param_param_results.add ("RemoteHost"); form_param_param_results.add (cgi.getEnvironment().getRemoteHost()); form_param_param_results.add ("RemoteAddr"); form_param_param_results.add (cgi.getEnvironment().getRemoteAddr()); form_param_param_results.add ("AuthType"); form_param_param_results.add (cgi.getEnvironment().getAuthType()); form_param_param_results.add ("RemoteUser"); form_param_param_results.add (cgi.getEnvironment().getRemoteUser()); form_param_param_results.add ("RemoteIdent"); form_param_param_results.add (cgi.getEnvironment().getRemoteIdent()); form_param_param_results.add ("ContentType"); form_param_param_results.add (cgi.getEnvironment().getContentType()); form_param_param_results.add ("ContentLength"); ostringstream cl; cl << cgi.getEnvironment().getContentLength(); form_param_param_results.add (cl.str()); form_param_param_results.add ("Accept"); form_param_param_results.add (cgi.getEnvironment().getAccept()); form_param_param_results.add ("UserAgent"); form_param_param_results.add (cgi.getEnvironment().getUserAgent()); form_param_param_results.add ("ServerSoftware"); form_param_param_results.add (cgi.getEnvironment().getServerSoftware()); form_param_param_results.add ("ServerName"); form_param_param_results.add (cgi.getEnvironment().getServerName()); form_param_param_results.add ("GatewayInterface"); form_param_param_results.add (cgi.getEnvironment().getGatewayInterface()); form_param_param_results.add ("ServerProtocol"); form_param_param_results.add (cgi.getEnvironment().getServerProtocol()); form_param_param_results.add ("RedirectRequest"); form_param_param_results.add (cgi.getEnvironment().getRedirectRequest()); form_param_param_results.add ("RedirectURL"); form_param_param_results.add (cgi.getEnvironment().getRedirectURL()); form_param_param_results.add ("RedirectStatus"); form_param_param_results.add (cgi.getEnvironment().getRedirectStatus()); form_param_param_results.add ("HTTPS"); form_param_param_results.add ((cgi.getEnvironment().usingHTTPS() ? "true" : "")); form_param_param_results.add ("MyURL"); form_param_param_results.add (url); xqvm.query_context.setVariableValue ("xqvm:r_results", form_param_param_results); gettimeofday (&started_thread, 0); XmlResults thread_results = q_finish_making_thread.execute (xqvm.txn, xqvm.query_context, 0); XmlValue thread; thread_results.next (thread); gettimeofday (&ended_thread, 0); xqvm.query_context.setVariableValue ("xqvm:r_thread", thread); //////////////////////////////////////////////////////////////// // // Retrieve or Construct the Base Document Indicated by the Request // //////////////////////////////////////////////////////////////// uuid gestalt; gestalt.make (UUID_MAKE_V4); xqvm.query_context.setVariableValue ("xqvm:r_gestalt", gestalt.string()); XmlValue initial_document; gettimeofday (&started_edit, 0); try { XmlResults loop_init_results = q_get_base_doc_for_request.execute (xqvm.txn, xqvm.query_context, 0); loop_init_results.next (initial_document); } catch(const XmlException& e) { cout << e.what(); exit (2); } catch(const DbException& e) { cout << e.what(); exit (3); } catch(const XqvmException& e) { cout << e.what(); exit (4); } gettimeofday (&ended_edit, 0); //////////////////////////////////////////////////////////////// // // The Filter Loop: Interpret the Filter Process // //////////////////////////////////////////////////////////////// xqvm.query_context.setVariableValue ("xqvm:r_results", initial_document); { uuid gestalt; gestalt.make (UUID_MAKE_V4); xqvm.query_context.setVariableValue ("xqvm:r_gestalt", gestalt.string()); } while (1) { XmlResults filter_step_results = q_get_filter_step_data.execute (xqvm.txn, xqvm.query_context, 0); XmlValue filter_uri_v; XmlValue filter_text_v; XmlValue current_doc_v; int x, y, z; x = filter_step_results.next(filter_uri_v); y = x && filter_step_results.next(filter_text_v); z = y && filter_step_results.next(current_doc_v); if (!z) break; string filter_uri = filter_uri_v.asString(); string filter_text = filter_text_v.asString(); if (filter_uri == "flower:compute-body") { // // authorization check goes here // if (filter_text != "") { XmlQueryExpression body_query = xqvm.known_query (filter_text); uuid gestalt; gestalt.make (UUID_MAKE_V4); xqvm.query_context.setVariableValue ("xqvm:r_gestalt", gestalt.string()); XmlResults body_results = body_query.execute (xqvm.txn, xqvm.query_context, 0); xqvm.query_context.setVariableValue ("xqvm:r_gestalt", body_results); XmlResults new_results = q_computed_body_assemble.execute (xqvm.txn, xqvm.query_context, 0); xqvm.query_context.setVariableValue ("xqvm:r_results", new_results); gestalt.make (UUID_MAKE_V4); xqvm.query_context.setVariableValue ("xqvm:r_gestalt", gestalt.string()); } } else if (filter_uri == "flower:delete") { // // authorization check goes here // XmlResults to_delete = q_deletes_from_delete.execute (xqvm.txn, xqvm.query_context, 0); XmlDocument d; while (to_delete.next(d)) { xqvm.container.deleteDocument (xqvm.txn, d, xqvm.update_context); } XmlResults from_delete = q_after_delete.execute (xqvm.txn, xqvm.query_context, 0); xqvm.query_context.setVariableValue ("xqvm:r_results", from_delete); } else if (filter_uri == "flower:update") { // // authorization check goes here // XmlResults to_delete = q_deletes_from_update.execute (xqvm.txn, xqvm.query_context, 0); XmlDocument d; while (to_delete.next(d)) { xqvm.container.deleteDocument (xqvm.txn, d, xqvm.update_context); } XmlResults from_add = q_after_add_for_update.execute (xqvm.txn, xqvm.query_context, 0); XmlValue a; xqvm.query_context.setVariableValue ("xqvm:r_results", from_add); from_add.reset (); if (from_add.next(a)) { XmlResults add_is_ok = q_check_for_add.execute (xqvm.txn, xqvm.query_context, 0); XmlValue q; if (add_is_ok.next (q)) { xqvm.container.putDocument (xqvm.txn, "doc-", a.asString(), xqvm.update_context, DbXml::DBXML_GEN_NAME); } } } else { break; } } //////////////////////////////////////////////////////////////// // // Transmit the Reply // //////////////////////////////////////////////////////////////// { uuid gestalt; gestalt.make (UUID_MAKE_V4); xqvm.query_context.setVariableValue ("xqvm:r_gestalt", gestalt.string()); } XmlResults reply_data = q_get_reply_data.execute (xqvm.txn, xqvm.query_context, 0); XmlValue status; XmlValue mimetype; XmlValue stylesheet; XmlValue contents; if (reply_data.next(status) && reply_data.next(mimetype) && reply_data.next(stylesheet) && reply_data.next(contents)) { IO << "Status: " << status.asString() << endl << "Content-type: " << mimetype.asString() << endl << endl; string style = stylesheet.asString (); if (style == "") { style = fallback_style; } if (style != "") { IO << "" << endl; } IO << contents.asString (); } xqvm.txn.commit (); xqvm.txn = xqvm.xml_mgr.createTransaction (); } catch(const XmlException& e) { cout << e.what(); exit (2); } catch(const DbException& e) { cout << e.what(); exit (3); } catch(const XqvmException& e) { cout << e.what(); exit (4); } catch(const exception& e) { // handle error condition exit (1); } struct timeval ended; gettimeofday (&ended, 0); long tsecs = (ended_thread.tv_sec - started_thread.tv_sec); long tusecs = (ended_thread.tv_usec - started_thread.tv_usec); long tall_usecs = (tusecs + (1000000 * tsecs)); long tall_msecs = (tall_usecs / 1000); long esecs = (ended_edit.tv_sec - started_edit.tv_sec); long eusecs = (ended_edit.tv_usec - started_edit.tv_usec); long eall_usecs = (eusecs + (1000000 * esecs)); long eall_msecs = (eall_usecs / 1000); long secs = (ended.tv_sec - started.tv_sec); long usecs = (ended.tv_usec - started.tv_usec); long all_usecs = (usecs + (1000000 * secs)); long all_msecs = (all_usecs / 1000); cerr << "elapsed " << all_msecs << " msecs (" << tall_msecs << " thread-building, " << eall_msecs << " editting)\n"; } xqvm.txn.commit (); xqvm.txn = xqvm.xml_mgr.createTransaction (); } catch(const XmlException& e) { cout << e.what(); exit (2); } catch(const DbException& e) { cout << e.what(); exit (3); } catch(const XqvmException& e) { cout << e.what(); exit (4); } catch(const exception& e) { // handle error condition exit (1); } return 0; }