pax_global_header00006660000000000000000000000064127407166510014523gustar00rootroot0000000000000052 comment=fef51cdb13ce3f10ab57feb1bd040bab6b5983fe basex-8.5.1/000077500000000000000000000000001274071665100126405ustar00rootroot00000000000000basex-8.5.1/.dockerignore000066400000000000000000000003331274071665100153130ustar00rootroot00000000000000**/.gitignore **/*.bat .git basex-examples basex-tests basex-api !basex-api/.classpath !basex-api/etc !basex-api/lib !basex-api/license.txt !basex-api/pom.xml !basex-api/src/main/java !basex-api/src/main/webapp/WEB-INF basex-8.5.1/.gitignore000066400000000000000000000001571274071665100146330ustar00rootroot00000000000000## generic files to ignore *~ *.lock *.DS_Store *.swp *.out org.maven.ide.eclipse.prefs .idea/ *.class /target basex-8.5.1/CHANGELOG000066400000000000000000000673741274071665100140730ustar00rootroot00000000000000VERSION 8.5.1 (July 11, 2016) ------------------------------------------ Bug fixes, experimental 8.6 features. VERSION 8.5 (July 4, 2016) --------------------------------------------- DATABASE JOBS - all registered database jobs are now centrally administered - jobs can be scheduled (periodical execution, start/end time) - job results can be cached and retrieved later on - new Jobs Module: XQuery functions for managing jobs - new commands: JOBS [LIST|RESULT|STOP] - DBA: visualization of currently registered jobs XQUERY - parallel execution via xquery:fork-join (thank you, James Wright!) - alignments with the latest changes in the XQuery 3.1 specification - easy chaining of update operations with the 'update' keyword - numerous optimizations, smarter rewritings and heuristics XQUERY MODULES - File, Conversion, Fetch Module: handling of invalid XML characters - Inspection Module: inspect:function-annotations added OPTIONS - LOGPATH: custom path for logging data - CACHETIMEOUT: timeout for dropping cached job results - AUTHMETHOD: `custom` added to skip authentication VERSION 8.4.4 (May 3, 2016) -------------------------------------------- Minor bug fixes and tweaks. VERSION 8.4.3 (April 13, 2016) ----------------------------------------- Minor bug fixes, experimental 8.5 features. VERSION 8.4.2 (March 30, 2016) ----------------------------------------- Minor bug fixes, performance tweaks, experimental 8.5 features. VERSION 8.4.1 (March 3, 2016) ------------------------------------------ Minor bug fixes, experimental support for 8.5 features. VERSION 8.4 (February 8, 2016) ----------------------------------------- GUI - Project View: parse all modules in a project in the background - Create Database, Properties: access to advanced database options - Sort Dialog: order by column positions and locale collation INDEXING - New Token Index: speedy token lookups in DITA and HTML documents - Including full support for incremental and main-memory databases - XQuery optimizations for fn:contains-token, fn:tokenize, fn:idref WEB APPLICATIONS - Cancel server-side queries via %rest:single annotation - DBA query editor: faster querying, better responsivity XQUERY - Database Module: ADDCACHE support (caching of large results) - Direct output of binary data with new 'basex' serialization method - Better XQuery 3.1 compliance: string constructors, new functions - Java bindings: address specific Java function signatures - User Module: create users and grant permissions in one step DOCUMENTATION - Many Wiki articles have been revised and updated VERSION 8.3.1 (October 29, 2015) --------------------------------------- - XQuery 3.1: alignments with latest spec - Bug fixes (GUI errors, XPath optimizations) VERSION 8.3 (September 23, 2015) --------------------------------------- SELECTIVE INDEXING - restrict value indexing to given elements and attributes - support extended to updatable and main-memory databases VALIDATION - support for RelaxNG validation (XML and compact syntax) - optional XML Schema 1.1 support - new function for creating validation reports XQUERY MODULES - Strings Module: Levenshtein, Soundex, Cologne Phonetic - updates in the Archive, Database, Admin and Process modules DBA - File panel: upload and download files BUG FIXES - automatic database optimization if node id turns negative - XQuery optimizations, performance tweaks, WebDAV issues VERSION 8.2.3 (July 14, 2015) ------------------------------------------ Bug fixes (DBA, admin:write-log; namespaces) VERSION 8.2.2 (July 6, 2015) ------------------------------------------- Various bug fixes (all minor) VERSION 8.2.1 (June 9, 2015) ------------------------------------------- DBA - code highlighting (thanks, James Ball!) - new panel for up- and downloading files - queries and files are now stored in a temporary directory GENERAL - various bug fixes VERSION 8.2 (May 21, 2015) --------------------------------------------- XQUERY - much faster sequence modification via finger trees - improved compliance with XQuery 3.1 DBA - open, save and delete queries - better Tomcat support STORAGE - updatable index structures: reduced disk space consumption XQUERY FUNCTIONS - Standard Module: fn:json-to-xml, fn:xml-to-json - Web Module: web:encode-url, web:decode-url - File Module: file:is-absolute, file:resolve-path - Admin Module: admin:delete-logs - Database Module: db:output-cache BUG FIXES - locking, full-text requests, stemming REMOVED FEATURES - event handling (will be replaced by database triggers) VERSION 8.1.1 (April 16, 2015) ----------------------------------------- Various bug fixes (all minor) VERSION 8.1 (March 28, 2015) ------------------------------------------- XQUERY - efficient Finger Tree algorithm for arrays - prof:variables() outputs list of currently bound variables - new higher-order functions: hof:scan-left, hof:take-while WEB APPLICATIONS - Web Module offers convenience functions for building web apps - RESTXQ: %input annotation; input-specific content-type parameters OPTIONS - RESTPATH: specify server path for queries and command scripts - IGNORECERT: ignore untrusted certificates when connecting to servers COMMAND-LINE - Bind input strings to the query context with -I VERSION 8.0.3 (March 19, 2015) ----------------------------------------- Various bug fixes (all minor) VERSION 8.0.2 (March 9, 2015) ------------------------------------------ REST - Better support for concurrent read and write operations XQUERY - Speed up wildcard queries without wildcards - XQUnit: compare error codes as QNames - Fix: process each single flwor clause when removing variables - Fix: consider xml:space='preserve' during serialization CORE - Fix: consider case when pinning databases HTTP: - Fix: digest authentication, correct quoting VERSION 8.0.1 (February 22, 2015) -------------------------------------- XQUERY - Faster execution of single, index-based results - Iterative evaluation of steps with multiple predicates Minor bug fixes: - WebDAV locking - Archive Module - Adaptive serialization of arrays and maps - Digest Authentication - Save command-line history VERSION 8.0 (February 9, 2015) ----------------------------------------- XQUERY - Support for XQuery 3.1 (nearly complete) - New default serialization ("adaptive") - MIXUPDATES option: update items and return results at the same time - Improved index rewritings, more precise cost estimations - New modules: Array and User Module - New annotations: %basex:inline, %basex:lazy - 13 of our modules were updated and extended STORAGE - Existing documents will be overwritten in-place whenever possible - Updatable index structures now take much less space - Faster document index (storing paths to all XML documents) - AUTOOPTIMIZE option: optimize after each update - XINCLUDE option: resolve or ignore XInclude tags SECURITY - Revised user management; users are stored as XML - Digest authentication, Salted SHA256 - Language bindings updated WEB APPLICATIONS - Integrated application: DBA, Database Administration - AUTHMETHOD option: Basic vs. Digest authentication - RESTXQ: regular expressions in paths, quality factor support GUI - Better code completions VERSION 7.9 (June 27, 2014) -------------------------------------------- XQUNIT - Unit testing has been improved a lot. All test functions will now be evaluated separately; this way, updates can be performed within test. - with the new TEST command, all test modules in a specified directory can be evaluated. - tests can be invoked from within the GUI editor and project view. - on command-line, the -t flag can be used for the same purpose. RESTXQ - Custom HTTP methods can be defined via %rest:method - Error handling has been improved and aligned with try/catch XQUERY MODULES - Database Module: parsing options for db:create/db:add REST - The "run" operation allows execution of server-side command scripts VERSION 7.8.2 (April 4, 2014) ------------------------------------------ GUI - Ctrl-U: efficiently sort large texts - Ctrl-H, Ctrl-J: context-sensitive behavior - tweaked code automatisms STORAGE, UPDATES - faster processing of documents with namespaces XQUERY MODULES - Database Module: alter, copy, create-backup, drop-backup, restore - Admin Module: new merge option, existing functions revised - XQuery Module: new evaluation options (memory, timeout, permissions) XQUERY OPTIMIZATIONS - always show full stack trace (enforce it with INLINELIMIT=0) - show stack trace again - better error messages - improved function inlining - prevent pre-evaluation of try/catch - Easter egg: partial sums (little Gauss) calculation API - WebDAV: improved password processing - Client/Server: reduce waiting time if password is wrong VERSION 7.8.1 (February 26, 2014) -------------------------------------- ADDED - Editor, Ctrl-H: filter by opened file type and selected text - XML Parsing: support for TAR & TGZ UPDATED - XQuery errors: function not found -> suggest similar one - Commands: improved parsing (allow more whitespaces) - French translation (thanks to Maud Ingarao!) FIXED - XQuery Update: nested transform expressions - XQuery: always return boolean when calling doc-available() - XQuery: disallow impossible casts before removing variables - Binary Module: iterative evaluation of bin:octets() - Commands, REPO INSTALL: install packages in same directory - CSV/JSON: serialization of underscores VERSION 7.8 (February 12, 2014) ---------------------------------------- GUI - new project view for organizing and opening project files - realtime search of project files and contents - various new editor shortcuts and code completions, code formatting UPDATES - improved performance of delete and insert operations - faster in-place value updates - 'update': convenience keyword for transform expressions XQUERY OPTIMIZATIONS - XQuery functions are now inlined and further optimized - closure optimizations, better static typing - improved detection and rewriting of tail calls - faster processing of (sub)sequences XQUERY MODULES - JSON and CSV Module: updated and enhanced conversion rules - Unit Module: enhanced test report output - Map Module: map:serialize added; syntax aligned with latest spec. - File Module: aligned with changes in EXPath spec. - XQuery Module: xquery:evaluate (operates on opened databases) - Full-Text Module: ft:contains added, ft:search: more options - EXPath Binary Module added - Java modules: support for locking annotations API - New options: INLINELIMIT, TAILCALLS, DEFAULTDB, RUNQUERY - New command-line flag: -R only parse query - Russian translation added (thanks to Oleksandr Shpak!) - Spanish translation added (thanks to Carlos Marcos!) VERSION 7.7.2 (October 7, 2013) ---------------------------------------- XQUERY - CSV Module and serialization added - JSON serializer updated (more to follow) - update checks in modify clause fixed - parsing of new map syntax fixed (ignoring spaces) - tail call handling in built-in higher order functions fixed API - Russian translation added (thanks to Oleksandr Shpak, Max Shamaev) - command-line arguments starting with '<' are interpreted as XQuery INDEXING - bug fixed in updatable index structure VERSION 7.7.1 (September 16, 2013) ------------------------------------- XQUERY - new map syntax: { 'key' : 'value' } - tail call optimization for dynamic functions - optimize fn:subsequence() calls and sequence casts - db:optimize(): original options are preserved WEBDAV - return non-breakable spaces as   CORE - avoid OutOfMemory exception when building large databases VERSION 7.7 (August 7, 2013) ------------------------------------------- XQUERY - Our XQuery 3.0 support is now complete! http://docs.basex.org/wiki/XQuery_3.0 - the Unit Module allows standardized testing of XQuery applications http://docs.basex.org/wiki/Unit_Module - the Streaming Module speeds up operations on large files http://docs.basex.org/wiki/Streaming_Module - the Inspection Module provides reflection and documentation features http://docs.basex.org/wiki/Inspection_Module - we have added support for XQuery collations http://docs.basex.org/wiki/Full-Text#Collations - we have extended the Database, Archive, File and other Modules http://docs.basex.org/wiki/Module_Library COMMANDS - database names have got more flexible http://docs.basex.org/wiki/Commands#Valid_Names - we have new options for simplifying the creation of large databases http://docs.basex.org/wiki/Options WEB/HTTP SERVICES - WebDAV has been enriched with locking features (sponsored feature!) http://docs.basex.org/wiki/WebDAV#Locking - RESTXQ has been improved and extended: http://docs.basex.org/wiki/RESTXQ VERSION 7.6 (February 5, 2013) ----------------------------------------- DATABASE LOCKING: - updates on different databases can now be executed in parallel and won't lock your read-only queries anymore: http://docs.basex.org/wiki/Transaction_Management XQUERY - when errors are raised, the full stack trace is now returned - the EXPath Geo Module, Fetch Module and HTML Module have been added: http://docs.basex.org/wiki/Module_Library - the Validation, XSLT, Database and Profiling Module have been updated GUI - error messages are now clickable and linked with the text editor - trace/profiling output is redirected to the info view in realtime VERSION 7.5 (December 21, 2012) ---------------------------------------- XQUERY UPDATE - bulk updates are now much faster than before - insert and replace operations take much less memory - databases can now be created via XQuery and db:create() GUI TEXT EDITOR - a fast and flexible search/replace panel has been added - error highlighting has been extended to XML files - the editor was improved for editing arbitrary text files XQUERY 3.0 - the code has been aligned with latest changes in the specs - HTML5 support has been updated WEB APPLICATIONS - new modules have been added: Request, Session, Sessions - logging has been revised and extended to HTTP requests - SSL support added, switched to Jetty 8 - all operation modes have been unified and simplified - RESTXQ elements added to simplify forwarding and redirects CORE FEATURES - improved stability of concurrent read/write operations - the INSPECT command performs database sanity checks - EXECUTE triggers the execution of command scripts VERSION 7.3 (June 18, 2012) -------------------------------------------- - Many new internal XQuery Modules have been added, and existing ones have been revised to ensure long-term stability of your future XQuery applications: http://docs.basex.org/wiki/Module_Library - A new powerful Command API is provided to specify BaseX commands and scripts as XML: http://docs.basex.org/wiki/Commands#Basics - The full-text fuzzy index was extended to also support wildcard queries: http://docs.basex.org/wiki/Full-Text - The XQuery 3.0 simple map operator gives you a compact syntax to process items of sequences: http://docs.basex.org/wiki/XQuery_3.0 - BaseX as Web Application can now start its own server instance: http://docs.basex.org/wiki/Web_Application - All command-line options will now be executed in the given order: http://docs.basex.org/wiki/Startup_Options - Charles Foster's latest XQJ Driver supports XQuery 3.0 and the Update and Full Text extensions: http://xqj.net/basex/ VERSION 7.2.1 (April 27, 2012) ----------------------------------------- - Our value indexes now support string-based range queries: http://docs.basex.org/wiki/Indexes#Value_Indexes - Our new XQJ API is based on Charles Foster's implementation. It fully utilizes the client/server architecture: http://xqj.net/basex - Import of XQuery modules has been simplified: http://docs.basex.org/wiki/Repository - Better Java code integration: http://docs.basex.org/wiki/Java_Bindings - Full support for the XQuery 3.0 Regular Expressions syntax: http://www.w3.org/TR/xpath-functions-30/#regex-syntax - Updating functions can now return values: http://docs.basex.org/wiki/Database_Module#db:output - Unified handling of document and database URIs: http://docs.basex.org/wiki/Databases#Access_Resources - Pinning of opened database replaced by filesystem locking: http://docs.basex.org/wiki/Transaction_Management#Locking - REST, RESTXQ, WebDav: concurrency issues fixed VERSION 7.2 (March 24, 2012) ------------------------------------------- - support for the new RESTXQ API for building XQuery web services - improved support for running BaseX as web application - XQuery: higher order functions added to speed up Top-K queries - proxy server settings added - advanced TagSoup options added for importing HTML files - XQuery: faster traversal of full-text index entries: ft:tokens() - Command-line: embedded readline and history support via JLine - XQuery 3.0: annotation added, updated EQName syntax (Q{uri}name) - opened databases are now pinned OS-wide to reduce write conflicts - HTML5 serialization of query results - a printable version of our Wiki documentation VERSION 7.1.1 (February 19, 2012) -------------------------------------- GUI: - new "Package" dialog: to list, install and delete XQuery Packages - "New/Add" dialog: "RAW" input format allows import of raw files; automatic detection of input formats, increased usability - "Export" dialog enhanced to support other methods like JSON, and various serialization parameters Command-Line: - new option -L: add trailing newline after query result - new option -C: execute commands from batch script REST: - new "option" parameter: set options before executing request Full-Text: - Indonesian Stemmer (thanks Andria Arisal!) IO: - faster read/write access to byte arrays VERSION 7.1 (February 8, 2012) ----------------------------------------- GUI - improved document management: hierarchical view of db resources - easier index creation/removal and database optimization - JSON/JsonML parser added - editor: updated and reopened files are reverted from disk REST - automatic XML conversion of popular content types (JSON/JsonML, CSV, HTML) XQUERY - new index module for directly accessing database index structures - new repository module to manage EXPath packages via XQuery - db:list-details() returns information on the database resources - db:content-type() retrieves content-type of a specific resource - ft:tokens() returns full-text tokens stored in the index - ft:tokenize() tokenizes a given input string - util:path() returns the path to the executed query file - util:time() prints the time needed to evaluate a given expression - util:mem() prints allocated memory for evaluating an expression - expanded QNames, computed namespace constructors COMMAND-LINE - multiple query files and -c/-i/-q flags can now be specified INDEXING - ID/Pre mapping, incremental indexing of value index structures - document index fix: correct replacement of existing documents - document index: faster updates OPTIONS - ADDRAW: add raw files if not filtered by CREATEFILTER - MAXLEN: max. length of strings to be added to the index structures - MAXCATS: max. number of distinct values stored in statistics - UPDINDEX: turn on incremental index updates for value indexes - improved BINDINGS option CLIENT/SERVER - improved log output if query iterator is used - new ActionScript API (thanks to Manfred Knobloch!) SERIALIZATION - "newline" parameter specifies the type of newline to be used COMMANDS - KILL command accepts IP:port combination to specify target TRANSLATIONS - Bahasa Indonesian: thanks to Andria Arisal! - Mongolian: thanks to Tuguldur Jamiyansharav! VERSION 7.0.2 (November 11, 2011) -------------------------------------- FULL TEXT - Stemming support for Japanese text corpora (thanks to Toshio HIRAI!) STARTUP - Updated start scripts (thanks to Ralf Jung!) - System property "org.basex.path" added to specify project's home directory (thanks to malamut2!) XQUERY - Numerous minor XQuery 3.0 enhancements and fixes - Fix for db:optimize() calls (thanks to Martin Hillert!) STORAGE - Fix to retain newly introduced namespaces (thanks to Laurent Chevalier!) USERS - Default privileges for new users set to "none" (thanks to Pascal Heus!) REST - query base URI for evaluated queries is now $HTTPPATH (thanks to Florent Georges!) VERSION 7.0.1 (October 23, 2011) --------------------------------------- DISTRIBUTIONS - Windows installer was updated to support latest features - ZIP file was updated (initial config & directories added) - Short directory names are chosen if config file resides in app.dir. - Start scripts have been improved XQUERY - much faster execution of count() when applied to opened databases SERVER - Flag -c connects to an existing database server - Flag -s specifies a port for stopping the HTTP server (Jetty) - Flag -S starts the HTTP server as a service - running write operations will be completed before server is stopped API - Ruby, Python, PHP, Java: clients updated VERSION 7.0 (October 14, 2010) TEI EDITION ----------------------------- API - Native and tightly integrated REST implementation replaces JAXRX - WebDAV provides a file system like access to BaseX databases XQUERY - Parsing and serializing JSON documents - SQL module builds a JDBC bridge to access relational databases - EXPath Cryptographic Module for encryption and XML Signatures - Full text engine tokenizes Japanese texts (thx to Toshio Hirai!) - db:retrieve() and db:store() handle raw data - util:uuid() to create random universally unique identifier - db:content-type() retrieves the content type of a resource - db:exists() checks if the specified database or resource exists - db:is-raw(), db:is-xml() check existence and type of a resource - db:list(), db:open() uses two separate arguments to specify database and resource path - further modifications: db:add(), SERVER - BaseX HTTP Server activates the REST and WebDAV services - ITER command returns all results in one go and the client handles the iterative execution COMMANDS - FLUSH command to write all database buffers to disk - STORE command to store raw data in a database - RETRIEVE command to get raw data from the database - modified ADD command OPTIONS - SERVERHOST: to specify a server - KEEPALIVE: optional timeout to close inactive client sessions - AUTOFLUSH database buffers - QUERYPATH: path to executed query VERSION 6.7.1 (July 28, 2011) BALISAGE EDITION ------------------------- XQuery: - New database functions for adding, deleting, renaming and replacing documents, and optimizing databases: http://docs.basex.org/wiki/Database_Functions - XSLT transformations via Java or Saxon: http://docs.basex.org/wiki/XSLT_Functions - All XQuery 3.0 functions are now supported: http://docs.basex.org/wiki/XQuery_3.0 - Tail-call optimizations to speed up recursive functions Storage: - Use ADDARCHIVES to parse files within archives - Use SKIPCORRUPT to skip non-well-formed files when creating a database: http://docs.basex.org/wiki/Options - Max. level depth limit (256) removed - The document index is now incrementally updated GUI: - "Manage Database" dialog now supports operations on multiple databases and the command-line glob syntax: http://docs.basex.org/wiki/Commands#Glob_Syntax - Drag and drop operations introduced for opening new files and copying file paths Client/Server: - Delay clients that repeatedly fail to login - All remaining plain-text password operations now use MD5 to send and log passwords VERSION 6.7 (June 30, 2011) -------------------------------------------- Main Features: [ADD] Native support for the EXPath Packaging system: http://docs.basex.org/wiki/Packaging [ADD] Client/server event notification: http://docs.basex.org/wiki/Events [ADD] Persistent document index added to speed up access to large collections XQuery: [ADD] New database and full-text functions: http://docs.basex.org/wiki/Database_Functions http://docs.basex.org/wiki/Full-Text_Functions [ADD] Event function added to fire events [MOD] Index optimizations, better cost estimations Commands: [ADD] Glob syntax introduced to database commands: http://docs.basex.org/wiki/Commands [ADD] New commands added: REPLACE, RENAME, REPO DELETE/INSTALL/LIST, CREATE/DROP EVENT [MOD] BACKUP optimized, renamed to CREATE BACKUP VERSION 6.6.2 (May 13, 2011) LINUXTAG RELEASE -------------------------- API: [ADD] JAX-RX API now supports basic user authentication: http://docs.basex.org/wiki/JAX-RX_API [ADD] The COPY creates identical database copies: http://docs.basex.org/wiki/Commands#COPY [ADD] The OPTIMIZE ALL command minimizes all database structures: http://docs.basex.org/wiki/Commands#OPTIMIZE XQuery: [ADD] map expressions and functions added: http://docs.basex.org/wiki/Map_Functions [MOD] File module aligned with latest EXPath specification: http://docs.basex.org/wiki/File_Functions [MOD] Speedup of full-text queries with keyword lists. Example: $x contains text { 'a', 'b', 'c', ...} [MOD] XQuery Update optimizations for replacing nodes; tree-aware updates. [MOD] XQuery optimizations to avoid materialization of sequences. GUI: [ADD] Multiple editor tabs added [ADD] Database management: copy databases Core: [ADD] Internal XML parser: HTML entities added [FIX] Glob syntax: support for multiple file suffixes VERSION 6.6.1 (March 30, 2011) XML PRAGUE RELEASE ---------------------- XQuery: [ADD] index rewritings added for .../text()[. = ..] syntax [ADD] optimizations of mixed axis path expressions, e.g.: //x/name() [MOD] index rewritings on collections fixed and generalized [MOD] faster evaluation of filters with pos. predicates, e.g.: $x[5] [FIX] fixed relocation of let clauses in GFLWOR expressions [FIX] trace function returned wrong original results [FIX] variables in catch clauses were discarded [FIX] HOF optimizations and fixes GUI: [FIX] language option (for Japanese, German, etc. interface) fixed VERSION 6.6 (March 23, 2011) ------------------------------------------- [ADD] XQuery 3.0: Full support of Higher Order Functions (dynamic function invocation, inlined functions, etc.) [ADD] XQuery: Full support of the EXPath ZIP and HTTP modules [MOD] XQuery Update: numerous speedups, memory consumption reduced [ADD] Commands: COPY command added to clone existing databases [ADD] CSV/Test importers revised, ENCODING option added to CSV/Text parsers [ADD] XQuery utility functions added: util:format(), util:crc32(), util:md5(), util:sha1(), etc. [ADD] XQuery 3.0: context item and decimal-format declarations [FIX] Storage and update features revised, bugs fixed VERSION 6.5 (November 17, 2010) ---------------------------------------- [ADD] Commands: LIST extended by optional database [path] argument [ADD] JAX-RX: full database path is now used to list documents within a database. Use "query" parameter to show document contents [ADD] JAX-RX: bind external variables to query and run parameter [ADD] Windows Installer: creates startmenu entries, sets file associations and path environment entries [ADD] GUI/Create: support different input formats: XML, HTML, Text, .. [MOD] Commands: Allow hierarchical paths in OPEN command [MOD] APIs: UTF-8 encoding added to Java binding [FIX] Storage: text decompression synchronized [FIX] XQuery: context choice in filter predicates [MOD] JavaDoc: package.html files added and updated basex-8.5.1/Dockerfile000066400000000000000000000013661274071665100146400ustar00rootroot00000000000000FROM maven:3-jdk-7 MAINTAINER BaseX Team # Compile BaseX, install COPY . /usr/src/basex/ RUN cd /usr/src/basex && \ mvn clean install -DskipTests && \ ln -s /usr/src/basex/basex-*/etc/* /usr/local/bin && \ cp -r /usr/src/basex/basex-api/src/main/webapp/WEB-INF /srv # Run as non-privileged user with fixed UID RUN adduser --system --home /srv --disabled-password --disabled-login --uid 1984 basex && \ mkdir /srv/BaseXData /srv/BaseXRepo /srv/BaseXWeb && \ chown -R basex /srv USER basex # 1984/tcp: API # 8984/tcp: HTTP # 8985/tcp: HTTP stop EXPOSE 1984 8984 8985 VOLUME ["/srv/BaseXData"] WORKDIR /srv # Run BaseX HTTP server by default, logging to STDOUT CMD ["/usr/local/bin/basexhttp", "-d"] basex-8.5.1/LICENSE000066400000000000000000000027641274071665100136560ustar00rootroot00000000000000Copyright (c) 2005-15 BaseX Team All rights reserved. The BSD 3-Clause License Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. basex-8.5.1/README.md000066400000000000000000000100141274071665100141130ustar00rootroot00000000000000BaseX =============================================================================== Welcome to the source code of BaseX. To get the project running as smooth as possible, please consider the following notes: Compiling BaseX --------------- JDK 1.7 and JUnit is currently required to compile the complete sources of the main project. If you are using another environment than Eclipse or don't have JUnit installed, just delete the `test` packages inside the project and rebuild the project. Please take a look at the [Maven documentation] for information on how to use Maven. You can launch the following classes, which are all placed in the basex-core directory and the `org.basex` main package: BaseX : console mode BaseXServer : server instance, waiting for requests BaseXClient : console mode, interacting with the server BaseXGUI : graphical user interface Moreover, try `-h` to list the available command line options. For example, you can use BaseX to process XQuery expressions without entering the console. [Maven documentation]: https://docs.basex.org/wiki/Maven Docker Image ------------ The BaseX server is also available as automated build [`basex/basexhttp`] on the Docker Hub, providing both release and nightly builds. All images are automatically rebuild if Docker provides updated base images. To start a BaseX container based on the latest development release publishing the BaseX server and HTTP ports `1984` and `8984` and bind-mounting your user's `BaseXData` directory, run docker run -ti \ --name basexhttp \ --publish 1984:1984 \ --publish 8984:8984 \ --volume ~/BaseXData:/srv/BaseXData \ basex/basexhttp:latest By passing any other BaseX executable, you can also for example run a BaseX client connecting to the linked BaseX server for management operations on the BaseX command line: docker run -ti \ --link basexhttp:basexhttp \ basex/basexhttp:latest basexclient -nbasexhttp If you prefer the DBA web interface, this can also be linked against your server container: docker run -d \ --name basex-dba \ --publix 18984:8984 \ --link basexhttp:basexhttp \ basex/dba If you want to add your own application, create an image `FROM basex/basexhttp:[tag]` with `[tag]` being the BaseX version you're developing against. Usually, you will add your application code to `/srv/BaseXWeb` and modules to `/srv/BaseXModule`. `BaseXData` is persisted as a volume, which means it cannot be preinitialized in the application image. For further information on using the Docker image, refer to the [BaseX Docker documentation]. [`basex/basexhttp`]: https://hub.docker.com/r/basex/basexhttp/ [BaseX Docker documentation]: http://docs.basex.org/wiki/Docker Using Eclipse ------------- BaseX is being developed with the Eclipse environment. Some style guidelines are integrated in the sources of BaseX; they are being embedded as soon as you open the project. ### Running BaseX The following steps can be performed to start BaseX with Eclipse: - Press `Run` -> `Run...` - Create a new `Java Application` launch configuration - Select `basex` as Project - Choose a `Main class` (e.g., org.basex.BaseXGUI) - Launch the project via `Run` ### Adding Checkstyle Some additional Checkstyle guidelines are defined in the project: - Open Eclipse - Press `Help` -> `Install new Software...` - Press `Search for new features to install` - Enter the URL: `http://eclipse-cs.sourceforge.net/update` - Follow the installation procedure and restart Eclipse Using Git --------- The code base of BaseX can be accessed via [GitHub]. [GitHub]: https://www.github.com Feedback -------- Any kind of feedback is welcome; please check out the [documentation]. Tell us if you run into any troubles installing BaseX: You are as well invited to contribute to our [bug tracker]. All the best, BaseX Team, 2016 [documentation]: (https://docs.basex.org) [bug tracker]: (https://github.com/BaseXdb/BaseX/issues) basex-8.5.1/basex-api/000077500000000000000000000000001274071665100145115ustar00rootroot00000000000000basex-8.5.1/basex-api/.checkstyle000066400000000000000000000012361274071665100166520ustar00rootroot00000000000000 basex-8.5.1/basex-api/.classpath000066400000000000000000000027021274071665100164750ustar00rootroot00000000000000 basex-8.5.1/basex-api/.gitignore000066400000000000000000000000221274071665100164730ustar00rootroot00000000000000/doc /lib /target basex-8.5.1/basex-api/.project000066400000000000000000000015721274071665100161650ustar00rootroot00000000000000 basex-api org.eclipse.jdt.core.javabuilder net.sf.eclipsecs.core.CheckstyleBuilder org.maven.ide.eclipse.maven2Builder org.eclipse.m2e.core.maven2Builder org.eclipse.m2e.core.maven2Nature org.maven.ide.eclipse.maven2Nature org.eclipse.jdt.core.javanature net.sf.eclipsecs.core.CheckstyleNature basex-8.5.1/basex-api/.settings/000077500000000000000000000000001274071665100164275ustar00rootroot00000000000000basex-8.5.1/basex-api/.settings/checkstyle.xml000066400000000000000000000103521274071665100213100ustar00rootroot00000000000000 basex-8.5.1/basex-api/.settings/org.eclipse.core.resources.prefs000066400000000000000000000002711274071665100246420ustar00rootroot00000000000000eclipse.preferences.version=1 encoding//src/main/java=UTF-8 encoding//src/main/resources=UTF-8 encoding//src/test/java=UTF-8 encoding//src/test/resources=UTF-8 encoding/=UTF-8 basex-8.5.1/basex-api/.settings/org.eclipse.core.runtime.prefs000066400000000000000000000001161274071665100243110ustar00rootroot00000000000000#Mon Feb 14 18:03:25 CET 2011 eclipse.preferences.version=1 line.separator=\n basex-8.5.1/basex-api/.settings/org.eclipse.jdt.core.prefs000066400000000000000000000772141274071665100234240ustar00rootroot00000000000000eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=warning org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable org.eclipse.jdt.core.compiler.annotation.nullable.secondary= org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve org.eclipse.jdt.core.compiler.compliance=1.7 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate org.eclipse.jdt.core.compiler.doc.comment.support=enabled org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.autoboxing=ignore org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning org.eclipse.jdt.core.compiler.problem.deadCode=warning org.eclipse.jdt.core.compiler.problem.deprecation=warning org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=enabled org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled org.eclipse.jdt.core.compiler.problem.fieldHiding=warning org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore org.eclipse.jdt.core.compiler.problem.missingJavadocComments=warning org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=private org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=all_standard_tags org.eclipse.jdt.core.compiler.problem.missingJavadocTags=warning org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=enabled org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=enabled org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=warning org.eclipse.jdt.core.compiler.problem.nullReference=warning org.eclipse.jdt.core.compiler.problem.nullSpecViolation=warning org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning org.eclipse.jdt.core.compiler.problem.parameterAssignment=warning org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning org.eclipse.jdt.core.compiler.problem.unclosedCloseable=ignore org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=disabled org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=disabled org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore org.eclipse.jdt.core.compiler.problem.unusedImport=warning org.eclipse.jdt.core.compiler.problem.unusedLabel=warning org.eclipse.jdt.core.compiler.problem.unusedLocal=warning org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore org.eclipse.jdt.core.compiler.problem.unusedParameter=warning org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=disabled org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=warning org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning org.eclipse.jdt.core.compiler.source=1.7 org.eclipse.jdt.core.compiler.taskCaseSensitive=disabled org.eclipse.jdt.core.compiler.taskPriorities=NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL,NORMAL org.eclipse.jdt.core.compiler.taskTags=TODO,[CG],[AH],[LK],[CG],[MS],[DP],[LW],[JE],[DK],[MSG] org.eclipse.jdt.core.formatter.align_type_members_on_columns=false org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 org.eclipse.jdt.core.formatter.alignment_for_assignment=0 org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=90 org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16 org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=0 org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 org.eclipse.jdt.core.formatter.blank_lines_after_package=1 org.eclipse.jdt.core.formatter.blank_lines_before_field=0 org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 org.eclipse.jdt.core.formatter.blank_lines_before_method=1 org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 org.eclipse.jdt.core.formatter.blank_lines_before_package=0 org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line org.eclipse.jdt.core.formatter.comment.clear_blank_lines=true org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=true org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false org.eclipse.jdt.core.formatter.comment.format_block_comments=true org.eclipse.jdt.core.formatter.comment.format_comments=false org.eclipse.jdt.core.formatter.comment.format_header=true org.eclipse.jdt.core.formatter.comment.format_html=true org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true org.eclipse.jdt.core.formatter.comment.format_line_comments=true org.eclipse.jdt.core.formatter.comment.format_source_code=true org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true org.eclipse.jdt.core.formatter.comment.indent_root_tags=true org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=do not insert org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert org.eclipse.jdt.core.formatter.comment.line_length=100 org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false org.eclipse.jdt.core.formatter.compact_else_if=true org.eclipse.jdt.core.formatter.continuation_indentation=2 org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true org.eclipse.jdt.core.formatter.indent_empty_lines=false org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true org.eclipse.jdt.core.formatter.indentation.size=2 org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=do not insert org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=do not insert org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=do not insert org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=do not insert org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=do not insert org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=do not insert org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=do not insert org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=do not insert org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=do not insert org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=do not insert org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=do not insert org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=do not insert org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=do not insert org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=insert org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert org.eclipse.jdt.core.formatter.join_lines_in_comments=true org.eclipse.jdt.core.formatter.join_wrapped_lines=true org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=true org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=true org.eclipse.jdt.core.formatter.lineSplit=100 org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true org.eclipse.jdt.core.formatter.tabulation.char=space org.eclipse.jdt.core.formatter.tabulation.size=2 org.eclipse.jdt.core.formatter.use_on_off_tags=false org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true basex-8.5.1/basex-api/.settings/org.eclipse.jdt.ui.prefs000066400000000000000000000126621274071665100231050ustar00rootroot00000000000000cleanup.add_default_serial_version_id=true cleanup.add_generated_serial_version_id=false cleanup.add_missing_annotations=true cleanup.add_missing_deprecated_annotations=true cleanup.add_missing_methods=false cleanup.add_missing_nls_tags=false cleanup.add_missing_override_annotations=true cleanup.add_missing_override_annotations_interface_methods=true cleanup.add_serial_version_id=false cleanup.always_use_blocks=true cleanup.always_use_parentheses_in_expressions=false cleanup.always_use_this_for_non_static_field_access=false cleanup.always_use_this_for_non_static_method_access=false cleanup.convert_functional_interfaces=false cleanup.convert_to_enhanced_for_loop=true cleanup.correct_indentation=false cleanup.format_source_code=false cleanup.format_source_code_changes_only=false cleanup.insert_inferred_type_arguments=false cleanup.make_local_variable_final=true cleanup.make_parameters_final=true cleanup.make_private_fields_final=true cleanup.make_type_abstract_if_missing_method=false cleanup.make_variable_declarations_final=true cleanup.never_use_blocks=false cleanup.never_use_parentheses_in_expressions=true cleanup.organize_imports=false cleanup.qualify_static_field_accesses_with_declaring_class=false cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true cleanup.qualify_static_member_accesses_with_declaring_class=true cleanup.qualify_static_method_accesses_with_declaring_class=false cleanup.remove_private_constructors=true cleanup.remove_redundant_type_arguments=true cleanup.remove_trailing_whitespaces=false cleanup.remove_trailing_whitespaces_all=true cleanup.remove_trailing_whitespaces_ignore_empty=false cleanup.remove_unnecessary_casts=true cleanup.remove_unnecessary_nls_tags=true cleanup.remove_unused_imports=true cleanup.remove_unused_local_variables=false cleanup.remove_unused_private_fields=true cleanup.remove_unused_private_members=false cleanup.remove_unused_private_methods=true cleanup.remove_unused_private_types=true cleanup.sort_members=false cleanup.sort_members_all=false cleanup.use_anonymous_class_creation=false cleanup.use_blocks=false cleanup.use_blocks_only_for_return_and_throw=false cleanup.use_lambda=true cleanup.use_parentheses_in_expressions=false cleanup.use_this_for_non_static_field_access=false cleanup.use_this_for_non_static_field_access_only_if_necessary=true cleanup.use_this_for_non_static_method_access=false cleanup.use_this_for_non_static_method_access_only_if_necessary=true cleanup.use_type_arguments=false cleanup_profile=_BaseX cleanup_settings_version=2 eclipse.preferences.version=1 editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true formatter_profile=_BaseX Formatter formatter_settings_version=12 org.eclipse.jdt.ui.ignorelowercasenames=true org.eclipse.jdt.ui.importorder=java;javax;org;com; org.eclipse.jdt.ui.ondemandthreshold=1 org.eclipse.jdt.ui.staticondemandthreshold=1 sp_cleanup.add_default_serial_version_id=true sp_cleanup.add_generated_serial_version_id=false sp_cleanup.add_missing_annotations=false sp_cleanup.add_missing_deprecated_annotations=true sp_cleanup.add_missing_methods=false sp_cleanup.add_missing_nls_tags=false sp_cleanup.add_missing_override_annotations=true sp_cleanup.add_missing_override_annotations_interface_methods=true sp_cleanup.add_serial_version_id=false sp_cleanup.always_use_blocks=true sp_cleanup.always_use_parentheses_in_expressions=false sp_cleanup.always_use_this_for_non_static_field_access=false sp_cleanup.always_use_this_for_non_static_method_access=false sp_cleanup.convert_to_enhanced_for_loop=false sp_cleanup.correct_indentation=false sp_cleanup.format_source_code=false sp_cleanup.format_source_code_changes_only=false sp_cleanup.make_local_variable_final=false sp_cleanup.make_parameters_final=false sp_cleanup.make_private_fields_final=true sp_cleanup.make_type_abstract_if_missing_method=false sp_cleanup.make_variable_declarations_final=false sp_cleanup.never_use_blocks=false sp_cleanup.never_use_parentheses_in_expressions=true sp_cleanup.on_save_use_additional_actions=true sp_cleanup.organize_imports=false sp_cleanup.qualify_static_field_accesses_with_declaring_class=false sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_with_declaring_class=false sp_cleanup.qualify_static_method_accesses_with_declaring_class=false sp_cleanup.remove_private_constructors=true sp_cleanup.remove_trailing_whitespaces=true sp_cleanup.remove_trailing_whitespaces_all=true sp_cleanup.remove_trailing_whitespaces_ignore_empty=false sp_cleanup.remove_unnecessary_casts=false sp_cleanup.remove_unnecessary_nls_tags=false sp_cleanup.remove_unused_imports=false sp_cleanup.remove_unused_local_variables=false sp_cleanup.remove_unused_private_fields=true sp_cleanup.remove_unused_private_members=false sp_cleanup.remove_unused_private_methods=true sp_cleanup.remove_unused_private_types=true sp_cleanup.sort_members=false sp_cleanup.sort_members_all=false sp_cleanup.use_blocks=false sp_cleanup.use_blocks_only_for_return_and_throw=false sp_cleanup.use_parentheses_in_expressions=false sp_cleanup.use_this_for_non_static_field_access=false sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true sp_cleanup.use_this_for_non_static_method_access=false sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true basex-8.5.1/basex-api/.settings/org.eclipse.ltk.core.refactoring.prefs000066400000000000000000000002061274071665100257220ustar00rootroot00000000000000#Fri Mar 19 00:48:33 CET 2010 eclipse.preferences.version=1 org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false basex-8.5.1/basex-api/.settings/org.eclipse.m2e.core.prefs000066400000000000000000000001651274071665100233150ustar00rootroot00000000000000#Wed Aug 03 00:22:23 CEST 2011 activeProfiles= eclipse.preferences.version=1 resolveWorkspaceProjects=true version=1 basex-8.5.1/basex-api/.settings/org.maven.ide.eclipse.prefs000066400000000000000000000004061274071665100235470ustar00rootroot00000000000000#Fri Jan 29 18:36:39 CET 2010 activeProfiles= eclipse.preferences.version=1 fullBuildGoals=process-test-resources includeModules=false resolveWorkspaceProjects=true resourceFilterGoals=process-resources resources\:testResources skipCompilerPlugin=true version=1 basex-8.5.1/basex-api/basex-api.iml000066400000000000000000000076611274071665100170770ustar00rootroot00000000000000 basex-8.5.1/basex-api/etc/000077500000000000000000000000001274071665100152645ustar00rootroot00000000000000basex-8.5.1/basex-api/etc/basexhttp000077500000000000000000000011441274071665100172140ustar00rootroot00000000000000#!/usr/bin/env bash # Path to this script FILE="${BASH_SOURCE[0]}" while [ -h "$FILE" ] ; do SRC="$(readlink "$FILE")" FILE="$( cd -P "$(dirname "$FILE")" && \ cd -P "$(dirname "$SRC")" && pwd )/$(basename "$SRC")" done BX="$( cd -P "$(dirname "$FILE")/.." && pwd )" BXCORE="$( cd -P "$BX/../basex-core" && pwd )" # API, core, and library classes CP="$BX/target/classes$(printf ":%s" "$BX/lib/"*.jar "$BXCORE/lib/"*.jar):$CLASSPATH" # Options for virtual machine (can be extended by global options) BASEX_JVM="-Xmx512m $BASEX_JVM" # Run code java -cp "$CP" $BASEX_JVM org.basex.BaseXHTTP "$@" basex-8.5.1/basex-api/etc/basexhttp.bat000066400000000000000000000006771274071665100177700ustar00rootroot00000000000000@echo off setLocal EnableDelayedExpansion REM Path to this script set PWD=%~dp0 REM Core and library classes set CP=%PWD%/../target/classes set LIB=%PWD%/../lib for /R "%LIB%" %%a in (*.jar) do set CP=!CP!;%%a set LIB=%PWD%/../../basex-core/lib for /R "%LIB%" %%a in (*.jar) do set CP=!CP!;%%a REM Options for virtual machine set BASEX_JVM=-Xmx512m %BASEX_JVM% REM Run code java -cp "%CP%" %BASEX_JVM% org.basex.BaseXHTTP %* basex-8.5.1/basex-api/etc/basexhttpstop000077500000000000000000000007731274071665100201310ustar00rootroot00000000000000#!/usr/bin/env bash # Path to this script FILE="${BASH_SOURCE[0]}" while [ -h "$FILE" ] ; do SRC="$(readlink "$FILE")" FILE="$( cd -P "$(dirname "$FILE")" && \ cd -P "$(dirname "$SRC")" && pwd )/$(basename "$SRC")" done BX="$( cd -P "$(dirname "$FILE")/.." && pwd )" BXCORE="$( cd -P "$BX/../basex-core" && pwd )" # API, core, and library classes CP="$BX/target/classes$(printf ":%s" "$BX/lib/"*.jar "$BXCORE/lib/"*.jar)" # Run code java -cp "$CP" $BASEX_JVM org.basex.BaseXHTTP "$@" stop basex-8.5.1/basex-api/etc/basexhttpstop.bat000066400000000000000000000005751274071665100206730ustar00rootroot00000000000000@echo off setLocal EnableDelayedExpansion REM Path to this script set PWD=%~dp0 REM Core and library classes set CP=%PWD%/../target/classes set LIB=%PWD%/../lib for /R "%LIB%" %%a in (*.jar) do set CP=!CP!;%%a set LIB=%PWD%/../../basex-core/lib for /R "%LIB%" %%a in (*.jar) do set CP=!CP!;%%a REM Run code java -cp "%CP%" %BASEX_JVM% org.basex.BaseXHTTP %* stop basex-8.5.1/basex-api/license.txt000066400000000000000000000031771274071665100167040ustar00rootroot00000000000000====================================================== BASEX LICENSE === Copyright (c) 2005-12 BaseX Team All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ======================================================================== basex-8.5.1/basex-api/pom.xml000066400000000000000000000072331274071665100160330ustar00rootroot00000000000000 4.0.0 basex-api org.basex basex-parent 8.5.1 .. BaseX API org.basex basex ${project.version} net.xqj basex-xqj org.xmldb xmldb-api org.eclipse.jetty jetty-server org.eclipse.jetty jetty-servlet org.eclipse.jetty jetty-webapp com.ettrema milton-api commons-fileupload commons-fileupload org.slf4j slf4j-simple junit junit com.vividsolutions jts ${project.artifactId}-${project.version} org.apache.maven.plugins maven-compiler-plugin false org.apache.maven.plugins maven-jar-plugin org.apache.maven.plugins maven-surefire-plugin org.apache.maven.plugins maven-source-plugin org.apache.maven.plugins maven-deploy-plugin org.apache.maven.plugins maven-dependency-plugin compile xstream org.mortbay.jetty jetty-maven-plugin 8.1.16.v20140903 ${basedir}/src/main/webapp/WEB-INF/jetty.xml keyToStopJetty 8985 basex-8.5.1/basex-api/src/000077500000000000000000000000001274071665100153005ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/000077500000000000000000000000001274071665100162245ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/as/000077500000000000000000000000001274071665100166275ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/as/Query.as000066400000000000000000000061611274071665100202650ustar00rootroot00000000000000/** * Language Binding for BaseX. * Works with BaseX 7.0 and later * * Documentation: http://docs.basex.org/wiki/Clients * * (C) Manfred Knobloch, BSD License * Institut fr Wissensmedien IWM - Knowledge Media Research Center (KMRC) **/ package basexapi { import flash.utils.ByteArray; public class Query { public const NO_ID:int = 0; public const CLOSED:int = -1; public const PENDING_ID_REQUEST:int = 10; public const ID_RECEIVED:int = 20; public const PENDING_BIND_REQUEST:int = 30; public const BIND_RESPONSE:int = 40; public const EXECUTE_REQUEST:int = 50; protected var _session:Session; protected var _query:String; protected var _name:String; protected var _value:String; [Bindable] public var state:int=0; public var id:String = ""; protected var socketBuffer:ByteArray; public function Query(session:Session, query:String) { _session = session; _query = query; _name = ""; _value = ""; state = PENDING_ID_REQUEST; socketBuffer = new ByteArray(); socketBuffer.writeByte(0); socketBuffer.writeUTFBytes(_query); socketBuffer.writeByte(0); _session.sendBuffer(socketBuffer); } /** * detect bind indicator 'declare' in query * string **/ public function needBind():Boolean{ var _lowerq:String = _query.toLowerCase(); if ( _lowerq.indexOf('declare') > -1){ return true; } return false; } public function setBindParams(name:String, value:String):void{ _name = name; _value = value; } /** * send 2 \0 bytes at end, otherwise bind will fail for * the type parameter seems to be neccessary * ('\x03', '0\x00$name\x00number\x00\x00') **/ public function bind():void{ if (state > PENDING_ID_REQUEST){ state = PENDING_BIND_REQUEST; socketBuffer = new ByteArray(); socketBuffer.writeByte(3); socketBuffer.writeUTFBytes(id); socketBuffer.writeByte(0); socketBuffer.writeUTFBytes(_name); socketBuffer.writeByte(0); socketBuffer.writeUTFBytes(_value); socketBuffer.writeByte(0); // termination for value socketBuffer.writeByte(0); // termination for unused type socketBuffer.position = 0; _session.sendBuffer(socketBuffer); } } public function sendQuery(code:int, arg:String):void{ socketBuffer = new ByteArray(); socketBuffer.writeByte(code); socketBuffer.writeUTFBytes(this.id); socketBuffer.writeByte(0); _session.sendBuffer(socketBuffer); } public function execute():void{ sendQuery(0x05, id); } public function info():void{ exc(6, id); } public function options():void{ exc(7, id); } public function close():void{ exc(2, id); } public function exc(cmd:int, arg:String):void{ sendQuery(cmd, arg); } } }basex-8.5.1/basex-api/src/main/as/Session.as000066400000000000000000000140531274071665100206020ustar00rootroot00000000000000/** * Language Binding for BaseX. * Works with BaseX 7.x (but not with BaseX 8.0 and later) * * Documentation: http://docs.basex.org/wiki/Clients * * (C) Manfred Knobloch, BSD License * Institut fr Wissensmedien IWM - Knowledge Media Research Center (KMRC) **/ package basexapi { import com.adobe.crypto.MD5; import flash.errors.*; import flash.events.*; import flash.net.Socket; import flash.system.Security; import flash.utils.ByteArray; public class Session extends Object { public static const DISCONNECTED:int = 0; public static const CONNECTING:int = 1; public static const CONNECTED:int = 2; public static const AUTHENTICATING:int = 3; public static const AUTHENTICATED:int = 4; public static const OPERATING:int = 5; public static const RESULT:int = 6; [Bindable] public var response:String= ""; [Bindable] protected var _socket:Socket; [Bindable] public var state:int = DISCONNECTED; protected var _result:ByteArray; protected var user:String; protected var password:String; protected var _query:Query; public var socketBuffer:ByteArray; public function Session(h:String=null, p:uint=0, u:String=null, pass:String=null) { user = u; password = pass; _socket = new Socket(); _socket.addEventListener(Event.CLOSE, closeHandler); _socket.addEventListener(Event.CONNECT, connectHandler); _socket.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler); _socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); _socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler); state = CONNECTING; _socket.connect(h,p); socketBuffer = new ByteArray(); } public function execute(arg:String):void{ socketBuffer = new ByteArray(); socketBuffer.writeUTFBytes(arg); socketBuffer.writeByte(0); sendBuffer(socketBuffer); } public function close():void{ _socket.close(); } public function query(q:String):Query{ _query = new Query(this, q); return _query; } public function sendCommand(code:int, arg:String, input:String):void{ socketBuffer = new ByteArray(); socketBuffer.writeByte(code); socketBuffer.writeUTFBytes(arg); socketBuffer.writeByte(0); socketBuffer.writeUTFBytes(input); socketBuffer.writeByte(0); sendBuffer(socketBuffer); } public function sendBuffer(b:ByteArray):void{ response = ""; _socket.writeBytes(b); _socket.flush(); } protected function sendInput(code:int, arg:String):void{ var breakat:int = arg.indexOf(" "); if (breakat == -1){ return; } var arg1:String = arg.substr(0,breakat); var arg2:String = arg.substring(breakat); sendCommand(code,arg1,arg2); } public function create(input:String):void{ sendInput(8,input); } public function add(input:String):void{ sendInput(9, input); } public function replace(input:String):void{ sendInput(12, input); } public function store(input:String):void{ sendInput(13,input); } public function authenticate():void{ var md5pw:String = MD5.hash(password); var md5resp:String = MD5.hash(md5pw + response); socketBuffer = new ByteArray(); socketBuffer.writeUTFBytes(user); socketBuffer.writeByte(0); socketBuffer.writeUTFBytes(md5resp); socketBuffer.writeByte(0); response = ""; _socket.writeBytes(socketBuffer); _socket.flush(); state = AUTHENTICATING; } public function readIntoByteBuffer(buf:ByteArray):void{ _socket.readBytes(buf,0,_socket.bytesAvailable); } public function readstrings():String{ _result = new ByteArray(); var last:int = 0; var current:int = 0; var foundStrings:Vector. = new Vector.; _socket.readBytes(_result,0,_socket.bytesAvailable); //read to letter 0 or end of bytes while (_result.bytesAvailable > 0 ) { if(_result.readByte() == 0){ current = _result.position; if (current > last){ _result.position = last; foundStrings.push(_result.readUTFBytes(current-last)); last = current; } } } return foundStrings.join(""); } // wrapper for public public function readUTFBytes():String{ return _socket.readUTFBytes(_socket.bytesAvailable); } private function socketDataHandler(event:ProgressEvent):void { if (state == AUTHENTICATING){ if( _socket.readByte() == 0){ state = AUTHENTICATED; return; } } response = readstrings(); if(_query && (_query.state == _query.PENDING_ID_REQUEST)){ _query.id = response; _query.state = _query.ID_RECEIVED; if (!_query.needBind()){ _query.execute(); return; } _query.bind(); return; } if(_query && (_query.state == _query.PENDING_BIND_REQUEST)){ _query.state = _query.BIND_RESPONSE _query.execute(); return; } if (state < AUTHENTICATING){ authenticate(); state = AUTHENTICATING } } // some string representations public function get stateinfo():String{ var info:String = ""; switch (state) { case DISCONNECTED: info = "DISCONNECTED"; break; case CONNECTING: info = "CONNECTING"; break; case CONNECTED: info = "CONNECTED"; break; case AUTHENTICATING: info = "AUTHENTICATING"; break; case AUTHENTICATED: info = "AUTHENTICATED"; break; case OPERATING: info = "OPERATING"; break; } return info; } // handler functions private function closeHandler(event:Event):void { trace("closeHandler: " + event); } private function connectHandler(event:Event):void { state = CONNECTED; } private function ioErrorHandler(event:IOErrorEvent):void { trace("ioErrorHandler: " + event); } private function securityErrorHandler(event:SecurityErrorEvent):void { trace("securityErrorHandler: " + event); } } }basex-8.5.1/basex-api/src/main/c#/000077500000000000000000000000001274071665100165115ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/c#/AddExample.cs000066400000000000000000000027511274071665100210510ustar00rootroot00000000000000/* * This example shows how new documents can be added. * * Documentation: http://docs.basex.org/wiki/Clients * * (C) BaseX Team 2005-12, BSD License */ using System; using System.Diagnostics; using System.IO; namespace BaseXClient { public class AddExample { public static void Main(string[] args) { try { // create session Session session = new Session("localhost", 1984, "admin", "admin"); // create empty database session.Execute("create db database"); Console.WriteLine(session.Info); // define InputStream MemoryStream ms = new MemoryStream( System.Text.Encoding.UTF8.GetBytes("Hello World!")); // add document session.Add("world/world.xml", ms); Console.WriteLine(session.Info); // define InputStream ms = new MemoryStream( System.Text.Encoding.UTF8.GetBytes("Hello Universe!")); // add document session.Add("Universe.xml", ms); Console.WriteLine(session.Info); // run query on database Console.WriteLine(session.Execute("xquery /")); // drop database session.Execute("drop db database"); // close session session.Close(); } catch (IOException e) { // print exception Console.WriteLine(e.Message); } } } } basex-8.5.1/basex-api/src/main/c#/BaseXClient.cs000066400000000000000000000132521274071665100212040ustar00rootroot00000000000000/* * Language Binding for BaseX. * Works with BaseX 7.0 and later * * Documentation: http://docs.basex.org/wiki/Clients * * (C) BaseX Team 2005-12, BSD License */ using System; using System.Net.Sockets; using System.Security.Cryptography; using System.Text; using System.IO; using System.Threading; using System.Collections; using System.Collections.Generic; namespace BaseXClient { public class Session { private byte[] cache = new byte[4096]; public NetworkStream stream; private TcpClient socket; private string info = ""; private int bpos; private int bsize; public Session(string host, int port, string username, string pw) { socket = new TcpClient(host, port); stream = socket.GetStream(); string[] response = Receive().Split(':'); string nonce; string code; if (response.Length > 1) { code = username + ":" + response[0] + ":" + pw; nonce = response[1]; } else { code = pw; nonce = response[0]; } Send(username); Send(MD5(MD5(code) + nonce)); if (stream.ReadByte() != 0) { throw new IOException("Access denied."); } } public void Execute(string com, Stream ms) { Send(com); Init(); Receive(ms); info = Receive(); if(!Ok()) { throw new IOException(info); } } public String Execute(string com) { MemoryStream ms = new MemoryStream(); Execute(com, ms); return System.Text.Encoding.UTF8.GetString(ms.ToArray()); } public Query Query(string q) { return new Query(this, q); } public void Create(string name, Stream s) { stream.WriteByte(8); Send(name); Send(s); } public void Add(string path, Stream s) { stream.WriteByte(9); Send(path); Send(s); } public void Replace(string path, Stream s) { stream.WriteByte(12); Send(path); Send(s); } public void Store(string path, Stream s) { stream.WriteByte(13); Send(path); Send(s); } public string Info { get { return info; } } public void Close() { Send("exit"); socket.Close(); } private void Init() { bpos = 0; bsize = 0; } public byte Read() { if (bpos == bsize) { bsize = stream.Read(cache, 0, 4096); bpos = 0; } return cache[bpos++]; } private void Receive(Stream ms) { while (true) { byte b = Read(); if (b == 0) break; // read next byte if 0xFF is received ms.WriteByte(b == 0xFF ? Read() : b); } } public string Receive() { MemoryStream ms = new MemoryStream(); Receive(ms); return System.Text.Encoding.UTF8.GetString(ms.ToArray()); } public void Send(string message) { byte[] msg = System.Text.Encoding.UTF8.GetBytes(message); stream.Write(msg, 0, msg.Length); stream.WriteByte(0); } private void Send(Stream s) { while (true) { int t = s.ReadByte(); if (t == -1) break; if (t == 0x00 || t == 0xFF) stream.WriteByte(Convert.ToByte(0xFF)); stream.WriteByte(Convert.ToByte(t)); } stream.WriteByte(0); info = Receive(); if(!Ok()) { throw new IOException(info); } } public bool Ok() { return Read() == 0; } private string MD5(string input) { MD5CryptoServiceProvider MD5 = new MD5CryptoServiceProvider(); byte[] hash = MD5.ComputeHash(Encoding.UTF8.GetBytes(input)); StringBuilder sb = new StringBuilder(); foreach (byte h in hash) { sb.Append(h.ToString("x2")); } return sb.ToString(); } } public class Query { private Session session; private string id; private ArrayList cache; private int pos; public Query(Session s, string query) { session = s; id = Exec(0, query); } public void Bind(string name, string value) { Bind(name, value, ""); } public void Bind(string name, string value, string type) { cache = null; Exec(3, id + '\0' + name + '\0' + value + '\0' + type); } public void Context(string value) { Context(value, ""); } public void Context(string value, string type) { cache = null; Exec(14, id + '\0' + value + '\0' + type); } public bool More() { if(cache == null) { session.stream.WriteByte(4); session.Send(id); cache = new ArrayList(); while (session.Read() > 0) { cache.Add(session.Receive()); } if(!session.Ok()) { throw new IOException(session.Receive()); } pos = 0; } if(pos < cache.Count) return true; cache = null; return false; } public string Next() { if(More()) { return cache[pos++] as string; } else { return null; } } public string Execute() { return Exec(5, id); } public string Info() { return Exec(6, id); } public string Options() { return Exec(7, id); } public void Close() { Exec(2, id); } private string Exec(byte cmd, string arg) { session.stream.WriteByte(cmd); session.Send(arg); string s = session.Receive(); bool ok = session.Ok(); if(!ok) { throw new IOException(session.Receive()); } return s; } } } basex-8.5.1/basex-api/src/main/c#/CreateExample.cs000066400000000000000000000021131274071665100215540ustar00rootroot00000000000000/* * This example shows how new databases can be created. * * Documentation: http://docs.basex.org/wiki/Clients * * (C) BaseX Team 2005-12, BSD License */ using System; using System.Diagnostics; using System.IO; namespace BaseXClient { public class CreateExample { public static void Main(string[] args) { try { // create session Session session = new Session("localhost", 1984, "admin", "admin"); // define InputStream MemoryStream ms = new MemoryStream( System.Text.Encoding.UTF8.GetBytes("Hello World!")); // create database session.Create("database", ms); Console.WriteLine(session.Info); // run query on database Console.WriteLine(session.Execute("xquery /")); // drop database session.Execute("drop db database"); // close session session.Close(); } catch (IOException e) { // print exception Console.WriteLine(e.Message); } } } } basex-8.5.1/basex-api/src/main/c#/EventExample.cs000066400000000000000000000024051274071665100214360ustar00rootroot00000000000000/* * This example shows how to use the event feature. * * Documentation: http://docs.basex.org/wiki/Clients * * (C) BaseX Team 2005-12, BSD License */ using System; using System.Diagnostics; using System.IO; namespace BaseXClient { public class EventExample { public static void Main(string[] args) { try { // create session Session session1 = new Session("localhost", 1984, "admin", "admin"); Session session2 = new Session("localhost", 1984, "admin", "admin"); session1.Execute("create event messenger"); session2.Watch("messenger", new Notification()); session2.Query("for $i in 1 to 1000000 where $i = 0 return $i").Execute(); session1.Query("db:event('messenger', 'Hello World!')").Execute(); session2.Unwatch("messenger"); session1.Execute("drop event messenger"); // close session session1.Close(); session2.Close(); } catch (IOException e) { // print exception Console.WriteLine(e.Message); } } } class Notification : EventNotification { public void Update (string data) { Console.WriteLine("Message received: " + data); } } }basex-8.5.1/basex-api/src/main/c#/Example.cs000066400000000000000000000022441274071665100204350ustar00rootroot00000000000000/* * This example shows how database commands can be executed. * * Documentation: http://docs.basex.org/wiki/Clients * * (C) BaseX Team 2005-12, BSD License */ using System; using System.Diagnostics; using System.IO; namespace BaseXClient { public class Example { public static void Main(string[] args) { try { // initialize timer Stopwatch watch = new Stopwatch(); watch.Start(); // create session Session session = new Session("localhost", 1984, "admin", "admin"); // version 1: perform command and print returned string Console.WriteLine(session.Execute("info")); // version 2 (faster): perform command and pass on result to output stream Stream stream = Console.OpenStandardOutput(); session.Execute("xquery 1 to 10", stream); // close session session.Close(); // print time needed Console.WriteLine("\n" + watch.ElapsedMilliseconds + " ms."); } catch (IOException e) { // print exception Console.WriteLine(e.Message); } } } } basex-8.5.1/basex-api/src/main/c#/QueryBindExample.cs000066400000000000000000000023651274071665100222640ustar00rootroot00000000000000/* * This example shows how external variables can be bound to XQuery expressions. * * Documentation: http://docs.basex.org/wiki/Clients * * (C) BaseX Team 2005-12, BSD License */ using System; using System.Diagnostics; using System.IO; namespace BaseXClient { public class QueryIteratorExample { public static void Main(string[] args) { try { // create session Session session = new Session("localhost", 1984, "admin", "admin"); try { // create query instance string input = "declare variable $name external;" + "for $i in 1 to 10 return element { $name } { $i }"; Query query = session.Query(input); // bind variable query.Bind("$name", "number"); // print result Console.WriteLine(query.Execute()); // close query query.Close(); } catch (IOException e) { // print exception Console.WriteLine(e.Message); } // close session session.Close(); } catch (IOException e) { // print exception Console.WriteLine(e.Message); } } } } basex-8.5.1/basex-api/src/main/c#/QueryExample.cs000066400000000000000000000024071274071665100214640ustar00rootroot00000000000000/* * This example shows how queries can be executed in an iterative manner. * Iterative evaluation will be slower, as more server requests are performed. * * Documentation: http://docs.basex.org/wiki/Clients * * (C) BaseX Team 2005-12, BSD License */ using System; using System.Diagnostics; using System.IO; namespace BaseXClient { public class QueryIteratorExample { public static void Main(string[] args) { try { // create session Session session = new Session("localhost", 1984, "admin", "admin"); try { // create query instance string input = "for $i in 1 to 10 return Text { $i }"; Query query = session.Query(input); // loop through all results while (query.More()) { Console.WriteLine(query.Next()); } // close query query.Close(); } catch (IOException e) { // print exception Console.WriteLine(e.Message); } // close session session.Close(); } catch (IOException e) { // print exception Console.WriteLine(e.Message); } } } } basex-8.5.1/basex-api/src/main/c/000077500000000000000000000000001274071665100164465ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/c/GNUmakefile000066400000000000000000000023221274071665100205170ustar00rootroot00000000000000.PHONY: all lib clean clean-depend clean-all include config.mk LIBNAME= basexdbc DBCLIB= lib$(LIBNAME).$(LIBSFX) VERSION= 0.1 FDEBUG= CC?= cc CFLAGS+= -DBUILD="\"$(VERSION)\"" -fPIC LDFLAGS+= -fPIC LIBS+= ifdef FDEBUG CFLAGS+= -g -ggdb -DDEBUG CFLAGS+= -Wno-long-long -Wall -W -Wnested-externs -Wformat=2 CFLAGS+= -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations CFLAGS+= -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare CFLAGS+= -Wundef -Wbad-function-cast -Winline -Wcast-align endif SRCS= $(shell echo *.c) OBJS= $(patsubst %.c,%.o,$(SRCS)) all: lib example lib: $(DBCLIB) example: $(CC) -L. -l$(LIBNAME) example.c -o $@ $(DBCLIB): md5.o basexdbc.o readstring.o $(CC) $(LIBS) $(LDFLAGS) -shared -o $@ $+ depend: $(SRCS) $(CC) $(CFLAGS) -MM $(SRCS) >depend install: lib install -d -m555 /usr/lib/basex install -d -m555 /usr/include/basex install -m555 $(DBCLIB) /usr/lib/basex/ install -m444 basexdbc.h /usr/include/basex/ uninstall: rm -rf /usr/lib/basex/ rm -rf /usr/include/basex/ clean: rm -f $(DBCLIB) rm -f ${OBJS} rm -f example clean-config: rm -f config.mk clean-depend: rm -f depend clean-all: clean clean-config clean-depend uninstall include depend basex-8.5.1/basex-api/src/main/c/README000066400000000000000000000013611274071665100173270ustar00rootroot00000000000000BASEX DATABASE CLIENT FOR C ==================================================== Connect to running BaseX database server (basex.org) from C code. Compile dependencies: openssl debian: apt-get install libssl-dev Build (tested on Debian GNU/Linux unstable only): $ make [all|lib|example] # make install Inspect and test example: $ less example.c $ ./example (or, if lib is not installed, $ LD_LIBRARY_PATH=. ./example) Cleanup: $ make clean # make clean-all (removes 'make install'ed files as well) License: Copyright (c) 2005-12, Alexander Holupirek , BSD license Bug reports: https://github.com/holu/basexdbc/issues Have fun. Alex ================================================================================ basex-8.5.1/basex-api/src/main/c/basexdbc.c000066400000000000000000000227521274071665100203750ustar00rootroot00000000000000/** * basexdbc.c : communicate with BaseX database server * Works with BaseX 7.x (but not with BaseX 8.0 and later) * * Copyright (c) 2005-12, Alexander Holupirek , BSD license */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "basexdbc.h" #include "md5.h" #include "readstring.h" static int send_db(int sfd, const char *buf, size_t buf_len); static int basex_status(int sfd); /** * Connect to host on port using stream sockets. * * @param host string representing host to connect to * @param port string representing port on host to connect to * @return socket file descriptor or -1 in case of failure */ int basex_connect(const char *host, const char *port) { struct addrinfo hints; struct addrinfo *result = NULL, *rp; int sfd, rc; if (host == NULL || port == NULL) { #if DEBUG warnx("Missing hostname '%s' / port '%s'.", host, port); #endif return -1; } /* Obtain address(es) matching host/port */ memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; /* Allows IPv4 or IPv6 */ hints.ai_socktype = SOCK_STREAM; /* TCP socket */ hints.ai_flags = AI_NUMERICSERV; rc = getaddrinfo(host, port, &hints, &result); if (rc != 0) { #if DEBUG warnx("getaddrinfo: %s", gai_strerror(rc)); #endif return -1; } /* getaddrinfo() returns a list of address structures. * Try each address until we successfully connect(2). * If socket(2) (or connect(2)) fails, we (close the * socket and) try the next address. */ for (rp = result; rp != NULL; rp = rp->ai_next) { sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sfd == -1) continue; /* On error, try next address */ if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) break; /* Success */ close(sfd); /* Connect failed: close socket, try next address */ } if (rp == NULL) { /* No address succeeded */ warnx("Can not connect to BaseX server."); warnx("Hostname '%s', port %s.", host, port); return -1; } freeaddrinfo(result); /* No longer needed */ return sfd; /* This file descriptor is ready for I/O. */ } /** * Authenticate against BaseX server connected on sfd using user and passwd. * * Authentication as defined by BaseX transfer protocol (BaseX 7.0 ff.): * https://github.com/BaseXdb/basex-api/blob/master/etc/readme.txt * {...} = string; \n = single byte * * 1. Client connects to server socket (basex_connect) * 2. Server sends timestamp: {timestamp} \0 * 3. Client sends username and hash: * {username} \0 {md5(md5(password) + timestamp)} \0 * 4. Server sends \0 (success) or \1 (error) * * @param sfd socket file descriptor successfully connected to BaseX server * @param user string with database username * @param passwd string with password for database username * @return 0 in case of success, -1 in case of failure */ int basex_authenticate(int sfd, const char *user, const char *passwd) { char ts[BUFSIZ]; /* timestamp returned by basex. */ char *md5_pwd; /* md5'ed passwd */ int ts_len, rc, i; /* Right after the first connect BaseX returns a nul-terminated * timestamp string. */ memset(ts, 0, BUFSIZ); rc = read(sfd, &ts, BUFSIZ); if (rc == -1) { warnx("Reading timestamp failed."); return -1; } ts_len = strlen(ts); #if DEBUG warnx("timestamp : %s (%d)", ts, strlen(ts)); #endif /* BaseX Server expects an authentification sequence: * {username}\0{md5(md5(password) + timestamp)}\0 */ /* Send {username}\0 */ int user_len = strlen(user) + 1; rc = write(sfd, user, user_len); if (rc == -1 || rc != user_len) { warnx("Sending username failed. %d != %d", rc, user_len); return -1; } /* Compute md5 for passwd. */ md5_pwd = md5(passwd); if (md5_pwd == NULL) { warnx("md5 computation for password failed."); return -1; } int md5_pwd_len = strlen(md5_pwd); #if DEBUG warnx("md5(pwd) : %s (%d)", md5_pwd, md5_pwd_len); #endif /* Concat md5'ed passwd string and timestamp string. */ int pwdts_len = md5_pwd_len + ts_len + 1; char pwdts[pwdts_len]; memset(pwdts, 0, sizeof(pwdts)); for (i = 0; i < md5_pwd_len; i++) pwdts[i] = md5_pwd[i]; int j = md5_pwd_len; for (i = 0; i < ts_len; i++,j++) pwdts[j] = ts[i]; pwdts[pwdts_len - 1] = '\0'; #if DEBUG warnx("md5(pwd)+ts : %s (%d)", pwdts, strlen(pwdts)); #endif /* Compute md5 for md5'ed password + timestamp */ char *md5_pwdts = md5(pwdts); if (md5_pwdts == NULL) { warnx("md5 computation for password + timestamp failed."); return -1; } int md5_pwdts_len = strlen(md5_pwdts); #if DEBUG warnx("md5(md5(pwd)+ts): %s (%d)", md5_pwdts, md5_pwdts_len); #endif /* Send md5'ed(md5'ed password + timestamp) to basex. */ rc = send_db(sfd, md5_pwdts, md5_pwdts_len + 1); // also send '\0' if (rc == -1) { warnx("Sending credentials failed."); return -1; } free(md5_pwd); free(md5_pwdts); /* Retrieve authentification status. */ rc = basex_status(sfd); if (rc == -1) { warnx("Reading authentification status failed."); return -1; } if (rc != 0) { warnx("Authentification failed"); return -1; } #if DEBUG warnx("Authentification succeded."); #endif return 0; } /** * Read status single byte from socket. */ int basex_status(int sfd) { char c; int b = read(sfd, &c, 1); if (b == -1) { warnx("Can not retrieve status code."); return -1; } return c; } /** * Executes a command and returns a result string and an info/error string. * * A database command is sent to BaseX server connected on sfd. * The result is a \0 terminated, dynamically allocated string, which is placed * at the given result address or NULL. The same holds for the processing * information stored at info. * * In either case it is the responsibility of the caller to free(3) those * strings. * * The returned int is 0 if the command could be processed successfully, in that * case the result contains the result string of the command and info holds * the processing information. * If a value >0 is returned, the command could not be processed successfully, * result contains NULL and info contains the database error message. * If -1 is interned, an error occurred, result and info are set to NULL. * * int | result* | info* | * -----+---------+-------| * -1 | NULL | NULL | * 0 | result | info | * >0 | NULL | error | * * * strings shall be free(3)'ed by caller * * BaseX C/S protocol: * * client sends: {command} \0 * server sends: {result} \0 {info} \0 \0 * or \0 {error} \0 \1 * * @param sfd socket file descriptor connected to BaseX server * @param command to be processed by BaseX server * @param result address at which result from BaseX server is placed * @param info address at which info/error message from BaseX server is placed * @return int 0 for success (result and info contain strings sent from BaseX) * -1 in case of failure (result and info are set to NULL), >0 an error occurred * while processing the command (result contains NULL, info contains error * message) */ int basex_execute(int sfd, const char *command, char **result, char **info) { int rc; /* Send {command}\0 to server. */ rc = send_db(sfd, command, strlen(command) + 1); if (rc == -1) { warnx("Can not send command '%s' to server.", command); goto err; } /* --- Receive from server: {result} \0 {info} \0 \0 * \0 {error} \0 \1 */ /* Receive {result} \0 */ rc = readstring(sfd, result); if (rc == -1) { warnx("Can not retrieve result for command '%s' from server.", command); goto err; } #if DEBUG warnx("[execute] result: '%s'\n", *result); #endif /* Receive {info/error} \0 .*/ rc = readstring(sfd, info); if (rc == -1) { warnx("Can not retrieve info for command '%s' from server.", *info); goto err; } #if DEBUG warnx("[execute] info/error: '%s'\n", *info); #endif /* Receive terminating \0 for success or \1 for error .*/ rc = basex_status(sfd); #if DEBUG warnx("[execute] status: '%d'\n", rc); #endif if (rc == -1) { warnx("Can not retrieve status."); goto err; } if (rc == 1) { warnx("BaseX error message : %s", *info); free(*result); *result = NULL; } assert(rc == 0 || rc == 1); return rc; err: *result = NULL; *info = NULL; return -1; } /** * Quits database session and closes stream connection to database server. * * @param socket file descriptor for database session. */ void basex_close(int sfd) { /* Send {exit}\0 to server. */ int rc = send_db(sfd, "exit", 4 + 1); if (rc != 0) warnx("Can not send 'exit' command to server."); /* Close socket. */ rc = shutdown(sfd, SHUT_RDWR); if (rc == -1) warn("Can not properly shutdown socket."); } /** * Writes buffer buf of buf_len to socket sfd. * * @param socket file descriptor for database session. * @param buf to be sent to server * @param buf_len # of bytes in buf * @return 0 if all data has successfully been written to server, * -1 in case of failure. */ static int send_db(int sfd, const char *buf, size_t buf_len) { ssize_t ret; while (buf_len != 0 && (ret = write(sfd, buf, buf_len)) != 0) { if (ret == -1) { if (errno == EINTR) continue; warn("Can not write to server"); return -1; } #if DEBUG int i; warnx("write: \n"); for (i = 0; i < ret; i++) warnx("[write] %3d : 0x%08x %4d %c", i, buf[i], buf[i], buf[i]); #endif /* DEBUG */ buf_len -= ret; buf += ret; } return 0; } basex-8.5.1/basex-api/src/main/c/basexdbc.h000066400000000000000000000017571274071665100204040ustar00rootroot00000000000000/** * basexdbc.h : communicate with BaseX database server * * Copyright (c) 2005-12, Alexander Holupirek , BSD license */ /* Connect to BaseX server and open session. Returns socket file descriptor. */ int basex_connect(const char *host, const char *port); /* Authenticate for this session (passing socket desc, db user, and passwd). */ int basex_authenticate(int sfd, const char *user, const char *passwd); /* Send database command to server. * Expect result and info to be filled (must be free(3)'ed afterwards). * * int | result | info | * -----+--------+-------| * -1 | NULL | NULL | general error (i/o and the like) * 0 | result | info | database command has been processed successfully * >0 | NULL | error | database command processing failed * * BaseX commands: http://docs.basex.org/wiki/Commands */ int basex_execute(int sfd, const char *command, char **result, char **info); /* Close session with descriptor sfd. */ void basex_close(int sfd); basex-8.5.1/basex-api/src/main/c/changelog000066400000000000000000000001561274071665100203220ustar00rootroot00000000000000basexdbc (0.1) * Initial release. -- Alexander Holupirek Sat May 14 16:59:07 CEST 2011 basex-8.5.1/basex-api/src/main/c/config.h000066400000000000000000000000151274071665100200600ustar00rootroot00000000000000/* Darwin */ basex-8.5.1/basex-api/src/main/c/configure000066400000000000000000000030351274071665100203530ustar00rootroot00000000000000#!/bin/sh # # Copyright (c) 2011 Alexander Holupirek # Copyright (c) 2009 Nicholas Marriott # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER # IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING # OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # DBC_PLATFORM=${DBC_PLATFORM:-`uname -s`} CONFIG_H=config.h rm -f $CONFIG_H echo "/* $DBC_PLATFORM */" >$CONFIG_H CONFIG_MK=config.mk rm -f $CONFIG_MK echo "# $DBC_PLATFORM" >$CONFIG_MK case $DBC_PLATFORM in # ------------------------------------------------------------------------------ Linux) cat <>$CONFIG_MK LIBSFX=so LIBS+= -lssl EOF ;; # ------------------------------------------------------------------------------ Darwin) cat <>$CONFIG_MK LIBSFX=dylib LIBS+= -lcrypto EOF ;; # ------------------------------------------------------------------------------ *) echo Unable to configure for $DBC_PLATFORM exit 1 esac echo Configured for $DBC_PLATFORM exit 0 basex-8.5.1/basex-api/src/main/c/example.c000066400000000000000000000031051274071665100202440ustar00rootroot00000000000000/* Copyright (c) 2005-12, Alexander Holupirek , BSD license */ #include #include #include #include #include "basexdbc.h" /* once libbasexdbc.so is installed in /usr/include/basex/ use: #include "basex/basexdbc.h" */ #define DBHOST "localhost" #define DBPORT "1984" #define DBUSER "admin" #define DBPASSWD "admin" /* * Example to demonstrate communication with running BaseX database server. * * $ cc -L. -lbasexdbc example.c -o example */ int main(void) { int sfd, rc; /* Connect to server and receive socket descriptor for this session. */ sfd = basex_connect(DBHOST, DBPORT); if (sfd == -1) { warnx("Can not connect to BaseX server."); return 0; } /* We are connected, let's authenticate for this session. */ rc = basex_authenticate(sfd, DBUSER, DBPASSWD); if (rc == -1) { warnx("Access to DB denied."); goto out; } /* Send command in default mode and receive the result string. */ const char *command = "xquery 1 + 1"; char *result; char *info; rc = basex_execute(sfd, command, &result, &info); if (rc == -1) { // general (i/o or the like) error warnx("An error occured during execution of '%s'.", command); goto free_and_out; } if (rc == 1) { // database error while processing command warnx("Processing of '%s' failed.", command); } /* print command, result and info/error */ printf("command: '%s'\n", command); printf("result : '%s'\n", result); printf("%s : '%s'\n", (rc == 1) ? "error" : "info", info); free_and_out: free(result); free(info); out: basex_close(sfd); return 0; } basex-8.5.1/basex-api/src/main/c/md5.c000066400000000000000000000040631274071665100173020ustar00rootroot00000000000000/* Copyright (c) 2005-12, Alexander Holupirek , BSD license */ #include #include #include #include #include "md5.h" /** * Print ascii hex representation of md5 value to newly allocated string. */ static int md5toa(unsigned char *md_value, unsigned int md_len, char **md5_string) { unsigned int i, j; int rc; int hex_len = 3; // hex length to be printed xx\0 unsigned int length = 32; // length of md5 ascii hex representation *md5_string = calloc(length + 1, sizeof(char)); if (*md5_string == NULL) { warnx("Can not allocate memory for md5 ascii hex string."); return -1; } for (i = 0, j = 0; i < md_len && j < length; i++, j += 2) { rc = snprintf((*md5_string) + j, hex_len, "%02x", md_value[i]); if (!(rc > -1 && rc < hex_len)) { warnx("Construction of md5 ascii hex string failed."); return -1; } } (*md5_string)[length] = '\0'; // is already \0, but we are defensive return 0; } /** * Consult EVP_DigestInit(3SSL) for details. */ static char * md5_digest(int n, ...) { EVP_MD_CTX mdctx; const EVP_MD *md; unsigned char md_value[EVP_MAX_MD_SIZE]; unsigned int md_len = 0; int i, rc; char *string; char *md5_result = NULL; OpenSSL_add_all_digests(); md = EVP_md5(); if(!md) err(1, "Unknown message digest"); EVP_MD_CTX_init(&mdctx); EVP_DigestInit_ex(&mdctx, md, NULL); va_list argPtr; va_start(argPtr, n); for (i = 0; i < n; i++) { string = va_arg(argPtr, char *); EVP_DigestUpdate(&mdctx, string, strlen(string)); } va_end(argPtr); EVP_DigestFinal_ex(&mdctx, md_value, &md_len); EVP_MD_CTX_cleanup(&mdctx); EVP_cleanup(); rc = md5toa(md_value, md_len, &md5_result); if (rc == -1) err(1, "md5 digest failure."); return md5_result; } /** * Compute 128bit MD5 digest for string. * * @param string from which digest shall be computed * @return Allocated C string containing the hex result representation is * returned. It should be passed to free(3). On failure NULL is returned. */ char * md5(const char *string) { return md5_digest(1, string); } basex-8.5.1/basex-api/src/main/c/md5.h000066400000000000000000000005571274071665100173130ustar00rootroot00000000000000/* Copyright (c) 2005-12, Alexander Holupirek , BSD license */ /** * Compute 128bit MD5 digest for string. * * @param string from which digest shall be computed * @return Allocated C string containing the hex result representation is * returned. It should be passed to free(3). On failure NULL is returned. */ char *md5(const char *string); basex-8.5.1/basex-api/src/main/c/readstring.c000066400000000000000000000042621274071665100207600ustar00rootroot00000000000000/* Copyright (c) 2005-12, Alexander Holupirek , BSD license */ #include #include #include #include #include #include #include #include #include "readstring.h" static size_t READSTRING_MAX = 1024 * 1024 * 10; // 10MB /** * Reads string from file descriptor into dynamically allocated string. * * A variable length of characters is read from fd until a \0 byte * is detected or a predefined maximum READSTRING_MAX is reached. * The read bytes are stored into a dynamically allocated buffer str. * It is the responsibility of the caller to free(3) str. * * @param fd file descriptor to read from * @param str address of the newly allocated c string * @return number of characters read or -1 in case of failure * in case of an error str is set to NULL */ ssize_t readstring(int fd, char **str) { char b; int rb; // # of read byte (-1, 0, or 1) size_t chars = 0; // # of stored chars in str size_t size = 32; // start capacity of alloc'ed string // allocate default capacity *str = calloc(size, sizeof(char)); if (str == NULL) { warn("malloc failed."); return -1; } // read until \0 is detected or predefined maximum is reached while(1) { if (!(chars < size - 1)) { // reallocate if (size && 2 > SIZE_MAX / size) { errno = ENOMEM; warn("overflow"); goto err; } size_t newsize = size * 2; if (newsize < READSTRING_MAX) { char *newstr; if ((newstr = realloc(*str, newsize)) == NULL) { warn("reallocation failed."); goto err; } *str = newstr; size = newsize; } else { errno = ENOBUFS; warn("variable string exceeds maximum of %d" , READSTRING_MAX); goto err; } } rb = read(fd, &b, 1); if (rb == -1) { if (rb == EINTR) // Interrupted, try again continue; else { warn("Can not read"); goto err; } } if (rb == 0) { // EOF warnx("Hmm, we expected a \\0 before EOF."); goto err; } // store another read char *((*str) + chars) = b; chars++; if (b == '\0') { // We are done. break; } } return chars; err: free(*str); *str = NULL; return -1; } basex-8.5.1/basex-api/src/main/c/readstring.h000066400000000000000000000001731274071665100207620ustar00rootroot00000000000000/* Copyright (c) 2005-12, Alexander Holupirek , BSD license */ ssize_t readstring(int fd, char **str); basex-8.5.1/basex-api/src/main/haskell/000077500000000000000000000000001274071665100176475ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/haskell/BaseXClient.hs000066400000000000000000000055571274071665100223600ustar00rootroot00000000000000------------------------------------------------------------------------------- -- | -- Module : BaseXClient -- Copyright : (C) BaseX Team 2005-12 -- License : BSD -- -- Maintainer : leo@woerteler.de -- Stability : experimental -- Portability : portable -- -- Haskell client for BaseX. -- Works with BaseX 6.x (but not with BaseX 7 and later) -- -- It requires the PureMD5 package fom Hackage: 'cabal install PureMD5'. -- ------------------------------------------------------------------------------- -- -- Example: -- -- module Example where -- import BaseXClient -- import Network ( withSocketsDo ) -- main :: IO () -- main = withSocketsDo $ do -- (Just session) <- connect "localhost" 1984 "admin" "admin" -- execute session "xquery 1 to 10" >>= putStrLn . either id content -- close session -- ------------------------------------------------------------------------------- module BaseXClient ( connect, execute, close, Result(..) ) where import Network ( withSocketsDo, PortID(..), PortNumber(..), connectTo ) import Control.Applicative ( (<$>) ) import System.IO ( Handle, hGetChar, hPutChar, hPutStr, hClose, BufferMode(..), hSetBuffering, hFlush) import qualified Data.Digest.Pure.MD5 as MD5 ( md5 ) import Data.ByteString.Lazy.UTF8 ( fromString ) data Session = Session Handle deriving Show data Result = Result { info :: String, content :: String } deriving Show -- | Connects to the BaseX server at host:port and establishes a session -- with the given user name and password. connect :: String -- ^ host name / IP -> PortNumber -- ^ port -> String -- ^ user name -> String -- ^ password -> IO (Maybe Session) connect host port user pass = do h <- connectTo host (PortNumber port) hSetBuffering h (BlockBuffering $ Just 4096) ts <- readString h writeString h user writeString h $ md5 (md5 pass ++ ts) success <- ('\0' ==) <$> hGetChar h return $ if success then Just $ Session h else Nothing where md5 = show . MD5.md5 . fromString -- | Executes a database command on the server and returns the result. execute :: Session -- ^ BaseX session -> String -- ^ db command -> IO (Either String Result) execute (Session h) cmd = do writeString h cmd res <- readString h inf <- readString h success <- ('\0' ==) <$> hGetChar h return $ if success then Right Result { info = inf, content = res } else Left inf -- | Closes the connection. close :: Session -> IO () close (Session h) = writeString h "exit" >> hClose h readString :: Handle -> IO String readString h = do c <- hGetChar h if c /= '\0' then (c:) <$> readString h else return [] writeString :: Handle -> String -> IO () writeString h str = hPutStr h str >> hPutChar h '\0' >> hFlush h basex-8.5.1/basex-api/src/main/haskell/Example.hs000066400000000000000000000021621274071665100215770ustar00rootroot00000000000000------------------------------------------------------------------------------- -- | -- Module : Example -- Copyright : (C) BaseX Team 2005-12 -- License : BSD -- -- Maintainer : leo@woerteler.de -- Stability : experimental -- Portability : portable -- -- This example shows how database commands can be executed. -- -- Documentation: http://docs.basex.org/wiki/Clients -- ------------------------------------------------------------------------------- module Example where import BaseXClient import Network ( withSocketsDo ) import Data.Time.Clock ( getCurrentTime, diffUTCTime ) import Control.Applicative ( (<$>), (<*>), pure ) query :: String query = "xquery 1 to 10" main :: IO () main = withSocketsDo $ do -- start time start <- getCurrentTime -- connect to the server (Just session) <- connect "localhost" 1984 "admin" "admin" -- execute and print the query execute session query >>= putStrLn . either id content -- close the session close session -- print time difference (diffUTCTime <$> getCurrentTime <*> pure start) >>= print basex-8.5.1/basex-api/src/main/java/000077500000000000000000000000001274071665100171455ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/java/org/000077500000000000000000000000001274071665100177345ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/java/org/basex/000077500000000000000000000000001274071665100210365ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/java/org/basex/BaseXHTTP.java000066400000000000000000000335251274071665100234130ustar00rootroot00000000000000package org.basex; import static org.basex.core.Text.*; import static org.basex.http.HTTPText.*; import java.io.*; import java.net.*; import javax.net.ssl.*; import org.basex.core.*; import org.basex.http.*; import org.basex.io.*; import org.basex.server.*; import org.basex.server.Log.LogType; import org.basex.util.*; import org.basex.util.options.*; import org.eclipse.jetty.server.*; import org.eclipse.jetty.server.nio.*; import org.eclipse.jetty.server.ssl.*; import org.eclipse.jetty.webapp.*; import org.eclipse.jetty.xml.*; /** * This is the main class for the starting the database HTTP services. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen * @author Dirk Kirsten */ public final class BaseXHTTP extends Main { /** Database context. */ private final Context context; /** HTTP server. */ private final Server jetty; /** HTTP port. */ private int httpPort; /** Start as daemon. */ private boolean service; /** Quiet flag. */ private boolean quiet; /** Stop flag. */ private boolean stop; /** * Main method, launching the HTTP services. * Command-line arguments are listed with the {@code -h} argument. * @param args command-line arguments */ public static void main(final String... args) { try { new BaseXHTTP(args); } catch(final Exception ex) { Util.errln(ex); System.exit(1); } } /** * Constructor. * @param args command-line arguments * @throws Exception exception */ public BaseXHTTP(final String... args) throws Exception { super(args); parseArgs(); // context must be initialized after parsing of arguments context = HTTPContext.init(); // create jetty instance and set default context to HTTP path final StaticOptions sopts = context.soptions; final String webapp = sopts.get(StaticOptions.WEBPATH); final WebAppContext wac = new WebAppContext(webapp, "/"); jetty = (Server) new XmlConfiguration(initJetty(webapp).inputStream()).configure(); jetty.setHandler(wac); final Connector[] conns = jetty.getConnectors(); if(conns == null || conns.length == 0) throw new BaseXException("No Jetty connector defined in " + JETTYCONF + "."); if(httpPort != 0) { for(final Connector conn : conns) { if(conn instanceof SelectChannelConnector) { conn.setPort(httpPort); break; } } } // info strings final String startX = HTTP + ' ' + SRV_STARTED_PORT_X; final String stopX = HTTP + ' ' + SRV_STOPPED_PORT_X; if(stop) { stop(); if(!quiet) for(final Connector conn : conns) Util.outln(stopX, conn.getPort()); // keep message visible for a while Performance.sleep(1000); return; } // start web server in a new process final Connector conn1 = conns[0]; if(service) { start(conn1.getPort(), conn1 instanceof SslSelectChannelConnector, args); if(!quiet) for(final Connector conn : conns) Util.outln(startX, conn.getPort()); // keep message visible for a while Performance.sleep(1000); return; } // request password on command line if only the user was specified final String user = Prop.get(StaticOptions.USER); String pw = Prop.get(StaticOptions.PASSWORD); if(user != null && !user.isEmpty()) { while(pw == null || pw.isEmpty()) { Util.out(PASSWORD + COLS); pw = Util.password(); Prop.put(StaticOptions.PASSWORD, pw); } } // start web server if(!quiet) Util.outln(header()); try { jetty.start(); } catch(final BindException ex) { Util.debug(ex); throw new BaseXException(HTTP + ' ' + SRV_RUNNING_X, conn1.getPort()); } // throw cached exception that did not break the servlet architecture final IOException ex = HTTPContext.exception(); if(ex != null) throw ex; // show start message if(!quiet) { for(final Connector conn : conns) Util.outln(startX, conn.getPort()); } // initialize web.xml settings, assign system properties and run database server. // the call of this function may already have been triggered during the start of jetty HTTPContext.init(wac.getServletContext()); // start daemon for stopping web server final int port = sopts.get(StaticOptions.STOPPORT); if(port >= 0) new StopServer(sopts.get(StaticOptions.SERVERHOST), port).start(); // show info when HTTP server is aborted. needs to be called in constructor: // otherwise, it may only be called if the JVM process is already shut down Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { if(!quiet) { for(final Connector conn : conns) Util.outln(stopX, conn.getPort()); } final Log l = context.log; if(l != null) { for(final Connector conn : conns) { l.writeServer(LogType.OK, Util.info(stopX, conn.getPort())); } } context.close(); } }); // log server start at very end (logging flag could have been updated by web.xml) for(final Connector conn : conns) { context.log.writeServer(LogType.OK, Util.info(startX, conn.getPort())); } } /** * Stops the server. * @throws Exception exception */ public void stop() throws Exception { // notify the jetty monitor to stop final StaticOptions sopts = context.soptions; final int port = num(StaticOptions.STOPPORT, sopts); final String host = sopts.get(StaticOptions.SERVERHOST); if(port >= 0) stop(host.isEmpty() ? S_LOCALHOST : host, port); // server has been started in a separate process and needs to be stopped if(!bool(StaticOptions.HTTPLOCAL, sopts)) { BaseXServer.stop(host, num(StaticOptions.SERVERPORT, sopts)); } } /** * Returns a numeric value for the specified option. * @param option option to be retrieved * @param sopts static options * @return numeric value */ private static int num(final NumberOption option, final StaticOptions sopts) { final String val = Prop.get(option); return val == null || val.isEmpty() ? sopts.get(option) : Strings.toInt(val); } /** * Returns a boolean value for the specified option. * @param option option to be retrieved * @param sopts static options * @return boolean value */ private static boolean bool(final BooleanOption option, final StaticOptions sopts) { final String val = Prop.get(option); return val == null || val.isEmpty() ? sopts.get(option) : Boolean.parseBoolean(val); } /** * Returns a reference to the Jetty configuration file. * @param root target root directory * @return input stream * @throws IOException I/O exception */ private static IOFile initJetty(final String root) throws IOException { locate(WEBCONF, root); return locate(JETTYCONF, root); } /** * Locates the specified configuration file. * @param file file to be copied * @param root target root directory * @return reference to created file * @throws IOException I/O exception */ private static IOFile locate(final String file, final String root) throws IOException { final IOFile trg = new IOFile(root, file); final boolean create = !trg.exists(); // try to locate file from development branch final IO in = new IOFile("src/main/webapp", file); final byte[] data; if(in.exists()) { data = in.read(); // check if resource path exists IOFile res = new IOFile("src/main/resources"); if(res.exists()) { res = new IOFile(res, file); // update file in resource path if it has changed if(!res.exists() || !Token.eq(data, res.read())) { Util.errln("Updating " + res); res.parent().md(); res.write(in.read()); } } } else if(create) { // try to locate file from resource path try(final InputStream is = BaseXHTTP.class.getResourceAsStream('/' + file)) { if(is == null) throw new BaseXException(in + " not found."); data = new IOStream(is).read(); } } else { return trg; } if(create) { // create configuration file Util.errln("Creating " + trg); trg.parent().md(); trg.write(data); } return trg; } @Override protected void parseArgs() throws IOException { /* command-line properties will be stored in system properties; * this way, they will not be overwritten by the settings specified in web.xml. */ final MainParser arg = new MainParser(this); boolean serve = true; while(arg.more()) { if(arg.dash()) { switch(arg.next()) { case 'c': // use client mode Prop.put(StaticOptions.HTTPLOCAL, Boolean.toString(false)); break; case 'd': // activate debug mode Prop.put(StaticOptions.DEBUG, Boolean.toString(true)); Prop.debug = true; break; case 'D': // hidden flag: daemon mode serve = false; break; case 'h': // parse HTTP port httpPort = arg.number(); break; case 'l': // use local mode Prop.put(StaticOptions.HTTPLOCAL, Boolean.toString(true)); break; case 'n': // parse host name final String n = arg.string(); Prop.put(StaticOptions.HOST, n); Prop.put(StaticOptions.SERVERHOST, n); break; case 'p': // parse server port final int p = arg.number(); Prop.put(StaticOptions.PORT, Integer.toString(p)); Prop.put(StaticOptions.SERVERPORT, Integer.toString(p)); break; case 'P': // specify password Prop.put(StaticOptions.PASSWORD, arg.string()); break; case 'q': // quiet flag (hidden) quiet = true; break; case 's': // parse stop port Prop.put(StaticOptions.STOPPORT, Integer.toString(arg.number())); break; case 'S': // set service flag service = serve; break; case 'U': // specify user name Prop.put(StaticOptions.USER, arg.string()); break; case 'z': // suppress logging Prop.put(StaticOptions.LOG, Boolean.toString(false)); break; default: throw arg.usage(); } } else { if(!"stop".equalsIgnoreCase(arg.string())) throw arg.usage(); stop = true; } } } // STATIC METHODS =========================================================== /** * Starts the HTTP server in a separate process. * @param port server port * @param ssl encryption via HTTPS * @param args command-line arguments * @throws BaseXException database exception */ private static void start(final int port, final boolean ssl, final String... args) throws BaseXException { // start server and check if it caused an error message final String error = Util.error(Util.start(BaseXHTTP.class, args), 2000); if(error != null) throw new BaseXException(error.trim()); // try to connect to the new server instance if(!ping(S_LOCALHOST, port, ssl)) throw new BaseXException(CONNECTION_ERROR_X, port); } /** * Generates a stop file for the specified port. * @param port server port * @return stop file */ private static IOFile stopFile(final int port) { return new IOFile(Prop.TMP, Util.className(BaseXHTTP.class) + port); } /** * Stops the server. * @param host server host * @param port server port * @throws IOException I/O exception */ private static void stop(final String host, final int port) throws IOException { final IOFile stopFile = stopFile(port); try { stopFile.touch(); try(final Socket s = new Socket(host, port)) { } // give the notified process some time to quit Performance.sleep(100); } catch(final ConnectException ex) { throw new IOException(Util.info(CONNECTION_ERROR_X, port)); } finally { stopFile.delete(); } } /** * Checks if a server is running. * @param host host * @param port server port * @param ssl encryption via HTTPS * @return boolean success */ private static boolean ping(final String host, final int port, final boolean ssl) { try(final InputStream is = new IOUrl((ssl ? "https://" : "http://") + host + ':' + port). connection().getInputStream()) { // create connection return true; } catch(final FileNotFoundException | SSLHandshakeException ex) { // if page is not found, server is running // if SSL handshake failed server is running, otherwise SSLException return true; } catch(final IOException ex) { return false; } } @Override public String header() { return Util.info(S_CONSOLE_X, S_HTTP_SERVER); } @Override public String usage() { return S_HTTPINFO; } /** Monitor for stopping the Jetty server. */ private final class StopServer extends Thread { /** Server socket. */ private final ServerSocket socket; /** Stop file. */ private final IOFile stopFile; /** * Constructor. * @param host host address * @param port stop port * @throws IOException I/O exception */ StopServer(final String host, final int port) throws IOException { final InetAddress addr = host.isEmpty() ? null : InetAddress.getByName(host); socket = new ServerSocket(); socket.setReuseAddress(true); socket.bind(new InetSocketAddress(addr, port)); stopFile = stopFile(port); setDaemon(true); } @Override public void run() { try { while(true) { try(final Socket s = socket.accept()) { } if(stopFile.exists()) { socket.close(); stopFile.delete(); jetty.stop(); break; } } } catch(final Exception ex) { Util.errln(ex); } } } } basex-8.5.1/basex-api/src/main/java/org/basex/api/000077500000000000000000000000001274071665100216075ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/java/org/basex/api/xmldb/000077500000000000000000000000001274071665100227155ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/java/org/basex/api/xmldb/BXCollection.java000066400000000000000000000203531274071665100261100ustar00rootroot00000000000000package org.basex.api.xmldb; import static org.basex.core.Text.*; import java.io.*; import java.lang.reflect.*; import org.basex.build.*; import org.basex.build.xml.*; import org.basex.core.*; import org.basex.core.cmd.*; import org.basex.data.*; import org.basex.io.*; import org.basex.util.*; import org.basex.util.list.*; import org.w3c.dom.*; import org.xmldb.api.base.*; import org.xmldb.api.modules.*; /** * Implementation of the Collection Interface for the XMLDB:API. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ public final class BXCollection implements Collection, BXXMLDBText { /** Database context. */ final BXDatabase db; /** Database context. */ Context ctx; /** * Constructor to create/open a collection. * @param name name of the database * @param open open existing database * @param database database context * @throws XMLDBException exception */ public BXCollection(final String name, final boolean open, final Database database) throws XMLDBException { db = (BXDatabase) database; ctx = db.ctx; try { final MainOptions opts = ctx.options; ctx.openDB(open ? Open.open(name, ctx, opts) : CreateDB.create(name, Parser.emptyParser(opts), ctx, opts)); } catch(final IOException ex) { throw new XMLDBException(ErrorCodes.VENDOR_ERROR, ex.getMessage()); } } @Override public String getName() { return ctx.data().meta.name; } @Override public Service[] getServices() throws XMLDBException { check(); return new Service[] { getService(BXQueryService.XPATH, "1.0"), getService(BXQueryService.XQUERY, "1.0"), getService(BXCollectionManagementService.MANAGEMENT, "1.0") }; } @Override public Service getService(final String name, final String version) throws XMLDBException { check(); if("1.0".equals(version)) { if(Strings.eq(name, BXQueryService.XPATH, BXQueryService.XQUERY)) return new BXQueryService(this, name, version); if(name.equals(BXCollectionManagementService.MANAGEMENT)) return new BXCollectionManagementService(this); } return null; } @Override public Collection getParentCollection() throws XMLDBException { check(); return null; } @Override public Collection getChildCollection(final String name) throws XMLDBException { check(); return null; } @Override public int getChildCollectionCount() throws XMLDBException { check(); return 0; } @Override public String[] listChildCollections() throws XMLDBException { check(); return new String[0]; } @Override public int getResourceCount() throws XMLDBException { check(); return ctx.data().meta.ndocs; } @Override public String[] listResources() throws XMLDBException { check(); final Data data = ctx.data(); final IntList docs = data.resources.docs(); final int ds = docs.size(); final StringList sl = new StringList(ds); for(int d = 0; d < ds; d++) sl.add(Token.string(data.text(docs.get(d), true))); return sl.finish(); } @Override public BXXMLResource createResource(final String id, final String type) throws XMLDBException { check(); if(type.equals(XMLResource.RESOURCE_TYPE)) { // create new id if necessary final String uid = id == null || id.isEmpty() ? createId() : id; return new BXXMLResource(null, 0, uid, this); } // reject binary and other resources throw type.equals(BinaryResource.RESOURCE_TYPE) ? new XMLDBException(ErrorCodes.VENDOR_ERROR, ERR_BINARY) : new XMLDBException(ErrorCodes.UNKNOWN_RESOURCE_TYPE, ERR_TYPE + type); } @Override public void removeResource(final Resource res) throws XMLDBException { check(); // check if the resource is an xml resource final BXXMLResource del = checkXML(res); final Data data = ctx.data(); // check if data instance refers to another database if(del.data != data && del.data != null) throw new XMLDBException( ErrorCodes.NO_SUCH_RESOURCE, ERR_UNKNOWN + data.meta.name); try { data.startUpdate(ctx.options); data.delete(getResource(del.getId()).pre); ctx.invalidate(); data.finishUpdate(ctx.options); } catch(final IOException ex) { Util.debug(ex); throw new XMLDBException(ErrorCodes.VENDOR_ERROR, ERR_LOCK); } } @Override public void storeResource(final Resource res) throws XMLDBException { check(); // check if resource has any contents final BXXMLResource xml = checkXML(res); if(res.getContent() == null) throw new XMLDBException(ErrorCodes.INVALID_RESOURCE, ERR_EMPTY); // disallow storage of resources without id final String id = res.getId(); if(id == null) throw new XMLDBException(ErrorCodes.INVALID_RESOURCE, ERR_ID); // document exists - delete old one first final Resource old = getResource(id); if(old != null) removeResource(old); // create parser, dependent on input type final Object cont = xml.content; // insert document final Data md; try { final Parser p = cont instanceof Document ? new DOMWrapper((Document) cont, id, ctx.options) : Parser.singleParser(new IOContent((byte[]) cont, id), ctx.options, ""); md = MemBuilder.build(id, p); } catch(final IOException ex) { throw new XMLDBException(ErrorCodes.INVALID_RESOURCE, ex.getMessage()); } final Data data = ctx.data(); try { data.startUpdate(ctx.options); data.insert(data.meta.size, -1, new DataClip(md)); ctx.invalidate(); data.finishUpdate(ctx.options); } catch(final IOException ex) { Util.debug(ex); throw new XMLDBException(ErrorCodes.VENDOR_ERROR, ERR_LOCK); } } @Override public BXXMLResource getResource(final String id) throws XMLDBException { check(); if(id == null) return null; final Data data = ctx.data(); final int pre = data.resources.doc(id); return pre == -1 ? null : new BXXMLResource(data, pre, id, this); } @Override public String createId() throws XMLDBException { final String[] res = listResources(); String id; do { id = Long.toString(System.currentTimeMillis()); } while(exists(res, id)); return id; } @Override public boolean isOpen() { return ctx != null; } @Override public void close() { if(ctx != null) ctx.close(); ctx = null; } @Override public String getProperty(final String name) throws XMLDBException { check(); try { return MetaData.class.getField(name).get(ctx.data().meta).toString(); } catch(final Exception ex) { return null; } } @Override public void setProperty(final String name, final String val) throws XMLDBException { check(); try { final MetaData md = ctx.data().meta; final Field f = MetaData.class.getField(name); final Object k = f.get(md); if(k instanceof Boolean) { final boolean b = val == null ? !(Boolean) k : val.equalsIgnoreCase(TRUE); f.setBoolean(md, b); } else if(k instanceof Integer) { f.setInt(md, Integer.parseInt(val)); } else { f.set(md, val); } } catch(final Exception ex) { throw new XMLDBException(ErrorCodes.VENDOR_ERROR, ERR_PROP + name); } } /** * Checks if the specified id exists in the specified id list. * @param list id list * @param id id to be found * @return result of check */ private static boolean exists(final String[] list, final String id) { for(final String l : list) if(l.equals(id)) return true; return false; } /** * Checks if the collection is currently open. * @throws XMLDBException exception */ private void check() throws XMLDBException { if(ctx == null) throw new XMLDBException(ErrorCodes.COLLECTION_CLOSED); } /** * Returns the specified resource as a project specific XML resource. * If that's not possible, throws an exception * @param res input resource * @return xml resource * @throws XMLDBException exception */ private static BXXMLResource checkXML(final Resource res) throws XMLDBException { if(!(res instanceof BXXMLResource)) { throw new XMLDBException(ErrorCodes.NO_SUCH_RESOURCE, ERR_UNKNOWN + res); } return (BXXMLResource) res; } } basex-8.5.1/basex-api/src/main/java/org/basex/api/xmldb/BXCollectionManagementService.java000066400000000000000000000035651274071665100314340ustar00rootroot00000000000000package org.basex.api.xmldb; import org.basex.core.*; import org.basex.core.cmd.*; import org.xmldb.api.base.*; import org.xmldb.api.modules.*; /** * Implementation of the CollectionManagementService Interface for the * XMLDB:API. Note that a database has one collection at a time, * so creating a new collection creates a new database as well, and the * specified collection reference is reset every time a database is created. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class BXCollectionManagementService implements CollectionManagementService, BXXMLDBText { /** Service constant. */ static final String MANAGEMENT = "CollectionManagementService"; /** Service constant. */ private static final String VERSION = "1.0"; /** Collection reference. */ private BXCollection coll; /** * Default constructor. * @param coll collection reference */ BXCollectionManagementService(final BXCollection coll) { this.coll = coll; } @Override public Collection createCollection(final String name) throws XMLDBException { return new BXCollection(name, false, coll.db); } @Override public void removeCollection(final String name) throws XMLDBException { try { new DropDB(name).execute(coll.ctx); } catch(final BaseXException ex) { throw new XMLDBException(ErrorCodes.VENDOR_ERROR, ex.getMessage()); } } @Override public String getName() { return MANAGEMENT; } @Override public String getVersion() { return VERSION; } @Override public void setCollection(final Collection cl) { coll = (BXCollection) cl; } @Override public String getProperty(final String name) { return null; } @Override public void setProperty(final String name, final String value) throws XMLDBException { throw new XMLDBException(ErrorCodes.VENDOR_ERROR, ERR_PROP + name); } } basex-8.5.1/basex-api/src/main/java/org/basex/api/xmldb/BXDatabase.java000066400000000000000000000046011274071665100255170ustar00rootroot00000000000000package org.basex.api.xmldb; import static org.basex.core.Text.*; import java.util.*; import org.basex.core.*; import org.basex.core.cmd.*; import org.basex.core.cmd.Set; import org.basex.util.*; import org.xmldb.api.base.*; import org.xmldb.api.base.Collection; /** * Implementation of the Database Interface for the XMLDB:API. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class BXDatabase implements Database, BXXMLDBText { /** Database context. */ final Context ctx = new Context(); @Override public boolean acceptsURI(final String uri) throws XMLDBException { getCollectionName(uri); return true; } @Override public Collection getCollection(final String uri, final String user, final String password) throws XMLDBException { // create database context final String name = getCollectionName(uri); final boolean exists = ctx.soptions.dbExists(name); return exists ? new BXCollection(name, true, this) : null; } @Override public String getConformanceLevel() { return CONFORMANCE_LEVEL; } @Override public String getName() { return Prop.PROJECT_NAME; } @Override public String getProperty(final String name) { try { return Get.get(name.toUpperCase(Locale.ENGLISH), ctx); } catch(final BaseXException ex) { return null; } } @Override public void setProperty(final String name, final String value) throws XMLDBException { try { new Set(name, value).execute(ctx); } catch(final BaseXException ex) { throw new XMLDBException(ErrorCodes.VENDOR_ERROR, ERR_PROP + name); } } /** * Returns the name of a collection. * @param uri input uri * @return collection name * @throws XMLDBException exception */ private String getCollectionName(final String uri) throws XMLDBException { // try to extract name of collection; otherwise, throw exception if(uri != null) { final String main = uri.startsWith(XMLDBC) ? uri : XMLDBC + uri; if(main.startsWith(XMLDBURI)) { final String host = main.substring(XMLDBURI.length()); final int port = ctx.soptions.get(StaticOptions.SERVERPORT); final String localhost = S_LOCALHOST + ':' + port + '/'; if(host.startsWith(localhost)) return host.substring(localhost.length()); } } throw new XMLDBException(ErrorCodes.INVALID_URI, ERR_URI + uri); } } basex-8.5.1/basex-api/src/main/java/org/basex/api/xmldb/BXQueryService.java000066400000000000000000000075121274071665100264450ustar00rootroot00000000000000package org.basex.api.xmldb; import static org.basex.util.Token.*; import java.util.*; import org.basex.query.*; import org.basex.query.value.*; import org.basex.query.value.node.*; import org.basex.query.value.seq.*; import org.basex.util.list.*; import org.xmldb.api.base.*; import org.xmldb.api.base.Collection; import org.xmldb.api.modules.*; /** * Abstract QueryService definition for the XMLDB:API. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class BXQueryService implements XPathQueryService, BXXMLDBText { /** XPath service constant. */ static final String XPATH = "XPathQueryService"; /** XQuery service constant. */ static final String XQUERY = "XQueryQueryService"; /** Namespaces. */ private final HashMap ns = new HashMap<>(); /** Service name. */ private final String name; /** Service version. */ private final String version; /** Collection reference. */ private BXCollection coll; /** * Standard constructor. * @param coll for collection reference * @param name service name * @param version version */ BXQueryService(final BXCollection coll, final String name, final String version) { this.coll = coll; this.name = name; this.version = version; } @Override public void setNamespace(final String prefix, final String uri) throws XMLDBException { if(uri != null && !uri.isEmpty()) ns.put(prefix == null ? "" : prefix, uri); else throw new XMLDBException(ErrorCodes.VENDOR_ERROR, ERR_NSURI + prefix); } @Override public String getNamespace(final String prefix) { return ns.get(prefix == null ? "" : prefix); } @Override public void removeNamespace(final String prefix) { ns.remove(prefix == null ? "" : prefix); } @Override public void clearNamespaces() { ns.clear(); } @Override public BXResourceSet query(final String query) throws XMLDBException { final DBNodes nodes = coll.ctx.current(); final boolean all = nodes.all(); return query(query, DBNodeSeq.get(new IntList(nodes.pres()), nodes.data(), all, all)); } @Override public BXResourceSet queryResource(final String id, final String query) throws XMLDBException { final BXXMLResource xml = coll.getResource(id); if(xml != null) return query(query, new DBNode(xml.data, xml.pre)); // throw exception if id was not found... throw new XMLDBException(ErrorCodes.VENDOR_ERROR, ERR_RES + id); } @Override public String getName() { return name; } @Override public String getVersion() { return version; } @Override public void setCollection(final Collection col) { coll = (BXCollection) col; } @Override public String getProperty(final String nm) { return null; } @Override public void setProperty(final String nm, final String value) throws XMLDBException { throw new XMLDBException(ErrorCodes.VENDOR_ERROR, ERR_PROP + nm); } /** * Runs a query and returns the result set. * @param query query string * @param nodes nodes * @return resource set * @throws XMLDBException exception */ private BXResourceSet query(final String query, final Value nodes) throws XMLDBException { // creates a query instance try(final QueryProcessor qp = new QueryProcessor(query, coll.ctx)) { qp.context(nodes); qp.parse(); try { qp.register(coll.ctx); // add default namespaces for(final Map.Entry entry : ns.entrySet()) { qp.sc.ns.add(token(entry.getKey()), token(entry.getValue()), null); } // perform query and return result return new BXResourceSet(qp.value(), coll); } finally { qp.close(); qp.unregister(coll.ctx); } } catch(final QueryException ex) { throw new XMLDBException(ErrorCodes.VENDOR_ERROR, ex.getMessage()); } } } basex-8.5.1/basex-api/src/main/java/org/basex/api/xmldb/BXResourceIterator.java000066400000000000000000000016331274071665100273160ustar00rootroot00000000000000package org.basex.api.xmldb; import java.util.*; import org.xmldb.api.base.*; /** * Implementation of the ResourceIterator Interface for the XMLDB:API. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class BXResourceIterator implements ResourceIterator, BXXMLDBText, Iterable { /** Resources. */ private final Iterator iter; /** * Standard constructor with result. * @param resources resource iterator */ BXResourceIterator(final ArrayList resources) { iter = resources.iterator(); } @Override public boolean hasMoreResources() { return iter.hasNext(); } @Override public Resource nextResource() throws XMLDBException { if(!iter.hasNext()) throw new XMLDBException(ErrorCodes.NO_SUCH_RESOURCE, ERR_ITER); return iter.next(); } @Override public Iterator iterator() { return iter; } } basex-8.5.1/basex-api/src/main/java/org/basex/api/xmldb/BXResourceSet.java000066400000000000000000000035351274071665100262630ustar00rootroot00000000000000package org.basex.api.xmldb; import java.util.*; import org.basex.query.value.*; import org.basex.util.*; import org.xmldb.api.base.*; import org.xmldb.api.base.Collection; /** * Implementation of the ResourceSet Interface for the XMLDB:API. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class BXResourceSet implements ResourceSet, BXXMLDBText { /** Resources. */ private final ArrayList list; /** Collection reference. */ private final Collection coll; /** * Default constructor with result. * @param result result * @param coll collection */ BXResourceSet(final Value result, final Collection coll) { // convert result into resource instances final int rs = (int) result.size(); list = new ArrayList<>(rs); for(int s = 0; s < rs; ++s) list.add(new BXXMLResource(result.itemAt(s), coll)); this.coll = coll; } @Override public Resource getResource(final long index) throws XMLDBException { if(index >= 0 && index < list.size()) return list.get((int) index); throw new XMLDBException(ErrorCodes.NO_SUCH_RESOURCE); } @Override public void addResource(final Resource resource) { list.add(resource); } @Override public void removeResource(final long index) { list.remove((int) index); } @Override public BXResourceIterator getIterator() { return new BXResourceIterator(list); } @Override public Resource getMembersAsResource() throws XMLDBException { final TokenBuilder tb = new TokenBuilder().add('<').add(XMLDB).add('>'); for(final Resource r : getIterator()) { tb.add(r.getContent().toString()); } return new BXXMLResource(tb.add('<').add('/').add(XMLDB).add('>').finish(), coll); } @Override public long getSize() { return list.size(); } @Override public void clear() { list.clear(); } } basex-8.5.1/basex-api/src/main/java/org/basex/api/xmldb/BXXMLDBText.java000066400000000000000000000027421274071665100255320ustar00rootroot00000000000000package org.basex.api.xmldb; import org.basex.util.*; /** * This class organizes textual information for the XMLDB API. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ interface BXXMLDBText { /** DB URI. */ String DBURI = Prop.PROJECT_NAME + "://"; /** XMLDB Prefix. */ String XMLDB = "xmldb"; /** XMLDB with colon. */ String XMLDBC = XMLDB + ':'; /** XMLDB URI. */ String XMLDBURI = XMLDBC + DBURI; /** Conformance level of the implementation. */ String CONFORMANCE_LEVEL = "0"; /** Error message. */ String ERR_URI = "Invalid URI: "; /** Error message. */ String ERR_PROP = "Property could not be set: "; /** Error message. */ String ERR_BINARY = "Binary resources not supported."; /** Error message. */ String ERR_TYPE = "Resource type is unknown: "; /** Error message. */ String ERR_EMPTY = "Resource has no contents."; /** Error message. */ String ERR_ID = "Resource has no ID."; /** Error message. */ String ERR_UNKNOWN = "Unknown Resource: "; /** Error message. */ String ERR_CONT = "Content cannot be set."; /** Error message. */ String ERR_NSURI = "Namespace URI is empty: "; /** Error message. */ String ERR_RES = "Resource not found: "; /** Error message. */ String ERR_ITER = "Resource pointer out of range."; /** Error message. */ String ERR_DOC = "Document ID cannot be retrieved from query result."; /** Error message. */ String ERR_LOCK = "Database cannot be marked as 'updating'."; } basex-8.5.1/basex-api/src/main/java/org/basex/api/xmldb/BXXMLResource.java000066400000000000000000000135051274071665100261660ustar00rootroot00000000000000package org.basex.api.xmldb; import static org.basex.util.Token.*; import java.io.*; import org.basex.api.dom.*; import org.basex.build.*; import org.basex.build.Parser; import org.basex.build.xml.*; import org.basex.core.*; import org.basex.data.*; import org.basex.io.*; import org.basex.io.in.*; import org.basex.io.out.*; import org.basex.io.parse.xml.*; import org.basex.io.serial.*; import org.basex.query.*; import org.basex.query.value.item.*; import org.basex.query.value.node.*; import org.w3c.dom.*; import org.xml.sax.*; import org.xmldb.api.base.*; import org.xmldb.api.modules.*; /** * Implementation of the XMLResource Interface for the XMLDB:API. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class BXXMLResource implements XMLResource, BXXMLDBText { /** Collection reference. */ private final Collection coll; /** String id. */ private String id; /** Query result. */ private Item item; /** Cached content. */ Object content; /** Data reference. */ Data data; /** Pre value or result position. */ int pre; /** * Constructor for generated results. * @param content content data * @param coll Collection */ BXXMLResource(final byte[] content, final Collection coll) { this.content = content; this.coll = coll; } /** * Constructor for query results. * @param item query result * @param coll Collection */ BXXMLResource(final Item item, final Collection coll) { this.item = item; this.coll = coll; } /** * Standard constructor. * @param data data reference * @param pre pre value * @param id id * @param coll collection */ BXXMLResource(final Data data, final int pre, final String id, final Collection coll) { this.id = id; this.coll = coll; this.data = data; this.pre = pre; } @Override public Collection getParentCollection() { return coll; } @Override public String getId() { return id; } @Override public String getResourceType() { return XMLResource.RESOURCE_TYPE; } @Override public Object getContent() throws XMLDBException { if(content == null) { try { // serialize and cache content final ArrayOutput ao = new ArrayOutput(); try(final Serializer ser = Serializer.get(ao, SerializerMode.NOINDENT.get())) { if(data != null) { ser.serialize(new DBNode(data, pre)); } else if(item != null) { ser.serialize(item); } else { return null; } } content = ao.toArray(); } catch(final IOException ex) { throw new XMLDBException(ErrorCodes.VENDOR_ERROR, ex.getMessage()); } } return content instanceof byte[] ? string((byte[]) content) : content; } @Override public void setContent(final Object value) throws XMLDBException { // allow only strings, byte arrays and {@link File} instances if(value instanceof byte[]) { content = value; } else if(value instanceof String) { content = token(value.toString()); } else if(value instanceof File) { try { content = new IOFile((File) value).read(); } catch(final IOException ex) { throw new XMLDBException(ErrorCodes.VENDOR_ERROR, ERR_CONT + '\n' + ex.getMessage()); } } else { throw new XMLDBException(ErrorCodes.VENDOR_ERROR, ERR_CONT); } } @Override public String getDocumentId() throws XMLDBException { // throw exception if resource results from query; does not conform to the // specs, but many query results are not related to a document anymore if(item != null) throw new XMLDBException(ErrorCodes.VENDOR_ERROR, ERR_DOC); // resource does not result from a query - return normal id if(id != null) return id; // get document root id int p = pre; while(p >= 0) { final int k = data.kind(p); if(k == Data.DOC) return string(data.text(p, true)); p = data.parent(p, k); } return null; } @Override public Node getContentAsDOM() { if(!(content instanceof Node)) content = new BXDoc(new DBNode(data, pre)); return (Node) content; } @Override public void setContentAsDOM(final Node cont) throws XMLDBException { // allow only document instances... if(cont == null) throw new XMLDBException(ErrorCodes.INVALID_RESOURCE); if(cont instanceof Document) content = cont; else throw new XMLDBException(ErrorCodes.WRONG_CONTENT_TYPE); } @Override public void getContentAsSAX(final ContentHandler handler) throws XMLDBException { if(handler == null) throw new XMLDBException(ErrorCodes.INVALID_RESOURCE); try { new XmlParser().contentHandler(handler).parse(new ArrayInput(getContent().toString())); } catch(final Exception pce) { throw new XMLDBException(ErrorCodes.VENDOR_ERROR, pce.getMessage()); } } @Override public ContentHandler setContentAsSAX() { // ..might be replaced by a custom SAX content handler in future final MemBuilder mb = new MemBuilder("", Parser.emptyParser(new MainOptions())); mb.init(); return new BXSAXContentHandler(this, mb); } /** SAX parser. */ private static final class BXSAXContentHandler extends SAXHandler { /** XMLResource. */ private final BXXMLResource resource; /** * Default constructor. * @param builder memory builder * @param resource resource */ BXSAXContentHandler(final BXXMLResource resource, final MemBuilder builder) { super(builder, false, false); this.resource = resource; } @Override public void endDocument() throws SAXException { try { resource.content = new DBNode(((MemBuilder) builder).data()).serialize( SerializerMode.NOINDENT.get()).toArray(); } catch(final QueryIOException ex) { error(new BaseXException(ex)); } } } } basex-8.5.1/basex-api/src/main/java/org/basex/api/xmldb/package-info.java000066400000000000000000000001361274071665100261040ustar00rootroot00000000000000/** * * Implementation of the XML:DB API. * */ package org.basex.api.xmldb;basex-8.5.1/basex-api/src/main/java/org/basex/http/000077500000000000000000000000001274071665100220155ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/java/org/basex/http/BaseXServlet.java000066400000000000000000000062071274071665100252340ustar00rootroot00000000000000package org.basex.http; import static javax.servlet.http.HttpServletResponse.*; import static org.basex.http.HTTPText.*; import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; import org.basex.core.*; import org.basex.core.StaticOptions.*; import org.basex.core.jobs.*; import org.basex.http.restxq.*; import org.basex.query.*; import org.basex.server.*; import org.basex.util.*; /** *

Base class for all servlets.

* * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ public abstract class BaseXServlet extends HttpServlet { /** Servlet-specific user. */ String username = ""; /** Servlet-specific password. */ String password = ""; /** Servlet-specific authentication method. */ AuthMethod auth; @Override public void init(final ServletConfig config) throws ServletException { super.init(config); try { HTTPContext.init(config.getServletContext()); final Enumeration en = config.getInitParameterNames(); while(en.hasMoreElements()) { String key = en.nextElement().toLowerCase(Locale.ENGLISH); final String val = config.getInitParameter(key); if(key.startsWith(Prop.DBPREFIX)) key = key.substring(Prop.DBPREFIX.length()); if(key.equalsIgnoreCase(StaticOptions.USER.name())) { username = val; } else if(key.equalsIgnoreCase(StaticOptions.PASSWORD.name())) { password = val; } else if(key.equalsIgnoreCase(StaticOptions.AUTHMETHOD.name())) { auth = AuthMethod.valueOf(val); } } } catch(final IOException ex) { throw new ServletException(ex); } } @Override public final void service(final HttpServletRequest req, final HttpServletResponse res) throws IOException { final HTTPContext http = new HTTPContext(req, res, this); final boolean restxq = this instanceof RestXqServlet; try { http.authorize(); run(http); http.log(SC_OK, ""); } catch(final HTTPException ex) { http.status(ex.getStatus(), Util.message(ex), restxq); } catch(final LoginException ex) { http.status(SC_UNAUTHORIZED, Util.message(ex), restxq); } catch(final IOException | QueryException ex) { http.status(SC_BAD_REQUEST, Util.message(ex), restxq); } catch(final JobException ex) { http.status(SC_GONE, Text.INTERRUPTED, restxq); } catch(final Exception ex) { final String msg = Util.bug(ex); Util.errln(msg); http.status(SC_INTERNAL_SERVER_ERROR, Util.info(UNEXPECTED, msg), restxq); } finally { if(Prop.debug) { Util.outln("_ REQUEST _________________________________" + Prop.NL + req); final Enumeration en = req.getHeaderNames(); while(en.hasMoreElements()) { final String key = en.nextElement(); Util.outln(Text.LI + key + Text.COLS + req.getHeader(key)); } Util.out("_ RESPONSE ________________________________" + Prop.NL + res); } } } /** * Runs the code. * @param http HTTP context * @throws Exception any exception */ protected abstract void run(final HTTPContext http) throws Exception; } basex-8.5.1/basex-api/src/main/java/org/basex/http/HTTPCode.java000066400000000000000000000033131274071665100242320ustar00rootroot00000000000000package org.basex.http; import static javax.servlet.http.HttpServletResponse.*; /** * Enumeration with HTTP codes and error messages. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ public enum HTTPCode { /** Error: 201 (created). */ CREATED_X(SC_CREATED, "%"), /** Error: 400 (bad request). */ BAD_REQUEST_X(SC_BAD_REQUEST, "%"), /** Error 400, "Only one operation can be specified". */ ONEOP(SC_BAD_REQUEST, "Only one operation can be specified."), /** Error 400, "Unknown parameter: '%'". */ UNKNOWN_PARAM_X(SC_BAD_REQUEST, "Unknown parameter: '%'."), /** Error 400, "Multiple context values specified.". */ MULTIPLE_CONTEXT_X(SC_BAD_REQUEST, "Multiple context values specified."), /** Error: 404 (not found). */ NOT_FOUND_X(SC_NOT_FOUND, "%"), /** Error: 404, "No path specified.". */ NO_PATH(SC_NOT_FOUND, "No path specified."), /** Error: 404, "No function found to process the request.". */ NO_XQUERY(SC_NOT_FOUND, "No function found that matches the request."), /** Error: 404, "RESTXQ directory not found.". */ NO_RESTXQ(SC_NOT_FOUND, "RESTXQ directory not found."), /** Error 501, "Method not supported: %.". */ NOT_IMPLEMENTED_X(SC_NOT_IMPLEMENTED, "Method not supported: %."); /** Status code. */ final int code; /** Error description. */ final String desc; /** * Constructor. * @param code status code * @param desc description */ HTTPCode(final int code, final String desc) { this.code = code; this.desc = desc; } /** * Returns an HTTP exception. * @param ext extended info * @return HTTP exception */ public HTTPException get(final Object... ext) { return new HTTPException(this, ext); } } basex-8.5.1/basex-api/src/main/java/org/basex/http/HTTPContext.java000066400000000000000000000412171274071665100250110ustar00rootroot00000000000000package org.basex.http; import static javax.servlet.http.HttpServletResponse.*; import static org.basex.http.HTTPText.*; import static org.basex.util.Token.*; import static org.basex.util.http.HttpText.*; import java.io.*; import java.net.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; import org.basex.*; import org.basex.core.*; import org.basex.core.StaticOptions.AuthMethod; import org.basex.core.users.*; import org.basex.io.*; import org.basex.io.out.*; import org.basex.io.serial.*; import org.basex.server.Log.LogType; import org.basex.server.*; import org.basex.util.*; import org.basex.util.http.*; import org.basex.util.http.HttpText.Request; /** * Bundles context-based information on a single HTTP operation. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ public final class HTTPContext { /** Global static database context. */ private static Context context; /** Initialization flag. */ private static boolean init; /** Initialized failed. */ private static IOException exception; /** Server instance. */ private static BaseXServer server; /** Servlet request. */ public final HttpServletRequest req; /** Servlet response. */ public final HttpServletResponse res; /** Request method. */ public final String method; /** Request method. */ public final HTTPParams params; /** Authentication method. */ public final AuthMethod auth; /** User name. */ public String username; /** Password (plain text). */ public String password; /** Performance. */ private final Performance perf = new Performance(); /** Path, starting with a slash. */ private final String path; /** Client database context. */ private Context ctx; /** Serialization parameters. */ private SerializerOptions sopts; /** * Constructor. * @param req request * @param res response * @param servlet calling servlet instance */ HTTPContext(final HttpServletRequest req, final HttpServletResponse res, final BaseXServlet servlet) { this.req = req; this.res = res; params = new HTTPParams(this); method = req.getMethod(); final StringBuilder uri = new StringBuilder(req.getRequestURL()); final String qs = req.getQueryString(); if(qs != null) uri.append('?').append(qs); context.log.write(address(), context.user(), LogType.REQUEST, '[' + method + "] " + uri, null); // set UTF8 as default encoding (can be overwritten) res.setCharacterEncoding(Strings.UTF8); path = decode(normalize(req.getPathInfo())); final StaticOptions mprop = context.soptions; if(servlet.username.isEmpty()) { // adopt existing servlet-specific credentials username = mprop.get(StaticOptions.USER); password = mprop.get(StaticOptions.PASSWORD); } else { // otherwise, adopt global credentials username = servlet.username; password = servlet.password; } // prefer safest authorization method final String value = req.getHeader(AUTHORIZATION); final String am = value == null ? AuthMethod.BASIC.toString() : Strings.split(value, ' ', 2)[0]; auth = StaticOptions.AUTHMETHOD.get(am) == AuthMethod.DIGEST ? AuthMethod.DIGEST : servlet.auth != null ? servlet.auth : mprop.get(StaticOptions.AUTHMETHOD); } /** * Authorizes a request. * @throws BaseXException database exception */ void authorize() throws BaseXException { final String value = req.getHeader(AUTHORIZATION); if(value == null) return; // overwrite credentials with client data (basic or digest) final String[] ams = Strings.split(value, ' ', 2); final AuthMethod am = StaticOptions.AUTHMETHOD.get(ams[0]); if(am == AuthMethod.BASIC) { final String details = ams.length > 1 ? ams[1] : ""; final String[] cred = Strings.split(org.basex.util.Base64.decode(details), ':', 2); if(cred.length != 2) throw new BaseXException(NOUSERNAME); username = cred[0]; password = cred[1]; } else if(am == AuthMethod.DIGEST) { final EnumMap map = HttpClient.digestHeaders(value); username = map.get(Request.USERNAME); password = map.get(Request.RESPONSE); } else { // custom authorization } } /** * Returns the content type of a request, or an empty string. * @return content type */ public MediaType contentType() { final String ct = req.getContentType(); return new MediaType(ct == null ? "" : ct); } /** * Initializes the output. Sets the expected encoding and content type. */ public void initResponse() { // set content type and encoding final SerializerOptions opts = sopts(); final String enc = opts.get(SerializerOptions.ENCODING); res.setCharacterEncoding(enc); res.setContentType(new MediaType(mediaType(opts) + "; " + CHARSET + '=' + enc).toString()); } /** * Returns the media type defined in the specified serialization parameters. * @param sopts serialization parameters * @return media type */ public static MediaType mediaType(final SerializerOptions sopts) { // set content type final String type = sopts.get(SerializerOptions.MEDIA_TYPE); if(!type.isEmpty()) return new MediaType(type); // determine content type dependent on output method final SerialMethod sm = sopts.get(SerializerOptions.METHOD); if(sm == SerialMethod.BASEX || sm == SerialMethod.ADAPTIVE || sm == SerialMethod.XML) return MediaType.APPLICATION_XML; if(sm == SerialMethod.XHTML || sm == SerialMethod.HTML) return MediaType.TEXT_HTML; if(sm == SerialMethod.JSON) return MediaType.APPLICATION_JSON; return MediaType.TEXT_PLAIN; } /** * Returns the URL path. The path always starts with a slash. * @return path path */ public String path() { return path; } /** * Returns the database path (i.e., all path entries except for the first). * @return database path */ public String dbpath() { final int s = path.indexOf('/', 1); return s == -1 ? "" : path.substring(s + 1); } /** * Returns the addressed database (i.e., the first path entry). * @return database, or {@code null} if the root directory was specified. */ public String db() { final int s = path.indexOf('/', 1); return path.substring(1, s == -1 ? path.length() : s); } /** * Returns all accepted media types. * @return accepted media types */ public MediaType[] accepts() { final String accept = req.getHeader(ACCEPT); final ArrayList list = new ArrayList<>(); if(accept == null) { list.add(MediaType.ALL_ALL); } else { for(final String produce : accept.split("\\s*,\\s*")) { // check if quality factor was specified final MediaType type = new MediaType(produce); final String qf = type.parameters().get("q"); final double d = qf != null ? toDouble(token(qf)) : 1; // only accept media types with valid double values if(d > 0 && d <= 1) { final StringBuilder sb = new StringBuilder(); final String main = type.main(), sub = type.sub(); sb.append(main.isEmpty() ? "*" : main).append('/'); sb.append(sub.isEmpty() ? "*" : sub).append("; q=").append(d); list.add(new MediaType(sb.toString())); } } } return list.toArray(new MediaType[list.size()]); } /** * Sets a status and sends an info message. * @param code status code * @param info info message (can be {@code null}) * @param error treat as error (use web server standard output) * @throws IOException I/O exception */ public void status(final int code, final String info, final boolean error) throws IOException { try { log(code, info); res.resetBuffer(); if(code == SC_UNAUTHORIZED) { final TokenBuilder header = new TokenBuilder(auth.toString()); final String nonce = Strings.md5(Long.toString(System.nanoTime())); if(auth == AuthMethod.DIGEST) { header.add(" "); header.addExt(Request.REALM).add("=\"").add(Prop.NAME).add("\","); header.addExt(Request.QOP).add("=\"").add(AUTH).add(',').add(AUTH_INT).add("\","); header.addExt(Request.NONCE).add("=\"").add(nonce).add('"'); } res.setHeader(WWW_AUTHENTICATE, header.toString()); } if(error && code >= SC_BAD_REQUEST) { res.sendError(code, info); } else { res.setStatus(code); if(info != null) { res.setContentType(MediaType.TEXT_PLAIN.toString()); try(final ArrayOutput ao = new ArrayOutput()) { ao.write(token(info)); res.getOutputStream().write(ao.normalize().finish()); } } } } catch(final IllegalStateException ex) { log(SC_INTERNAL_SERVER_ERROR, Util.message(ex)); } } /** * Updates the credentials. * @param user user * @param pass password */ public void credentials(final String user, final String pass) { username = user; password = pass; } /** * Returns the client database context. Authenticates the user if necessary. * @param authenticate authenticate user * @return client database context * @throws IOException I/O exception */ public Context context(final boolean authenticate) throws IOException { if(ctx == null) { ctx = new Context(context); ctx.user(authenticate ? authenticate() : context.users.get(UserText.ADMIN)); } return ctx; } /** * Authenticates the user and returns a new client {@link Context} instance. * @return user * @throws IOException I/O exception */ private User authenticate() throws IOException { final byte[] address = token(req.getRemoteAddr()); try { if(username == null || username.isEmpty()) throw new LoginException(NOUSERNAME); final User us = context.users.get(username); if(us == null) throw new LoginException(); if(auth == AuthMethod.BASIC) { if(password == null || !us.matches(password)) throw new LoginException(); } else if(auth == AuthMethod.DIGEST) { final EnumMap map = HttpClient.digestHeaders(req.getHeader(AUTHORIZATION)); final String am = map.get(Request.AUTH_METHOD); if(!AuthMethod.DIGEST.toString().equals(am)) throw new LoginException(DIGESTAUTH); final String nonce = map.get(Request.NONCE), cnonce = map.get(Request.CNONCE); String ha1 = us.code(Algorithm.DIGEST, Code.HASH); if(Strings.eq(map.get(Request.ALGORITHM), MD5_SESS)) ha1 = Strings.md5(ha1 + ':' + nonce + ':' + cnonce); String h2 = method + ':' + map.get(Request.URI); final String qop = map.get(Request.QOP); if(Strings.eq(qop, AUTH_INT)) h2 += ':' + Strings.md5(params.body().toString()); final String ha2 = Strings.md5(h2); final StringBuilder rsp = new StringBuilder(ha1).append(':').append(nonce); if(Strings.eq(qop, AUTH, AUTH_INT)) { rsp.append(':').append(map.get(Request.NC)); rsp.append(':').append(cnonce); rsp.append(':').append(qop); } rsp.append(':').append(ha2); if(!Strings.md5(rsp.toString()).equals(password)) throw new LoginException(); } else { // custom authorization } context.blocker.remove(address); return us; } catch(final LoginException ex) { // delay users with wrong passwords context.blocker.delay(address); throw ex; } } /** * Returns an exception that may have been caught by the initialization of the database server. * @return exception */ public static IOException exception() { return exception; } /** * Assigns serialization parameters. * @param opts serialization parameters. */ public void sopts(final SerializerOptions opts) { sopts = opts; } /** * Returns the serialization parameters. * @return serialization parameters. */ public SerializerOptions sopts() { if(sopts == null) sopts = new SerializerOptions(); return sopts; } /** * Writes a log message. * @param type log type * @param info info string (can be {@code null}) */ void log(final int type, final String info) { context.log.write(address(), context.user(), type, info, perf); } /** * Sends a redirect. * @param location location * @throws IOException I/O exception */ public void redirect(final String location) throws IOException { res.sendRedirect(resolve(location)); } /** * Normalizes a redirection location. Prefixes absolute locations with the request URI. * @param location location * @return normalized representation */ public String resolve(final String location) { String loc = location; if(location.startsWith("/")) { final String uri = req.getRequestURI(), info = req.getPathInfo(); if(info == null) { loc = uri + location; } else { loc = uri.substring(0, uri.length() - info.length()) + location; } } return loc; } /** * Sends a forward. * @param location location * @throws IOException I/O exception * @throws ServletException servlet exception */ public void forward(final String location) throws IOException, ServletException { req.getRequestDispatcher(resolve(location)).forward(req, res); } // STATIC METHODS ===================================================================== /** * Initializes the HTTP context. * @return context; */ public static synchronized Context init() { if(context == null) context = new Context(); return context; } /** * Initializes the database context, based on the initial servlet context. * Parses all context parameters and passes them on to the database context. * @param sc servlet context * @throws IOException I/O exception */ public static synchronized void init(final ServletContext sc) throws IOException { // check if HTTP context has already been initialized if(init) return; init = true; final String webapp = sc.getRealPath("/"); // system property (requested in Prop#homePath) System.setProperty(Prop.PATH, webapp); // global option (will later be assigned to StaticOptions#WEBPATH) Prop.put(StaticOptions.WEBPATH, webapp); // set all parameters that start with "org.basex." as global options final Enumeration en = sc.getInitParameterNames(); while(en.hasMoreElements()) { final String key = en.nextElement(); String val = sc.getInitParameter(key); if(key.startsWith(Prop.DBPREFIX) && key.endsWith("path") && !new File(val).isAbsolute()) { // prefix relative path with absolute servlet path Util.debug(key.toUpperCase(Locale.ENGLISH) + ": " + val); val = new IOFile(webapp, val).path(); } Prop.put(key, val); } // create context, update options if(context == null) { context = new Context(false); } else { context.soptions.setSystem(); context.options.setSystem(); } // start server instance if(!context.soptions.get(StaticOptions.HTTPLOCAL)) { try { server = new BaseXServer(context, "-D"); } catch(final IOException ex) { exception = ex; throw ex; } } } /** * Closes the database context. */ static synchronized void close() { if(server != null) { try { server.stop(); } catch(final IOException ex) { Util.stack(ex); } server = null; } context.close(); } /** * Decodes the specified path. * @param path strings to be decoded * @return argument */ public static String decode(final String path) { try { return URLDecoder.decode(path, Prop.ENCODING); } catch(final UnsupportedEncodingException | IllegalArgumentException ex) { return path; } } // PRIVATE METHODS ==================================================================== /** * Normalizes the specified path. * @param path path, or {@code null} * @return normalized path */ private static String normalize(final String path) { final TokenBuilder tmp = new TokenBuilder(); if(path != null) { final TokenBuilder tb = new TokenBuilder(); final int pl = path.length(); for(int p = 0; p < pl; p++) { final char ch = path.charAt(p); if(ch == '/') { if(tb.isEmpty()) continue; tmp.add('/').add(tb.toArray()); tb.reset(); } else { tb.add(ch); } } if(!tb.isEmpty()) tmp.add('/').add(tb.finish()); } if(tmp.isEmpty()) tmp.add('/'); return tmp.toString(); } /** * Returns a string with the remote user address. * @return user address */ private String address() { return req.getRemoteAddr() + ':' + req.getRemotePort(); } } basex-8.5.1/basex-api/src/main/java/org/basex/http/HTTPException.java000066400000000000000000000012771274071665100253250ustar00rootroot00000000000000package org.basex.http; import java.io.*; import org.basex.util.*; /** * HTTP exception. Also thrown to pass on correct status codes. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ public final class HTTPException extends IOException { /** Status code. */ private final int status; /** * Constructs an exception with the specified message and extension. * @param err error * @param ext message extension */ HTTPException(final HTTPCode err, final Object... ext) { super(Util.info(err.desc, ext)); status = err.code; } /** * Returns the status code. * @return status code */ public int getStatus() { return status; } } basex-8.5.1/basex-api/src/main/java/org/basex/http/HTTPParams.java000066400000000000000000000101711274071665100246030ustar00rootroot00000000000000package org.basex.http; import java.io.*; import java.net.*; import java.util.*; import java.util.Map.Entry; import org.basex.core.*; import org.basex.io.*; import org.basex.io.in.*; import org.basex.query.*; import org.basex.query.value.*; import org.basex.query.value.item.*; import org.basex.util.*; import org.basex.util.http.*; /** * Bundles parameters of an HTTP request. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ public final class HTTPParams { /** HTTP Context. */ private final HTTPContext http; /** Parameter map. */ private Map map; /** Query parameters. */ private Map query; /** Form parameters. */ private Map form; /** Content body. */ private IOContent content; /** * Returns an immutable map with all query parameters. * @param http HTTP context */ HTTPParams(final HTTPContext http) { this.http = http; } /** * Returns the query parameters as map. * @return map * @throws IOException I/O exception */ public Map map() throws IOException { try { if(map == null) map = http.req.getParameterMap(); return map; } catch(final IllegalStateException ex) { // may be caused by too large input (#884) throw new IOException(ex); } } /** * Binds form parameters. * @param options main options * @return parameters * @throws IOException I/O exception * @throws QueryException query exception */ public Map form(final MainOptions options) throws QueryException, IOException { if(form == null) { form = new HashMap<>(); final MediaType mt = http.contentType(); if(mt.is(MediaType.MULTIPART_FORM_DATA)) { // convert multipart parameters encoded in a form addMultipart(mt, options); } else if(mt.is(MediaType.APPLICATION_X_WWW_FORM_URLENCODED)) { // convert URL-encoded parameters addURLEncoded(); } } return form; } /** * Returns query parameters. * @return query parameters * @throws IOException I/O exception */ public Map query() throws IOException { if(query == null) { query = new HashMap<>(); for(final Entry entry : map().entrySet()) { final String key = entry.getKey(); final String[] values = entry.getValue(); final ValueBuilder vb = new ValueBuilder(); for(final String v : values) vb.add(new Atm(v)); query.put(key, vb.value()); } } return query; } /** * Returns the cached body. * @return value * @throws IOException I/O exception */ public IOContent body() throws IOException { if(content == null) { content = new IOContent(new BufferInput(http.req.getInputStream()).content()); } return content; } // PRIVATE FUNCTIONS ============================================================================ /** * Adds multipart form-data from the passed on request body. * @param type media type * @param options main options * @throws QueryException query exception * @throws IOException I/O exception */ private void addMultipart(final MediaType type, final MainOptions options) throws QueryException, IOException { try(final InputStream is = body().inputStream()) { final HttpPayload hp = new HttpPayload(is, true, null, options); final HashMap mp = hp.multiForm(type); for(final Entry entry : mp.entrySet()) { form.put(entry.getKey(), entry.getValue()); } } } /** * Adds URL-encoded parameters from the passed on request body. * @throws IOException I/O exception */ private void addURLEncoded() throws IOException { for(final String nv : Strings.split(body().toString(), '&')) { final String[] parts = Strings.split(nv, '=', 2); if(parts.length == 2) { final Atm i = new Atm(URLDecoder.decode(parts[1], Strings.UTF8)); final String k = parts[0]; final Value v = form.get(k); form.put(k, v == null ? i : new ValueBuilder().add(v).add(i).value()); } } } } basex-8.5.1/basex-api/src/main/java/org/basex/http/HTTPText.java000066400000000000000000000013251274071665100243050ustar00rootroot00000000000000package org.basex.http; /** * This class assembles texts which are used in the HTTP classes. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ public interface HTTPText { /** HTTP String. */ String HTTP = "HTTP"; /** WEB-INF directory. */ String WEB_INF = "WEB-INF/"; /** Path to jetty configuration file. */ String JETTYCONF = WEB_INF + "jetty.xml"; /** Path to web configuration file. */ String WEBCONF = WEB_INF + "web.xml"; /** Error: credentials missing. */ String NOUSERNAME = "No username specified."; /** Error: digest authorization. */ String DIGESTAUTH = "Digest authentication expected."; /** Error message. */ String UNEXPECTED = "Unexpected error: %"; } basex-8.5.1/basex-api/src/main/java/org/basex/http/ServletListener.java000066400000000000000000000007011274071665100260100ustar00rootroot00000000000000package org.basex.http; import javax.servlet.*; /** * This class creates and destroys servlet contexts. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ public final class ServletListener implements ServletContextListener { @Override public void contextInitialized(final ServletContextEvent event) { } @Override public void contextDestroyed(final ServletContextEvent event) { HTTPContext.close(); } } basex-8.5.1/basex-api/src/main/java/org/basex/http/SessionListener.java000066400000000000000000000015461274071665100260170ustar00rootroot00000000000000package org.basex.http; import java.util.*; import javax.servlet.http.*; /** * This class creates and destroys HTTP sessions. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ public final class SessionListener implements HttpSessionListener { /** Sessions. */ private static HashMap sessions; @Override public void sessionCreated(final HttpSessionEvent event) { final HttpSession sess = event.getSession(); sessions().put(sess.getId(), sess); } @Override public void sessionDestroyed(final HttpSessionEvent event) { sessions().remove(event.getSession().getId()); } /** * Initializes the HTTP context. * @return context; */ public static synchronized HashMap sessions() { if(sessions == null) sessions = new HashMap<>(); return sessions; } } basex-8.5.1/basex-api/src/main/java/org/basex/http/rest/000077500000000000000000000000001274071665100227725ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/java/org/basex/http/rest/RESTCmd.java000066400000000000000000000111531274071665100250370ustar00rootroot00000000000000package org.basex.http.rest; import static org.basex.util.Token.*; import java.io.*; import java.util.*; import java.util.Map.Entry; import org.basex.core.*; import org.basex.core.cmd.*; import org.basex.core.locks.*; import org.basex.core.users.*; import org.basex.http.*; import org.basex.io.out.*; import org.basex.query.value.item.*; import org.basex.query.value.node.*; import org.basex.util.*; import org.basex.util.list.*; /** * Abstract class for performing REST operations. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ abstract class RESTCmd extends Command { /** REST session. */ final RESTSession session; /** Commands. */ final ArrayList cmds; /** Return code (may be {@code null}). */ HTTPCode code; /** * Constructor. * @param session REST session */ RESTCmd(final RESTSession session) { super(max(session.cmds)); this.session = session; cmds = session.cmds; job().type(RESTText.REST); } @Override public void databases(final LockResult lr) { for(final Command cmd : cmds) { // collect local locks and merge it with global lock list final LockResult tmp = new LockResult(); cmd.databases(tmp); lr.union(tmp); } } @Override public boolean updating(final Context ctx) { boolean up = false; for(final Command cmd : cmds) up |= cmd.updating(ctx); return up; } @Override protected final boolean run() { try { run0(); return true; } catch(final IOException ex) { return error(ex.getMessage()); } finally { new Close().run(context); } } /** * Runs the command. * @throws IOException I/O exception */ protected abstract void run0() throws IOException; /** * Runs the specified command. * @param cmd command * @return string result * @throws HTTPException HTTP exception */ final String run(final Command cmd) throws HTTPException { final ArrayOutput ao = new ArrayOutput(); run(cmd, ao); return ao.toString(); } /** * Runs the specified command. * @param cmd command * @param os output stream * @throws HTTPException HTTP exception */ final void run(final Command cmd, final OutputStream os) throws HTTPException { try { final boolean ok = pushJob(cmd).run(context, os); error(cmd.info()); if(!ok) throw HTTPCode.BAD_REQUEST_X.get(cmd.info()); } finally { popJob(); } } /** * Lists the table contents. * @param table table reference * @param root root node * @param header table header * @param skip number of columns to skip */ static void list(final Table table, final FElem root, final QNm header, final int skip) { for(final TokenList list : table.contents) { final FElem el = new FElem(header); // don't show last attribute (input path) final int ll = list.size() - skip; for(int l = 1; l < ll; l++) { el.add(new QNm(lc(table.header.get(l))), list.get(l)); } el.add(list.get(0)); root.add(el); } } /** * Adds a command or opening the addressed database. * @param session REST session */ static void open(final RESTSession session) { final String db = session.http.db(); if(!db.isEmpty()) session.add(new Open(db, session.http.dbpath())); } /** * Returns the maximum permission from the specified commands. * @param cmds commands to be checked * @return permission */ private static Perm max(final ArrayList cmds) { Perm p = Perm.NONE; for(final Command cmd : cmds) p = p.max(cmd.perm); return p; } /** * Parses and sets database options. * Throws an exception if an option is unknown. * @param session REST session * @throws IOException I/O exception */ static void parseOptions(final RESTSession session) throws IOException { for(final Entry param : session.http.params.map().entrySet()) parseOption(session, param, true); } /** * Parses and sets a single database option. * @param session REST session * @param param current parameter * @param force force execution * @return success flag, indicates if value was found * @throws BaseXException database exception */ static boolean parseOption(final RESTSession session, final Entry param, final boolean force) throws BaseXException { final String key = param.getKey().toUpperCase(Locale.ENGLISH); final MainOptions options = session.context.options; final boolean found = options.option(key) != null; if(found || force) options.assign(key, param.getValue()[0]); return found; } } basex-8.5.1/basex-api/src/main/java/org/basex/http/rest/RESTCommand.java000066400000000000000000000024441274071665100257150ustar00rootroot00000000000000package org.basex.http.rest; import java.io.*; import org.basex.core.*; import org.basex.core.parse.*; import org.basex.http.*; import org.basex.io.serial.*; import org.basex.query.*; /** * REST-based evaluation of database commands. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class RESTCommand extends RESTCmd { /** * Constructor. * @param session REST session */ private RESTCommand(final RESTSession session) { super(session); } @Override protected void run0() throws IOException { // set content type to text final HTTPContext http = session.http; http.sopts().set(SerializerOptions.METHOD, SerialMethod.TEXT); http.initResponse(); for(final Command cmd : cmds) run(cmd, http.res.getOutputStream()); } /** * Creates a new instance of this command. * @param session REST session * @param input string input * @return command * @throws BaseXException database exception */ static RESTCommand get(final RESTSession session, final String input) throws BaseXException { try { open(session); session.add(new CommandParser(input, session.context).parseSingle()); return new RESTCommand(session); } catch(final QueryException ex) { throw new BaseXException(ex); } } } basex-8.5.1/basex-api/src/main/java/org/basex/http/rest/RESTDelete.java000066400000000000000000000016571274071665100255460ustar00rootroot00000000000000package org.basex.http.rest; import java.io.*; import org.basex.core.cmd.*; import org.basex.http.*; /** * REST-based evaluation of DELETE operations. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class RESTDelete { /** Private constructor. */ private RESTDelete() { } /** * Creates a new instance of this command. * @param session REST session * @return command * @throws IOException I/O exception */ static RESTExec get(final RESTSession session) throws IOException { RESTCmd.parseOptions(session); final HTTPContext http = session.http; final String db = http.db(); if(db.isEmpty()) throw HTTPCode.NO_PATH.get(); // open database to ensure it exists session.add(new Open(db)); final String path = http.dbpath(); if(path.isEmpty()) session.add(new DropDB(db)); else session.add(new Delete(path)); return new RESTExec(session); } } basex-8.5.1/basex-api/src/main/java/org/basex/http/rest/RESTExec.java000066400000000000000000000011421274071665100252150ustar00rootroot00000000000000package org.basex.http.rest; import java.io.*; import org.basex.core.*; import org.basex.util.*; /** * Executes a simple REST operation. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class RESTExec extends RESTCmd { /** * Constructor. * @param session REST session */ RESTExec(final RESTSession session) { super(session); } @Override protected void run0() throws IOException { // execute command and return info of last command for(final Command c : cmds) run(c); session.http.res.getOutputStream().write(Token.token(info())); } } basex-8.5.1/basex-api/src/main/java/org/basex/http/rest/RESTGet.java000066400000000000000000000035541274071665100250610ustar00rootroot00000000000000package org.basex.http.rest; import static org.basex.http.rest.RESTText.*; import java.io.*; import java.util.*; import java.util.Map.Entry; import org.basex.http.*; import org.basex.io.serial.*; import org.basex.util.*; /** * This class processes GET requests sent to the REST server. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class RESTGet { /** Private constructor. */ private RESTGet() { } /** * Creates REST code. * @param session REST session * @return code * @throws IOException I/O exception */ public static RESTCmd get(final RESTSession session) throws IOException { final Map vars = new HashMap<>(); // parse query string String op = null, input = null, value = null; final HTTPContext http = session.http; final SerializerOptions sopts = http.sopts(); for(final Entry param : http.params.map().entrySet()) { final String key = param.getKey(); final String[] vals = param.getValue(); final String val = vals[0]; if(Strings.eqic(key, COMMAND, QUERY, RUN)) { if(op != null || vals.length > 1) throw HTTPCode.ONEOP.get(); op = key; input = val; } else if(key.equalsIgnoreCase(CONTEXT)) { // context parameter value = val; } else if(sopts.option(key) != null) { // serialization parameters for(final String v : vals) sopts.assign(key, v); } else if(!RESTCmd.parseOption(session, param, false)) { // options or (if not found) external variables vars.put(key, new String[] { val }); } } if(op == null) return RESTRetrieve.get(session); if(op.equals(QUERY)) return RESTQuery.get(session, input, vars, value); if(op.equals(RUN)) return RESTRun.get(session, input, vars, value); return RESTCommand.get(session, input); } } basex-8.5.1/basex-api/src/main/java/org/basex/http/rest/RESTList.java000066400000000000000000000017311274071665100252500ustar00rootroot00000000000000package org.basex.http.rest; import static org.basex.util.Token.*; import java.io.*; import org.basex.http.*; import org.basex.io.serial.*; import org.basex.query.value.node.*; import org.basex.util.*; /** * Lists REST resources. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class RESTList extends RESTCmd { /** * Constructor. * @param session REST session */ RESTList(final RESTSession session) { super(session); } @Override protected void run0() throws IOException { // list all databases final Table table = new Table(run(cmds.get(0))); final FElem el = new FElem(RESTText.Q_DATABASES).declareNS(); el.add(RESTText.RESOURCES, token(table.contents.size())); list(table, el, RESTText.Q_DATABASE, 1); final HTTPContext http = session.http; http.initResponse(); try(final Serializer ser = Serializer.get(http.res.getOutputStream(), http.sopts())) { ser.serialize(el); } } } basex-8.5.1/basex-api/src/main/java/org/basex/http/rest/RESTPost.java000066400000000000000000000113711274071665100252630ustar00rootroot00000000000000package org.basex.http.rest; import static org.basex.http.rest.RESTText.*; import java.io.*; import java.util.*; import javax.xml.parsers.*; import javax.xml.transform.dom.*; import org.basex.core.*; import org.basex.http.*; import org.basex.io.*; import org.basex.io.in.*; import org.basex.io.serial.*; import org.basex.query.*; import org.basex.query.util.*; import org.basex.query.value.item.*; import org.basex.query.value.node.*; import org.basex.util.*; /** * REST-based evaluation of POST operations. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class RESTPost { /** Private constructor. */ private RESTPost() { } /** * Creates REST code. * @param session REST session * @return code * @throws IOException I/O exception */ public static RESTCmd get(final RESTSession session) throws IOException { final HTTPContext http = session.http; String enc = http.req.getCharacterEncoding(); if(enc == null) enc = Strings.UTF8; // perform queries final byte[] input = new NewlineInput(http.req.getInputStream()).encoding(enc).content(); validate(input); final Context ctx = session.context; final DBNode doc; try { doc = new DBNode(new IOContent(input)); } catch(final IOException ex) { throw HTTPCode.BAD_REQUEST_X.get(ex); } try { // handle serialization parameters final SerializerOptions sopts = http.sopts(); try(final QueryProcessor qp = new QueryProcessor("*/*:parameter", ctx).context(doc)) { for(final Item param : qp.value()) { final String name = value("@name", param, ctx); final String value = value("@value", param, ctx); if(sopts.option(name) != null) { sopts.assign(name, value); } else { throw HTTPCode.UNKNOWN_PARAM_X.get(name); } } } // handle database options try(final QueryProcessor qp = new QueryProcessor("*/*:option", ctx).context(doc)) { for(final Item it : qp.value()) { final String name = value("@name", it, ctx).toUpperCase(Locale.ENGLISH); final String value = value("@value", it, ctx); ctx.options.assign(name, value); } } // handle variables final Map vars = new HashMap<>(); try(final QueryProcessor qp = new QueryProcessor("*/*:variable", ctx).context(doc)) { for(final Item it : qp.value()) { final String name = value("@name", it, ctx); final String value = value("@value", it, ctx); final String type = value("@type", it, ctx); vars.put(name, new String[] { value, type }); } } // handle input String val = null; try(final QueryProcessor qp = new QueryProcessor( "*/*:context/(*, text()[normalize-space()])", ctx).context(doc)) { for(final Item it : qp.value()) { if(val != null) throw HTTPCode.MULTIPLE_CONTEXT_X.get(); // create main memory instance of the specified node val = DataBuilder.stripNS((ANode) it, REST_URI, ctx).serialize().toString(); } } // handle request final String request = value("local-name(*)", doc, ctx); final String text = value("*/*:text/text()", doc, ctx); if(request.equals(COMMAND)) return RESTCommand.get(session, text); if(request.equals(RUN)) return RESTRun.get(session, text, vars, val); return RESTQuery.get(session, text, vars, val); } catch(final QueryException ex) { throw HTTPCode.BAD_REQUEST_X.get(ex); } } /** * Returns the atomized item for the specified query. * @param query query * @param value context value * @param ctx database context * @return atomized item * @throws QueryException query exception */ private static String value(final String query, final Item value, final Context ctx) throws QueryException { try(final QueryProcessor qp = new QueryProcessor(query, ctx).context(value)) { final Item it = qp.iter().next(); return it == null ? null : Token.string(it.string(null)); } } /** * Validates the specified XML input against the POST schema. * @param input input document * @throws HTTPException exception */ private static void validate(final byte[] input) throws HTTPException { try { final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); final DocumentBuilder db = dbf.newDocumentBuilder(); RESTSchema.newValidator().validate(new DOMSource(db.parse(new ArrayInput(input)))); } catch(final Exception ex) { Util.debug("Error while validating \"" + Token.string(input) + '"'); // validation fails throw HTTPCode.BAD_REQUEST_X.get(ex); } } } basex-8.5.1/basex-api/src/main/java/org/basex/http/rest/RESTPut.java000066400000000000000000000053121274071665100251040ustar00rootroot00000000000000package org.basex.http.rest; import java.io.*; import org.basex.build.csv.*; import org.basex.build.html.*; import org.basex.build.json.*; import org.basex.build.text.*; import org.basex.core.*; import org.basex.core.MainOptions.MainParser; import org.basex.core.cmd.*; import org.basex.http.*; import org.basex.util.http.*; /** * REST-based evaluation of PUT operations. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class RESTPut { /** Private constructor. */ private RESTPut() { } /** * Creates REST code. * @param session REST session * @return code * @throws IOException I/O exception */ public static RESTExec get(final RESTSession session) throws IOException { // create new database or update resource final HTTPContext http = session.http; final String db = http.db(); if(db.isEmpty()) throw HTTPCode.NO_PATH.get(); RESTCmd.parseOptions(session); final MainOptions options = session.context.options; final InputStream is = http.req.getInputStream(); final MediaType mt = http.contentType(); // choose correct importer boolean xml = true; final String ct = mt.type(); if(mt.is(MediaType.APPLICATION_JSON)) { final JsonParserOptions opts = new JsonParserOptions(); opts.assign(mt); options.set(MainOptions.JSONPARSER, opts); options.set(MainOptions.PARSER, MainParser.JSON); } else if(mt.is(MediaType.TEXT_CSV)) { final CsvParserOptions opts = new CsvParserOptions(); opts.assign(mt); options.set(MainOptions.CSVPARSER, opts); options.set(MainOptions.PARSER, MainParser.CSV); } else if(mt.is(MediaType.TEXT_HTML)) { final HtmlOptions opts = new HtmlOptions(); opts.assign(mt); options.set(MainOptions.HTMLPARSER, opts); options.set(MainOptions.PARSER, MainParser.HTML); } else if(mt.isText()) { final TextOptions opts = new TextOptions(); opts.assign(mt); options.set(MainOptions.TEXTPARSER, opts); options.set(MainOptions.PARSER, MainParser.TEXT); } else if(!ct.isEmpty() && !mt.isXML()) { xml = false; } // store data as XML or raw file, depending on content type final String path = http.dbpath(); if(path.isEmpty()) { if(xml) { session.add(new CreateDB(db), is); } else { session.add(new CreateDB(db)); session.add(new Store(db), is); } } else { session.add(new Open(db)); if(xml) { session.add(new Replace(path), is); } else { session.add(new Delete(path)); session.add(new Store(path), is); } } final RESTExec cmd = new RESTExec(session); cmd.code = HTTPCode.CREATED_X; return cmd; } } basex-8.5.1/basex-api/src/main/java/org/basex/http/rest/RESTQuery.java000066400000000000000000000052131274071665100254410ustar00rootroot00000000000000package org.basex.http.rest; import java.io.*; import java.util.*; import java.util.Map.Entry; import org.basex.core.*; import org.basex.core.cmd.*; import org.basex.http.*; import org.basex.query.value.type.*; /** * Evaluate queries via REST. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ class RESTQuery extends RESTCmd { /** External variables. */ private final Map vars; /** Optional context value. */ private final String value; /** * Constructor. * @param session REST Session * @param vars external variables * @param value context value */ RESTQuery(final RESTSession session, final Map vars, final String value) { super(session); this.vars = vars; this.value = value; } @Override protected void run0() throws IOException { query(session.context.soptions.get(StaticOptions.WEBPATH)); } /** * Evaluates the specified query. * @param path query path * @throws HTTPException REST exception * @throws IOException I/O exception */ final void query(final String path) throws IOException { // set base path and serialization parameters final HTTPContext http = session.http; context.options.set(MainOptions.QUERYPATH, path); context.options.set(MainOptions.SERIALIZER, http.sopts()); http.initResponse(); for(final Command cmd : cmds) { if(cmd instanceof XQuery) { final XQuery xq = (XQuery) cmd; // create query instance if(value != null) xq.bind(null, value, NodeType.DOC.toString()); // bind HTTP context and external variables xq.http(http); for(final Entry e : vars.entrySet()) { final String key = e.getKey(); final String[] val = e.getValue(); if(val.length == 2) xq.bind(key, val[0], val[1]); if(val.length == 1) xq.bind(key, val[0]); } // initializes the response with query serialization options http.sopts().assign(xq.parameters(context)); http.initResponse(); } // run command run(cmd, http.res.getOutputStream()); } } /** * Creates a new instance of this command. * @param session REST session * @param query query * @param vars external variables * @param val context value * @return command * @throws IOException I/O exception */ @SuppressWarnings("unused") static RESTQuery get(final RESTSession session, final String query, final Map vars, final String val) throws IOException { open(session); session.add(new XQuery(query)); return new RESTQuery(session, vars, val); } } basex-8.5.1/basex-api/src/main/java/org/basex/http/rest/RESTRetrieve.java000066400000000000000000000046751274071665100261340ustar00rootroot00000000000000package org.basex.http.rest; import static org.basex.query.func.Function.*; import static org.basex.util.Token.*; import java.io.*; import org.basex.core.*; import org.basex.core.cmd.*; import org.basex.http.*; import org.basex.io.serial.*; import org.basex.query.func.*; import org.basex.query.value.node.*; import org.basex.util.*; /** * Retrieve resources via REST. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class RESTRetrieve extends RESTCmd { /** * Constructor. * @param session REST session */ private RESTRetrieve(final RESTSession session) { super(session); } @Override protected void run0() throws IOException { // open addressed database for(final Command cmd : cmds) run(cmd); final HTTPContext http = session.http; final SerializerOptions sopts = http.sopts(); if(run(query(_DB_EXISTS)).equals(Text.TRUE)) { // return database resource final boolean raw = run(query(_DB_IS_RAW)).equals(Text.TRUE); if(raw) sopts.set(SerializerOptions.MEDIA_TYPE, run(query(_DB_CONTENT_TYPE))); http.initResponse(); context.options.set(MainOptions.SERIALIZER, sopts); run(query(raw ? _DB_RETRIEVE : _DB_OPEN), http.res.getOutputStream()); } else { // list database resources final Table table = new Table(run(new List(http.db(), http.dbpath()))); final FElem el = new FElem(RESTText.Q_DATABASE).declareNS(); el.add(RESTText.NAME, http.db()).add(RESTText.RESOURCES, token(table.contents.size())); list(table, el, RESTText.Q_RESOURCE, 0); http.initResponse(); try(final Serializer ser = Serializer.get(http.res.getOutputStream(), sopts)) { ser.serialize(el); } } } /** * Creates a query instance. * @param f function * @return query */ private XQuery query(final Function f) { final HTTPContext http = session.http; final String query = "declare variable $d external;" + "declare variable $p external;" + f.args("$d", "$p"); return new XQuery(query).bind("d", http.db()).bind("p", http.dbpath()); } /** * Creates a new instance of this command. * @param session REST session * @return command */ static RESTCmd get(final RESTSession session) { final HTTPContext http = session.http; final String db = http.db(); if(db.isEmpty()) return new RESTList(session.add(new List())); return new RESTRetrieve(session.add(new Open(db))); } } basex-8.5.1/basex-api/src/main/java/org/basex/http/rest/RESTRun.java000066400000000000000000000046241274071665100251050ustar00rootroot00000000000000package org.basex.http.rest; import static org.basex.core.Text.*; import java.io.*; import java.util.*; import org.basex.core.*; import org.basex.core.cmd.*; import org.basex.core.parse.*; import org.basex.http.*; import org.basex.io.*; import org.basex.query.*; import org.basex.util.*; /** * REST-based evaluation of XQuery files. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class RESTRun extends RESTQuery { /** Path. */ private final String path; /** * Constructor. * @param session REST session * @param vars external variables * @param val context value * @param path path to query file */ private RESTRun(final RESTSession session, final Map vars, final String val, final String path) { super(session, vars, val); this.path = path; } @Override protected void run0() throws IOException { query(path); } /** * Creates a new instance of this command. * @param session REST session * @param path relative path to query input * @param vars external variables * @param val context value * @return command * @throws IOException I/O exception */ static RESTQuery get(final RESTSession session, final String path, final Map vars, final String val) throws IOException { // get root directory for files final Context context = session.context; final String webpath = context.soptions.get(StaticOptions.WEBPATH); final String rpath = context.soptions.get(StaticOptions.RESTPATH); final IOFile root = new IOFile(webpath).resolve(rpath); // check if file is not found, is a folder or points to parent folder final IOFile file = new IOFile(root, path); if(!file.exists() || file.isDir() || !file.path().startsWith(root.path())) throw HTTPCode.NOT_FOUND_X.get(Util.info(RES_NOT_FOUND_X, path)); // retrieve file contents final String input = file.string(); // interpret as commands if input ends with command script suffix if(file.hasSuffix(IO.BXSSUFFIX)) { try { for(final Command cmd : new CommandParser(input, context).parse()) session.add(cmd); } catch(final QueryException ex) { throw new IOException(ex); } } else { // otherwise, interpret input as xquery session.add(new XQuery(input)); } // perform query return new RESTRun(session, vars, val, file.path()); } } basex-8.5.1/basex-api/src/main/java/org/basex/http/rest/RESTSchema.java000066400000000000000000000065631274071665100255450ustar00rootroot00000000000000package org.basex.http.rest; import javax.xml.*; import javax.xml.transform.stream.*; import javax.xml.validation.*; import org.basex.io.in.*; import org.basex.util.*; import org.xml.sax.*; /** * XML Schemas for REST requests. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ enum RESTSchema { /** Single instance. */ INSTANCE; /** Validation schema. */ private final Schema schema; /** Constructor. */ RESTSchema() { try { schema = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI). newSchema(new StreamSource(new ArrayInput(Token.token(SCHEMA_CONTENT)))); } catch(final SAXException ex) { throw Util.notExpected(ex); } } /** * Create a new validator against the schema. * @return a new validator */ static Validator newValidator() { return INSTANCE.schema.newValidator(); } /** Post Schema. */ private static final String SCHEMA_CONTENT = "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + ""; } basex-8.5.1/basex-api/src/main/java/org/basex/http/rest/RESTServlet.java000066400000000000000000000030621274071665100257600ustar00rootroot00000000000000package org.basex.http.rest; import java.io.*; import org.basex.core.*; import org.basex.core.cmd.*; import org.basex.http.*; import org.basex.util.http.*; /** *

This servlet receives and processes REST requests.

* * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ public final class RESTServlet extends BaseXServlet { @Override protected void run(final HTTPContext http) throws IOException { final RESTSession session = new RESTSession(http, http.context(true)); final RESTCmd cmd = code(session); try { cmd.execute(session.context); } catch(final BaseXException ex) { // catch "database not found" message final String msg = Open.dbnf(http.db()); if(ex.getMessage().equals(msg)) throw HTTPCode.NOT_FOUND_X.get(msg); throw ex; } final HTTPCode code = cmd.code; if(code != null) throw code.get(cmd.info()); } /** * Returns the correct code for the specified HTTP method, or an exception. * @param session session * @return code * @throws IOException I/O exception */ private static RESTCmd code(final RESTSession session) throws IOException { final String mth = session.http.method; if(mth.equals(HttpMethod.GET.name())) return RESTGet.get(session); if(mth.equals(HttpMethod.POST.name())) return RESTPost.get(session); if(mth.equals(HttpMethod.PUT.name())) return RESTPut.get(session); if(mth.equals(HttpMethod.DELETE.name())) return RESTDelete.get(session); throw HTTPCode.NOT_IMPLEMENTED_X.get(session.http.req.getMethod()); } } basex-8.5.1/basex-api/src/main/java/org/basex/http/rest/RESTSession.java000066400000000000000000000021411274071665100257540ustar00rootroot00000000000000package org.basex.http.rest; import java.io.*; import java.util.*; import org.basex.core.*; import org.basex.http.*; /** * REST session. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class RESTSession { /** HTTP context. */ final HTTPContext http; /** Commands to be executed. */ final ArrayList cmds = new ArrayList<>(); /** Client context. */ final Context context; /** * Constructor, specifying login data and an output stream. * @param http HTTP context * @param context client context */ RESTSession(final HTTPContext http, final Context context) { this.http = http; this.context = context; } /** * Adds a command to be executed. * @param cmd command * @return self reference */ RESTSession add(final Command cmd) { cmds.add(cmd); return this; } /** * Adds a command to be executed. * @param cmd command * @param is input stream * @return self reference */ RESTSession add(final Command cmd, final InputStream is) { cmds.add(cmd); cmd.setInput(is); return this; } } basex-8.5.1/basex-api/src/main/java/org/basex/http/rest/RESTText.java000066400000000000000000000017741274071665100252700ustar00rootroot00000000000000package org.basex.http.rest; import org.basex.query.*; import org.basex.query.value.item.*; import org.basex.util.*; /** * This class assembles texts which are used in the HTTP classes. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ interface RESTText { /** REST URI. */ byte[] REST_URI = new TokenBuilder(Prop.URL).add('/').add(QueryText.REST_PREFIX).finish(); /** Name. */ QNm Q_DATABASES = new QNm(QueryText.REST_PREFIX, "databases", REST_URI); /** Name. */ QNm Q_DATABASE = new QNm(QueryText.REST_PREFIX, "database", REST_URI); /** Name. */ QNm Q_RESOURCE = new QNm(QueryText.REST_PREFIX, "resource", REST_URI); /** REST. */ String REST = "REST"; /** Attribute. */ String RESOURCES = "resources"; /** Attribute. */ String NAME = "name"; /** Command operation. */ String COMMAND = "command"; /** Run operation. */ String RUN = "run"; /** Query operation. */ String QUERY = "query"; /** Initial context. */ String CONTEXT = "context"; } basex-8.5.1/basex-api/src/main/java/org/basex/http/restxq/000077500000000000000000000000001274071665100233435ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/java/org/basex/http/restxq/RestXqError.java000066400000000000000000000031021274071665100264420ustar00rootroot00000000000000package org.basex.http.restxq; import java.util.*; import org.basex.query.expr.path.*; import org.basex.query.value.item.*; /** * This class catches RESTXQ errors with the same priority. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class RestXqError implements Comparable { /** Error tests. */ private final ArrayList tests = new ArrayList<>(1); /** * Adds a test if it has not been specified before. * @param test test to be added * @return success flag */ boolean add(final NameTest test) { for(final NameTest t : tests) if(t.eq(test)) return false; tests.add(test); return true; } /** * Returns the test at the specified position, or {@code null}. * @param index test index * @return test */ NameTest get(final int index) { return index < tests.size() ? tests.get(index) : null; } /** * Checks if the specified name matches the test. * @param name name * @return result of check */ boolean matches(final QNm name) { for(final NameTest test : tests) if(test.eq(name)) return true; return false; } @Override public int compareTo(final RestXqError error) { final NameTest nt1 = tests.get(0), nt2 = error.tests.get(0); return nt1 == null || nt2 == null ? 0 : nt2.kind.ordinal() - nt1.kind.ordinal(); } @Override public String toString() { final StringBuilder sb = new StringBuilder(); for(final NameTest test : tests) { if(sb.length() != 0) sb.append(", "); sb.append(test); } return sb.toString(); } } basex-8.5.1/basex-api/src/main/java/org/basex/http/restxq/RestXqFunction.java000066400000000000000000000461451274071665100271540ustar00rootroot00000000000000package org.basex.http.restxq; import static org.basex.http.restxq.RestXqText.*; import static org.basex.query.QueryError.*; import static org.basex.query.ann.Annotation.*; import static org.basex.util.Token.*; import java.io.*; import java.util.*; import java.util.Map.*; import java.util.Set; import java.util.regex.*; import javax.servlet.http.*; import org.basex.build.csv.*; import org.basex.build.html.*; import org.basex.build.json.*; import org.basex.build.text.*; import org.basex.core.*; import org.basex.http.*; import org.basex.io.serial.*; import org.basex.query.*; import org.basex.query.ann.*; import org.basex.query.expr.*; import org.basex.query.expr.path.*; import org.basex.query.expr.path.Test.*; import org.basex.query.func.*; import org.basex.query.value.*; import org.basex.query.value.item.*; import org.basex.query.value.seq.*; import org.basex.query.value.type.*; import org.basex.query.var.*; import org.basex.util.*; import org.basex.util.http.*; import org.basex.util.list.*; import org.basex.util.options.*; /** * This class represents a single RESTXQ function. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class RestXqFunction implements Comparable { /** Single template pattern. */ private static final Pattern TEMPLATE = Pattern.compile("\\s*\\{\\s*\\$(.+?)\\s*\\}\\s*"); /** EQName pattern. */ private static final Pattern EQNAME = Pattern.compile("^Q\\{(.*?)\\}(.*)$"); /** Query parameters. */ final ArrayList queryParams = new ArrayList<>(); /** Form parameters. */ final ArrayList formParams = new ArrayList<>(); /** Header parameters. */ final ArrayList headerParams = new ArrayList<>(); /** Returned media types. */ final ArrayList produces = new ArrayList<>(); /** Supported methods. */ final Set methods = new HashSet<>(); /** Serialization parameters. */ final SerializerOptions output; /** Associated function. */ final StaticFunc function; /** Associated module. */ private final RestXqModule module; /** Query parameters. */ private final ArrayList errorParams = new ArrayList<>(); /** Cookie parameters. */ private final ArrayList cookieParams = new ArrayList<>(); /** Consumed media types. */ private final ArrayList consumes = new ArrayList<>(); /** Path. */ RestXqPath path; /** Function key for single instances. */ String key; /** Error. */ private RestXqError error; /** Post/Put variable. */ private QNm requestBody; /** * Constructor. * @param function associated user function * @param qc query context * @param module associated module */ RestXqFunction(final StaticFunc function, final QueryContext qc, final RestXqModule module) { this.function = function; this.module = module; output = qc.serParams(); } /** * Processes the HTTP request. * Parses new modules and discards obsolete ones. * @param http HTTP context * @param exc optional query exception * @throws Exception exception */ void process(final HTTPContext http, final QueryException exc) throws Exception { try { module.process(http, this, exc); } catch(final QueryException ex) { if(ex.file() == null) ex.info(function.info); throw ex; } } /** * Checks a function for RESTFful annotations. * @param ctx database context * @return {@code true} if module contains relevant annotations * @throws Exception exception */ boolean parse(final Context ctx) throws Exception { // parse all annotations final boolean[] declared = new boolean[function.args.length]; boolean found = false; final MainOptions options = ctx.options; for(final Ann ann : function.anns) { final Annotation sig = ann.sig; if(sig == null) continue; found |= eq(sig.uri, QueryText.REST_URI); final Item[] args = ann.args(); if(sig == _REST_PATH) { try { path = new RestXqPath(toString(args[0]), ann.info); } catch(final IllegalArgumentException ex) { throw error(ann.info, ex.getMessage()); } for(final QNm v : path.vars()) checkVariable(v, AtomType.AAT, declared); } else if(sig == _REST_ERROR) { error(ann); } else if(sig == _REST_CONSUMES) { strings(ann, consumes); } else if(sig == _REST_PRODUCES) { strings(ann, produces); } else if(sig == _REST_QUERY_PARAM) { queryParams.add(param(ann, declared)); } else if(sig == _REST_FORM_PARAM) { formParams.add(param(ann, declared)); } else if(sig == _REST_HEADER_PARAM) { headerParams.add(param(ann, declared)); } else if(sig == _REST_COOKIE_PARAM) { cookieParams.add(param(ann, declared)); } else if(sig == _REST_ERROR_PARAM) { errorParams.add(param(ann, declared)); } else if(sig == _REST_METHOD) { final String mth = toString(args[0]).toUpperCase(Locale.ENGLISH); final Item body = args.length > 1 ? args[1] : null; addMethod(mth, body, declared, ann.info); } else if(sig == _REST_SINGLE) { key = "\u0000" + (args.length > 0 ? toString(args[0]) : (function.info.path() + ':' + function.info.line())); } else if(eq(sig.uri, QueryText.REST_URI)) { final Item body = args.length == 0 ? null : args[0]; addMethod(string(sig.local()), body, declared, ann.info); } else if(sig == _INPUT_CSV) { final CsvParserOptions opts = new CsvParserOptions(options.get(MainOptions.CSVPARSER)); options.set(MainOptions.CSVPARSER, parse(opts, ann)); } else if(sig == _INPUT_JSON) { final JsonParserOptions opts = new JsonParserOptions(options.get(MainOptions.JSONPARSER)); options.set(MainOptions.JSONPARSER, parse(opts, ann)); } else if(sig == _INPUT_HTML) { final HtmlOptions opts = new HtmlOptions(options.get(MainOptions.HTMLPARSER)); options.set(MainOptions.HTMLPARSER, parse(opts, ann)); } else if(sig == _INPUT_TEXT) { final TextOptions opts = new TextOptions(options.get(MainOptions.TEXTPARSER)); options.set(MainOptions.TEXTPARSER, parse(opts, ann)); } else if(eq(sig.uri, QueryText.OUTPUT_URI)) { // serialization parameters try { output.assign(string(sig.local()), toString(args[0])); } catch(final BaseXException ex) { throw error(ann.info, UNKNOWN_SER, sig.local()); } } } if(found) { if(path == null && error == null) throw error(function.info, ANN_MISSING, '%', PATH, '%', ERROR); final int dl = declared.length; for(int d = 0; d < dl; d++) { if(declared[d]) continue; throw error(function.info, VAR_UNDEFINED, function.args[d].name.string()); } } return found; } /** * Assigns annotation values as options. * @param option type * @param opts options instance * @param ann annotation * @return options instance * @throws Exception any exception */ private static O parse(final O opts, final Ann ann) throws Exception { for(final Item arg : ann.args()) opts.assign(string(arg.string(ann.info))); return opts; } /** * Add a HTTP method to the list of supported methods by this RESTXQ function. * @param method HTTP method as a string * @param body variable to which the HTTP request body to be bound (optional) * @param declared variable declaration flags * @param ii input info * @throws QueryException query exception */ private void addMethod(final String method, final Item body, final boolean[] declared, final InputInfo ii) throws QueryException { if(body != null && !body.isEmpty()) { final HttpMethod m = HttpMethod.get(method); if(m != null && !m.body) throw error(ii, METHOD_VALUE, m); if(requestBody != null) throw error(ii, ANN_BODYVAR); requestBody = checkVariable(toString(body), declared); } if(methods.contains(method)) throw error(ii, ANN_TWICE, "%", method); methods.add(method); } /** * Checks if an HTTP request matches this function and its constraints. * @param http http context * @param err error code * @return result of check */ boolean matches(final HTTPContext http, final QNm err) { // check method, consumed and produced media type, and path or error return (methods.isEmpty() || methods.contains(http.method)) && consumes(http) && produces(http) && (err == null ? path != null && path.matches(http) : error != null && error.matches(err)); } /** * Binds the annotated variables. * @param http http context * @param arg argument array * @param err optional query error * @param qc query context * @throws QueryException query exception * @throws IOException I/O exception */ void bind(final HTTPContext http, final Expr[] arg, final QueryException err, final QueryContext qc) throws QueryException, IOException { // bind variables from segments if(path != null) { for(final Entry entry : path.values(http).entrySet()) { final QNm qnm = new QNm(entry.getKey().string(), function.sc); if(function.sc.elemNS != null && eq(qnm.uri(), function.sc.elemNS)) qnm.uri(EMPTY); bind(qnm, arg, new Atm(entry.getValue()), qc); } } // bind request body in the correct format final MainOptions mo = http.context(false).options; if(requestBody != null) { try { bind(requestBody, arg, HttpPayload.value(http.params.body(), mo, http.contentType()), qc); } catch(final IOException ex) { throw error(INPUT_CONV, ex); } } // bind query and form parameters for(final RestXqParam rxp : queryParams) bind(rxp, arg, http.params.query().get(rxp.name), qc); for(final RestXqParam rxp : formParams) bind(rxp, arg, http.params.form(mo).get(rxp.name), qc); // bind header parameters for(final RestXqParam rxp : headerParams) { final TokenList tl = new TokenList(); final Enumeration en = http.req.getHeaders(rxp.name); while(en.hasMoreElements()) { for(final String s : en.nextElement().toString().split(", *")) tl.add(s); } bind(rxp, arg, StrSeq.get(tl), qc); } // bind cookie parameters final Cookie[] ck = http.req.getCookies(); for(final RestXqParam rxp : cookieParams) { Value val = Empty.SEQ; if(ck != null) { for(final Cookie c : ck) { if(rxp.name.equals(c.getName())) val = Str.get(c.getValue()); } } bind(rxp, arg, val, qc); } // bind errors final Map errs = new HashMap<>(); if(err != null) { final Value[] values = Catch.values(err); final QNm[] names = Catch.NAMES; final int nl = names.length; for(int n = 0; n < nl; n++) errs.put(string(names[n].local()), values[n]); } for(final RestXqParam rxp : errorParams) bind(rxp, arg, errs.get(rxp.name), qc); } /** * Creates an exception with the specified message. * @param msg message * @param ext error extension * @return exception */ QueryException error(final String msg, final Object... ext) { return error(function.info, msg, ext); } /** * Creates an exception with the specified message. * @param ii input info * @param msg message * @param ext error extension * @return exception */ private static QueryException error(final InputInfo ii, final String msg, final Object... ext) { return BASX_RESTXQ_X.get(ii, Util.info(msg, ext)); } @Override public int compareTo(final RestXqFunction rxf) { return path == null ? error.compareTo(rxf.error) : path.compareTo(rxf.path); } // PRIVATE METHODS ==================================================================== /** * Checks the specified template and adds a variable. * @param tmp template string * @param declared variable declaration flags * @return resulting variable * @throws QueryException query exception */ private QNm checkVariable(final String tmp, final boolean... declared) throws QueryException { return checkVariable(tmp, AtomType.ITEM, declared); } /** * Checks the specified template and adds a variable. * @param tmp template string * @param type allowed type * @param declared variable declaration flags * @return resulting variable * @throws QueryException query exception */ private QNm checkVariable(final String tmp, final Type type, final boolean... declared) throws QueryException { final Matcher m = TEMPLATE.matcher(tmp); if(!m.find()) throw error(INV_TEMPLATE, tmp); final byte[] vn = token(m.group(1)); if(!XMLToken.isQName(vn)) throw error(INV_VARNAME, vn); final QNm name = new QNm(vn); return checkVariable(name, type, declared); } /** * Checks if the specified variable exists in the current function. * @param name variable * @param type allowed type * @param declared variable declaration flags * @return resulting variable * @throws QueryException query exception */ private QNm checkVariable(final QNm name, final Type type, final boolean[] declared) throws QueryException { if(name.hasPrefix()) name.uri(function.sc.ns.uri(name.prefix())); int a = -1; final Var[] args = function.args; final int al = args.length; while(++a < al && !args[a].name.eq(name)); if(a == args.length) throw error(UNKNOWN_VAR, name.string()); if(declared[a]) throw error(VAR_ASSIGNED, name.string()); final SeqType st = args[a].declaredType(); if(args[a].checksType() && !st.type.instanceOf(type)) throw error(INV_VARTYPE, name.string(), type); declared[a] = true; return name; } /** * Checks if the consumed content type matches. * @param http http context * @return result of check */ private boolean consumes(final HTTPContext http) { // return true if no type is given if(consumes.isEmpty()) return true; // return true if no content type is specified by the user final MediaType type = http.contentType(); if(type.type().isEmpty()) return true; // check if any combination matches for(final MediaType consume : consumes) { if(consume.matches(type)) return true; } return false; } /** * Checks if the produced media type matches. * @param http http context * @return result of check */ private boolean produces(final HTTPContext http) { // return true if no type is given if(produces.isEmpty()) return true; // check if any combination matches for(final MediaType accept : http.accepts()) { for(final MediaType produce : produces) { if(produce.matches(accept)) return true; } } return false; } /** * Binds the specified parameter to a variable. * @param rxp parameter * @param args argument array * @param value values to be bound; the parameter's default value is assigned * if the argument is {@code null} or empty * @param qc query context * @throws QueryException query exception */ private void bind(final RestXqParam rxp, final Expr[] args, final Value value, final QueryContext qc) throws QueryException { bind(rxp.var, args, value == null || value.isEmpty() ? rxp.value : value, qc); } /** * Binds the specified value to a variable. * @param name variable name * @param args argument array * @param value value to be bound * @param qc query context * @throws QueryException query exception */ private void bind(final QNm name, final Expr[] args, final Value value, final QueryContext qc) throws QueryException { // skip nulled values if(value == null) return; final Var[] fargs = function.args; final int fl = fargs.length; for(int f = 0; f < fl; f++) { final Var var = fargs[f]; if(var.name.eq(name)) { // casts and binds the value final SeqType decl = var.declaredType(); final Value val = value.seqType().instanceOf(decl) ? value : decl.cast(value, qc, function.sc, null); args[f] = var.checkType(val, qc, false); break; } } } /** * Returns the specified item as a string. * @param item item * @return string */ private static String toString(final Item item) { return ((Str) item).toJava(); } /** * Adds items to the specified list. * @param ann annotation * @param list list to add values to */ private static void strings(final Ann ann, final ArrayList list) { for(final Item it : ann.args()) list.add(new MediaType(toString(it))); } /** * Returns a parameter. * @param ann annotation * @param declared variable declaration flags * @return parameter * @throws QueryException HTTP exception */ private RestXqParam param(final Ann ann, final boolean... declared) throws QueryException { // name of parameter final Item[] args = ann.args(); final String name = toString(args[0]); // variable template final QNm var = checkVariable(toString(args[1]), declared); // default value final int al = args.length; final ValueBuilder vb = new ValueBuilder(); for(int a = 2; a < al; a++) vb.add(args[a]); return new RestXqParam(var, name, vb.value()); } /** * Returns an error. * @param ann annotation * @throws QueryException HTTP exception */ private void error(final Ann ann) throws QueryException { if(error == null) error = new RestXqError(); // name of parameter NameTest last = error.get(0); for(final Item arg : ann.args()) { final String err = toString(arg); final Kind kind; QNm qnm = null; if(err.equals("*")) { kind = Kind.WILDCARD; } else if(err.startsWith("*:")) { final byte[] local = token(err.substring(2)); if(!XMLToken.isNCName(local)) throw error(INV_CODE, err); qnm = new QNm(local); kind = Kind.NAME; } else if(err.endsWith(":*")) { final byte[] prefix = token(err.substring(0, err.length() - 2)); if(!XMLToken.isNCName(prefix)) throw error(INV_CODE, err); qnm = new QNm(concat(prefix, COLON), function.sc); kind = Kind.URI; } else { final Matcher m = EQNAME.matcher(err); if(m.matches()) { final byte[] uri = token(m.group(1)); final byte[] local = token(m.group(2)); if(local.length == 1 && local[0] == '*') { qnm = new QNm(COLON, uri); kind = Kind.URI; } else { if(!XMLToken.isNCName(local) || !Uri.uri(uri).isValid()) throw error(INV_CODE, err); qnm = new QNm(local, uri); kind = Kind.URI_NAME; } } else { final byte[] nm = token(err); if(!XMLToken.isQName(nm)) throw error(INV_CODE, err); qnm = new QNm(nm, function.sc); kind = Kind.URI_NAME; } } // message if(qnm != null && qnm.hasPrefix() && !qnm.hasURI()) throw error(INV_NONS, qnm); final NameTest test = new NameTest(qnm, kind, false, null); if(last != null && last.kind != kind) throw error(INV_PRIORITY, last, test); if(!error.add(test)) throw error(INV_ERR_SAME, last); last = test; } } } basex-8.5.1/basex-api/src/main/java/org/basex/http/restxq/RestXqModule.java000066400000000000000000000073611274071665100266110ustar00rootroot00000000000000package org.basex.http.restxq; import static org.basex.query.QueryError.*; import static org.basex.util.Token.*; import java.io.*; import java.util.*; import org.basex.core.*; import org.basex.http.*; import org.basex.io.*; import org.basex.query.*; import org.basex.query.func.*; /** * This class caches information on a single XQuery module with RESTXQ annotations. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class RestXqModule { /** Supported methods. */ private final ArrayList functions = new ArrayList<>(); /** File reference. */ private final IOFile file; /** Parsing timestamp. */ private long time; /** * Constructor. * @param file xquery file */ RestXqModule(final IOFile file) { this.file = file; time = file.timeStamp(); } /** * Checks the module for RESTXQ annotations. * @param http HTTP context * @return {@code true} if module contains relevant annotations * @throws Exception exception (including unexpected ones) */ boolean parse(final HTTPContext http) throws Exception { functions.clear(); // loop through all functions final Context ctx = http.context(false); try(final QueryContext qc = qc(ctx)) { // loop through all functions final String name = file.name(); for(final StaticFunc sf : qc.funcs.funcs()) { // only add functions that are defined in the same module (file) if(sf.expr != null && name.equals(new IOFile(sf.info.path()).name())) { final RestXqFunction rxf = new RestXqFunction(sf, qc, this); if(rxf.parse(ctx)) functions.add(rxf); } } } return !functions.isEmpty(); } /** * Checks if the timestamp is still up-to-date. * @return result of check */ boolean uptodate() { return time == file.timeStamp(); } /** * Updates the timestamp. */ void touch() { time = file.timeStamp(); } /** * Returns all functions. * @return functions */ ArrayList functions() { return functions; } /** * Processes the HTTP request. * @param http HTTP context * @param func function to be processed * @param error optional error reference * @throws Exception exception */ void process(final HTTPContext http, final RestXqFunction func, final QueryException error) throws Exception { // create new XQuery instance final Context ctx = http.context(false); try(final QueryContext qc = qc(ctx)) { final StaticFunc sf = find(qc, func.function); // will only happen if file has been swapped between caching and parsing if(sf == null) throw HTTPCode.NO_XQUERY.get(); final RestXqFunction rxf = new RestXqFunction(sf, qc, this); rxf.parse(ctx); RestXqResponse.create(rxf, qc, http, error); } } // PRIVATE METHODS ==================================================================== /** * Retrieves a query context for the given module. * @param ctx database context * @return query context * @throws Exception exception */ private QueryContext qc(final Context ctx) throws Exception { final QueryContext qc = new QueryContext(ctx); try { qc.parse(string(file.read()), file.path(), null); return qc; } catch(final IOException ex) { // may be triggered when reading the file throw IOERR_X.get(null, ex); } } /** * Returns the specified function from the given query context. * @param qctx query context. * @param func function to be found * @return function */ private static StaticFunc find(final QueryContext qctx, final StaticFunc func) { for(final StaticFunc sf : qctx.funcs.funcs()) { if(func.info.equals(sf.info)) return sf; } return null; } } basex-8.5.1/basex-api/src/main/java/org/basex/http/restxq/RestXqModules.java000066400000000000000000000140711274071665100267700ustar00rootroot00000000000000package org.basex.http.restxq; import static org.basex.http.restxq.RestXqText.*; import java.util.*; import org.basex.core.*; import org.basex.http.*; import org.basex.io.*; import org.basex.query.value.item.*; import org.basex.query.value.node.*; import org.basex.util.*; import org.basex.util.http.*; /** * This class caches RESTXQ modules found in the HTTP root directory. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ public final class RestXqModules { /** Class instance. */ private static final RestXqModules INSTANCE = new RestXqModules(); /** Module cache. */ private HashMap modules = new HashMap<>(); /** Private constructor. */ private RestXqModules() { } /** * Returns the singleton instance. * @return instance */ public static RestXqModules get() { return INSTANCE; } /** * Initializes the module cache. */ public void init() { modules = new HashMap<>(); } /** * Returns a WADL description for all available URIs. * @param http HTTP context * @return WADL description */ public FElem wadl(final HTTPContext http) { return new RestXqWadl(http).create(modules); } /** * Returns the function that matches the current request or the specified error code. * Returns {@code null} if no function matches. * @param http HTTP context * @param error error code (optional) * @return function * @throws Exception exception (including unexpected ones) */ RestXqFunction find(final HTTPContext http, final QNm error) throws Exception { // collect all functions final ArrayList list = new ArrayList<>(); for(final RestXqModule mod : cache(http).values()) { for(final RestXqFunction rxf : mod.functions()) { if(rxf.matches(http, error)) list.add(rxf); } } // no path matches if(list.isEmpty()) return null; // sort by relevance Collections.sort(list); // return best matching function final RestXqFunction best = list.get(0); if(list.size() == 1 || best.compareTo(list.get(1)) != 0) return best; final RestXqFunction bestQf = bestQf(list, http); if(bestQf != null) return bestQf; // show error if more than one path with the same specifity exists final TokenBuilder tb = new TokenBuilder(); for(final RestXqFunction rxf : list) { if(best.compareTo(rxf) != 0) break; tb.add(Prop.NL).add(rxf.function.info.toString()); } throw best.path == null ? best.error(ERROR_CONFLICT, error, tb) : best.error(PATH_CONFLICT, best.path, tb); } /** * Returns the function that has a media type whose quality factor matches the HTTP request best. * @param list list of functions * @param http http context * @return best function, or {@code null} if more than one function exists */ private static RestXqFunction bestQf(final ArrayList list, final HTTPContext http) { // media types accepted by the client final MediaType[] accepts = http.accepts(); double bestQf = 0; RestXqFunction best = list.get(0); for(final RestXqFunction rxf : list) { // skip remaining functions with a weaker specifity if(best.compareTo(rxf) != 0) break; if(rxf.produces.isEmpty()) return null; for(final MediaType produce : rxf.produces) { for(final MediaType accept : accepts) { final String value = accept.parameters().get("q"); final double qf = value == null ? 1 : Double.parseDouble(value); if(produce.matches(accept)) { // multiple functions with the same quality factor if(bestQf == qf) return null; if(bestQf < qf) { bestQf = qf; best = rxf; } } } } } return best; } /** * Updates the module cache. Parses new modules and discards obsolete ones. * @param http http context * @return module cache * @throws Exception exception (including unexpected ones) */ private synchronized HashMap cache(final HTTPContext http) throws Exception { final StaticOptions sopts = http.context(false).soptions; HashMap cache = modules; // create new cache if it is empty, or if cache is to be recreated every time if(cache.isEmpty() || !sopts.get(StaticOptions.CACHERESTXQ)) { cache = new HashMap<>(); final String webpath = sopts.get(StaticOptions.WEBPATH); final String rxqpath = sopts.get(StaticOptions.RESTXQPATH); final IOFile restxq = new IOFile(webpath).resolve(rxqpath); if(!restxq.exists()) throw HTTPCode.NO_RESTXQ.get(); cache(http, restxq, cache, modules); modules = cache; } return cache; } /** * Parses the specified path for RESTXQ modules and caches new entries. * @param root root path * @param http http context * @param cache cached modules * @param old old cache * @throws Exception exception (including unexpected ones) */ private static synchronized void cache(final HTTPContext http, final IOFile root, final HashMap cache, final HashMap old) throws Exception { // check if directory is to be skipped final IOFile[] files = root.children(); for(final IOFile file : files) if(file.name().equals(IO.IGNORESUFFIX)) return; for(final IOFile file : files) { if(file.isDir()) { cache(http, file, cache, old); } else { final String path = file.path(); if(file.hasSuffix(IO.XQSUFFIXES)) { RestXqModule module = old.get(path); boolean parsed = false; if(module != null) { // check if module has been modified parsed = module.uptodate(); } else { // create new module module = new RestXqModule(file); } // add module if it has been parsed, and if it contains annotations if(parsed || module.parse(http)) { module.touch(); cache.put(path, module); } } } } } } basex-8.5.1/basex-api/src/main/java/org/basex/http/restxq/RestXqParam.java000066400000000000000000000012271274071665100264170ustar00rootroot00000000000000package org.basex.http.restxq; import org.basex.query.value.*; import org.basex.query.value.item.*; /** * This class contains a single RESTXQ parameter. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class RestXqParam { /** Variable name. */ final QNm var; /** Name of parameter. */ final String name; /** Default value. */ final Value value; /** * Constructor. * @param var variable name * @param name name of parameter * @param value default value */ RestXqParam(final QNm var, final String name, final Value value) { this.var = var; this.name = name; this.value = value; } } basex-8.5.1/basex-api/src/main/java/org/basex/http/restxq/RestXqPath.java000066400000000000000000000037531274071665100262610ustar00rootroot00000000000000package org.basex.http.restxq; import org.basex.http.*; import org.basex.query.QueryException; import org.basex.query.value.item.QNm; import org.basex.util.InputInfo; import java.util.List; import java.util.Map; /** * This class represents the path of a RESTXQ function. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class RestXqPath implements Comparable { /** Path. */ private final String path; /** Path matcher. */ private final RestXqPathMatcher matcher; /** * Constructor. * @param path path * @param info input info * @throws QueryException query exception */ RestXqPath(final String path, final InputInfo info) throws QueryException { this.path = path; matcher = RestXqPathMatcher.parse(path, info); } /** * Checks if the path matches the HTTP request. * @param http http context * @return result of check */ boolean matches(final HTTPContext http) { return matcher.matches(http.path()); } /** * Returns the names of the template variables. * @return list of qualified variable names */ List vars() { return matcher.vars; } /** * Gets the variable values for the given HTTP context path. * @param http HTTP context * @return map with variable values */ Map values(final HTTPContext http) { return matcher.values(http.path()); } /** * Checks if the specified path segment is a template. * @param s offset of segment * @return result of check */ private boolean isTemplate(final int s) { return matcher.varsPos.testBit(s); } @Override public int compareTo(final RestXqPath rxs) { final int ms = matcher.segments; final int d = ms - rxs.matcher.segments; if(d != 0) return d; for(int s = 0; s < ms; s++) { final boolean wc1 = isTemplate(s), wc2 = rxs.isTemplate(s); if(wc1 != wc2) return wc1 ? 1 : -1; } return 0; } @Override public String toString() { return path; } } basex-8.5.1/basex-api/src/main/java/org/basex/http/restxq/RestXqPathMatcher.java000066400000000000000000000165541274071665100275700ustar00rootroot00000000000000package org.basex.http.restxq; import static java.math.BigInteger.*; import static org.basex.http.restxq.RestXqText.*; import java.math.*; import java.util.*; import java.util.regex.*; import org.basex.http.*; import org.basex.query.*; import org.basex.query.value.item.*; import org.basex.util.*; /** * RESTXQ path template. * * @author BaseX Team 2005-16, BSD License * @author Dimitar Popov */ final class RestXqPathMatcher { /** Default matcher for empty path templates. */ private static final RestXqPathMatcher EMPTY = new RestXqPathMatcher("/", Collections.emptyList(), 0, ZERO); /** Variable names defined in the path template. */ final List vars; /** Compiled regular expression which matches paths defined by the path annotation. */ final Pattern pattern; /** Number of path segments. */ final int segments; /** Bit array with variable positions within the path template. */ final BigInteger varsPos; /** * Constructor. * @param regex regular expression which matches paths defined by the path annotation * @param vars variable names defined in the path template * @param segments segment count * @param varsPos variable position */ private RestXqPathMatcher(final String regex, final List vars, final int segments, final BigInteger varsPos) { this.vars = vars; this.segments = segments; this.varsPos = varsPos; pattern = Pattern.compile(regex); } /** * Checks if the given path matches. * @param path path to match * @return result of check */ boolean matches(final String path) { return matcher(path).matches(); } /** * Gets variable values for the given path. * @param path from which to read the values * @return map with variable values */ Map values(final String path) { final Map result = new HashMap<>(); final Matcher m = matcher(path); if(m.matches()) { final int groupCount = m.groupCount(); if(vars.size() <= groupCount) { int group = 1; for(final QNm var : vars) { result.put(var, m.group(group)); // skip nested groups final int end = m.end(group); while (++group <= groupCount && m.start(group) < end); } } } return result; } /** * Creates a pattern matcher for the given string. * @param input input string * @return pattern matcher */ private Matcher matcher(final String input) { return pattern.matcher(input); } /** * Parses a path template. * @param path path template string to be parsed * @param ii input info * @return parsed path template * @throws QueryException if given template is invalid */ static RestXqPathMatcher parse(final String path, final InputInfo ii) throws QueryException { if(path.isEmpty()) return EMPTY; final ArrayList vars = new ArrayList<>(); final StringBuilder result = new StringBuilder(); final StringBuilder literals = new StringBuilder(); final TokenBuilder variable = new TokenBuilder(); final StringBuilder regex = new StringBuilder(); final BitSet varsPos = new BitSet(); int segment = 0; final CharIterator i = new CharIterator(path); if(path.charAt(0) == '/') i.next(); literals.append('/'); while(i.hasNext()) { char ch = i.next(); if(ch == '{') { decodeAndEscape(literals, result); // variable if(!i.hasNext() || i.nextNonWS() != '$') throw error(ii, INV_TEMPLATE, path); // default variable regular expression regex.append("[^/]+?"); int braces = 1; while(i.hasNext()) { ch = i.nextNonWS(); if(ch == '=') { regex.setLength(0); addRegex(i, regex); if(regex.length() == 0) throw error(ii, INV_TEMPLATE, path); break; } else if(ch == '{') { ++braces; variable.add(ch); } else if(ch == '}' && --braces == 0) { break; } else { variable.add(ch); } } final byte[] var = variable.toArray(); if(!XMLToken.isQName(var)) throw error(ii, INV_VARNAME, variable); vars.add(new QNm(var)); variable.reset(); varsPos.set(segment); result.append('(').append(regex).append(')'); regex.setLength(0); } else { if(ch == '/') ++segment; literals.append(ch); } } decodeAndEscape(literals, result); final BigInteger vp = varsPos.cardinality() == 0 ? ZERO : new BigInteger(varsPos.toByteArray()); return new RestXqPathMatcher(result.toString(), vars, segment + 1, vp); } /** * Parses a regular expression defined for a template variable. * @param i character iterator positioned before the first character of the regex. * @param result string builder where the parsed regular expression will be appended to. */ private static void addRegex(final CharIterator i, final StringBuilder result) { int braces = 1; while(i.hasNext()) { final char ch = i.nextNonWS(); if(ch == '{') ++braces; else if(ch == '}' && --braces == 0) break; result.append(ch); } } /** * Decodes the URL and escapes regex characters in path template literals. * @param literals literals to escape * @param result string builder where the escaped literals will be appended to. */ private static void decodeAndEscape(final StringBuilder literals, final StringBuilder result) { if(literals.length() > 0) { final String decoded = HTTPContext.decode(literals.toString()); final int n = decoded.length(); for(int i = 0; i < n; ++i) { final char c = decoded.charAt(i); if(isRegexChar(c)) result.append('\\'); result.append(c); } literals.setLength(0); } } /** * Checks if a character is a regex character. * @param c character to check. * @return result of check */ private static boolean isRegexChar(final char c) { return ".^&!?-:<>()[]{}$=,*+|".indexOf(c) >= 0; } /** * Creates a query exception. * @param ii input info * @param msg exception message * @param e text extensions * @return query exception */ private static QueryException error(final InputInfo ii, final String msg, final Object... e) { return QueryError.BASX_RESTXQ_X.get(ii, Util.info(msg, e)); } /** Character iterator. */ private static final class CharIterator { /** Input text to iterate over. */ private final String input; /** Input text length. */ private final int len; /** Current iterator position. */ private int pos; /** * Construct a new character iterator for the given input text. * @param input input text to iterator over. */ CharIterator(final String input) { this.input = input; len = input.length(); } /** * Check if there are more characters to iterate over. * @return {@code false} if text end is reached */ boolean hasNext() { return pos < len; } /** * Get next character. * @return next character */ char next() { return input.charAt(pos++); } /** * Get next non-white-space character. * @return non-white-space character */ char nextNonWS() { char ch; do { ch = next(); } while(Character.isWhitespace(ch) && hasNext()); return ch; } } } basex-8.5.1/basex-api/src/main/java/org/basex/http/restxq/RestXqRespBuilder.java000066400000000000000000000065071274071665100276050ustar00rootroot00000000000000package org.basex.http.restxq; import static org.basex.http.restxq.RestXqText.*; import static org.basex.util.Token.*; import org.basex.http.*; import org.basex.io.out.*; import org.basex.io.serial.*; import org.basex.query.func.*; import org.basex.query.iter.*; import org.basex.query.value.item.*; import org.basex.query.value.node.*; import org.basex.util.http.*; /** * This class holds information on a custom RESTXQ response. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class RestXqRespBuilder { /** Output cache. */ final ArrayOutput cache = new ArrayOutput(); /** Show error. */ boolean error; /** Status code. */ int status; /** Status message. */ String message; /** * Builds a response element and creates the serialization parameters. * @param response response element * @param func function * @param iter result iterator * @param http http context * @throws Exception exception (including unexpected ones) */ void build(final ANode response, final RestXqFunction func, final Iter iter, final HTTPContext http) throws Exception { // don't allow attributes for(final ANode a : response.attributes()) throw func.error(UNEXP_NODE, a); // parse response and serialization parameters SerializerOptions sp = func.output; String cType = null; for(final ANode n : response.children()) { // process http:response element if(HTTP_RESPONSE.eq(n)) { // check status and reason byte[] sta = null, msg = null; for(final ANode a : n.attributes()) { final QNm qnm = a.qname(); if(qnm.eq(Q_STATUS)) sta = a.string(); else if(qnm.eq(Q_REASON) || qnm.eq(Q_MESSAGE)) msg = a.string(); else throw func.error(UNEXP_NODE, a); } if(sta != null) { status = toInt(sta); message = msg != null ? string(msg) : null; } for(final ANode c : n.children()) { // process http:header elements if(HTTP_HEADER.eq(c)) { final byte[] nam = c.attribute(Q_NAME); final byte[] val = c.attribute(Q_VALUE); if(nam != null && val != null) { final String key = string(nam), value = string(val); if(key.equalsIgnoreCase(HttpText.CONTENT_TYPE)) { cType = value; } else { http.res.setHeader(key, key.equalsIgnoreCase(HttpText.LOCATION) ? http.resolve(value) : value); } } } else { throw func.error(UNEXP_NODE, c); } } } else if(OUTPUT_SERIAL.eq(n)) { // parse output:serialization-parameters sp = FuncOptions.serializer(n, func.output, func.function.info); } else { throw func.error(UNEXP_NODE, n); } } // set content type if(cType != null) sp.set(SerializerOptions.MEDIA_TYPE, cType); // check next item Item item = iter.next(); if(item == null) { error = true; } else if(func.methods.size() == 1 && func.methods.contains(HttpMethod.HEAD.name())) { throw func.error(HEAD_METHOD); } // cache result http.sopts(sp); http.initResponse(); try(final Serializer ser = Serializer.get(cache, sp)) { for(; item != null; item = iter.next()) ser.serialize(item); } } } basex-8.5.1/basex-api/src/main/java/org/basex/http/restxq/RestXqResponse.java000066400000000000000000000067241274071665100271640ustar00rootroot00000000000000package org.basex.http.restxq; import static org.basex.http.restxq.RestXqText.*; import static org.basex.util.Token.*; import org.basex.http.*; import org.basex.io.serial.*; import org.basex.query.*; import org.basex.query.expr.*; import org.basex.query.func.*; import org.basex.query.iter.*; import org.basex.query.value.item.*; import org.basex.query.value.node.*; import org.basex.query.value.type.*; import org.basex.util.http.*; /** * This class creates a new HTTP response. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class RestXqResponse { /** Private constructor. */ private RestXqResponse() { } /** * Evaluates the specified function and creates a response. * @param function function to be evaluated * @param qc query context * @param http HTTP context * @param error optional query error * @throws Exception exception (including unexpected ones) */ static void create(final RestXqFunction function, final QueryContext qc, final HTTPContext http, final QueryException error) throws Exception { // bind variables final StaticFunc sf = function.function; final Expr[] args = new Expr[sf.args.length]; function.bind(http, args, error, qc); // assign function call and http context and register process qc.mainModule(MainModule.get(sf, args)); qc.http(http); qc.job().type(RESTXQ); qc.register(qc.context); final RestXqSession session = new RestXqSession(http, function.key, qc); String redirect = null, forward = null; RestXqRespBuilder response = null; try { // evaluate query final Iter iter = qc.iter(); Item item = iter.next(); // handle response element if(item instanceof ANode) { final ANode node = (ANode) item; // send redirect to browser if(REST_REDIRECT.eq(node)) { final ANode ch = node.children().next(); if(ch == null || ch.type != NodeType.TXT) throw function.error(NO_VALUE, node.name()); redirect = string(ch.string()).trim(); return; } // server-side forwarding if(REST_FORWARD.eq(node)) { final ANode ch = node.children().next(); if(ch == null || ch.type != NodeType.TXT) throw function.error(NO_VALUE, node.name()); forward = string(ch.string()).trim(); return; } if(REST_RESPONSE.eq(node)) { response = new RestXqRespBuilder(); response.build(node, function, iter, http); return; } } // HEAD method must return a single response element if(function.methods.size() == 1 && function.methods.contains(HttpMethod.HEAD.name())) throw function.error(HEAD_METHOD); // serialize result final SerializerOptions sp = function.output; http.sopts(sp); http.initResponse(); try(final Serializer ser = Serializer.get(http.res.getOutputStream(), sp)) { for(; item != null; item = iter.next()) ser.serialize(item); } } finally { qc.close(); qc.unregister(qc.context); session.close(); if(redirect != null) { http.redirect(redirect); } else if(forward != null) { http.forward(forward); } else if(response != null) { if(response.status != 0) http.status(response.status, response.message, response.error); final byte[] out = response.cache.finish(); if(out.length != 0) http.res.getOutputStream().write(out); } } } } basex-8.5.1/basex-api/src/main/java/org/basex/http/restxq/RestXqServlet.java000066400000000000000000000027341274071665100270070ustar00rootroot00000000000000package org.basex.http.restxq; import static org.basex.http.restxq.RestXqText.*; import org.basex.http.*; import org.basex.query.*; /** *

This servlet receives and processes REST requests. * The evaluated code is defined in XQuery modules, which are located in the web server's * root directory (specified by the {@code HTTPPATH} option), and decorated with RESTXQ * annotations.

* *

The implementation is based on Adam Retter's paper presented at XMLPrague 2012, * titled "RESTful XQuery - Standardised XQuery 3.0 Annotations for REST".

* * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ public final class RestXqServlet extends BaseXServlet { @Override protected void run(final HTTPContext http) throws Exception { // no trailing slash: send redirect if(http.req.getPathInfo() == null) { http.redirect("/"); return; } // analyze input path final RestXqModules rxm = RestXqModules.get(); // initialize RESTXQ if(http.path().equals("/" + INIT)) { rxm.init(); return; } // select function that closest matches the request RestXqFunction func = rxm.find(http, null); if(func == null) throw HTTPCode.NO_XQUERY.get(); try { // process function func.process(http, null); } catch(final QueryException ex) { // run optional error function func = rxm.find(http, ex.qname()); if(func == null) throw ex; func.process(http, ex); } } } basex-8.5.1/basex-api/src/main/java/org/basex/http/restxq/RestXqSession.java000066400000000000000000000023561274071665100270060ustar00rootroot00000000000000package org.basex.http.restxq; import javax.servlet.http.*; import org.basex.http.*; import org.basex.query.*; /** * Information on a RESTXQ session. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class RestXqSession { /** HTTP session. */ private final HttpSession session; /** Function id. */ private final String key; /** Query context. */ private final QueryContext qc; /** * Returns a query context stored in the current session. * @param http HTTP session * @param key function key * @param qc query context */ RestXqSession(final HTTPContext http, final String key, final QueryContext qc) { this.qc = qc; this.key = key; session = http.req.getSession(); if(key != null) { final Object oldQc = session.getAttribute(key); if(oldQc instanceof QueryContext) { ((QueryContext) oldQc).stop(); do Thread.yield(); while(session.getAttribute(key) == oldQc); } session.setAttribute(key, qc); } } /** * Closes a session. Drops a previously cached query context. */ void close() { if(key != null) { final Object oldQc = session.getAttribute(key); if(oldQc == qc) session.removeAttribute(key); } } } basex-8.5.1/basex-api/src/main/java/org/basex/http/restxq/RestXqText.java000066400000000000000000000077071274071665100263140ustar00rootroot00000000000000package org.basex.http.restxq; import static org.basex.query.QueryText.*; import static org.basex.util.Token.*; import org.basex.query.expr.path.*; import org.basex.query.func.*; import org.basex.query.value.item.*; /** * This class assembles texts which are used in the HTTP classes. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ public interface RestXqText { /** Token "error". */ byte[] ERROR = token("error"); /** Token "path". */ byte[] PATH = token("path"); /** Token "header". */ byte[] HEADER = token("header"); /** Token "response". */ byte[] RESPONSE = token("response"); /** Token "status". */ byte[] STATUS = token("status"); /** Token "reason". */ byte[] REASON = token("reason"); /** Token "message". */ byte[] MESSAGE = token("message"); /** Token "name". */ byte[] NAME = token("name"); /** Token "value". */ byte[] VALUE = token("value"); /** Token "redirect". */ byte[] REDIRECT = token("redirect"); /** Token "forward". */ byte[] FORWARD = token("forward"); /** RESTXQ string. */ String RESTXQ = "RESTXQ"; /** WADL prefix. */ String WADL = "wadl:"; /** WADL namespace. */ String WADL_URI = "http://wadl.dev.java.net/2009/02"; /** XHTML namespace. */ String XHTML_URL = "http://www.w3.org/1999/xhtml"; /** Init call. */ String INIT = ".init"; /** Error message. */ String ANN_MISSING = "Annotation %% or %% missing."; /** Error message. */ String ANN_BODYVAR = "More than one body request variable specified."; /** Error message. */ String ANN_TWICE = "Annotation %% is specified twice."; /** Error message. */ String INV_TEMPLATE = "Invalid path template: \"%\"."; /** Error message. */ String INV_VARNAME = "Invalid variable name: $%."; /** Error message. */ String INV_CODE = "Invalid error code: '%'."; /** Error message. */ String INV_PRIORITY = "Errors must be of the same priority (\"%\" vs \"%\")."; /** Error message. */ String INV_ERR_SAME = "The same error has been specified twice: \"%\"."; /** Error message. */ String INV_NONS = "No namespace declared for '%'."; /** Error message. */ String INV_VARTYPE = "Variable $% must inherit from %."; /** Error message. */ String UNKNOWN_VAR = "Variable $% is not specified as argument."; /** Error message. */ String VAR_ASSIGNED = "Variable $% is specified more than once."; /** Error message. */ String VAR_UNDEFINED = "Variable $% is not assigned by the annotations."; /** Error message. */ String UNKNOWN_SER = "Unknown serialization parameter \"%\"."; /** Error message. */ String UNEXP_NODE = "Unexpected node: %."; /** Error message. */ String HEAD_METHOD = "HEAD method must return a single 'restxq:response' element."; /** Error message. */ String METHOD_VALUE = "Method % does not allow values."; /** Error message. */ String INPUT_CONV = "Input could not be converted: %"; /** Error message. */ String PATH_CONFLICT = "Path \"%\" assigned to several functions:%"; /** Error message. */ String ERROR_CONFLICT = "Error \"%\" matched by several functions:%"; /** Error message. */ String NO_VALUE = "'%' element has no string value."; /** QName. */ QNm Q_STATUS = new QNm(STATUS); /** QName. */ QNm Q_REASON = new QNm(REASON); /** QName. */ QNm Q_MESSAGE = new QNm(MESSAGE); /** QName. */ QNm Q_NAME = new QNm(NAME); /** QName. */ QNm Q_VALUE = new QNm(VALUE); /** Serializer node test. */ NodeTest OUTPUT_SERIAL = new NodeTest(FuncOptions.Q_SPARAM); /** HTTP Response test. */ NodeTest HTTP_RESPONSE = new NodeTest(new QNm(RESPONSE, HTTP_URI)); /** RESTXQ Response test. */ NodeTest REST_RESPONSE = new NodeTest(new QNm(RESPONSE, REST_URI)); /** RESTXQ Redirect test. */ NodeTest REST_REDIRECT = new NodeTest(new QNm(REDIRECT, REST_URI)); /** RESTXQ Forward test. */ NodeTest REST_FORWARD = new NodeTest(new QNm(FORWARD, REST_URI)); /** HTTP Header test. */ NodeTest HTTP_HEADER = new NodeTest(new QNm(HEADER, HTTP_URI)); } basex-8.5.1/basex-api/src/main/java/org/basex/http/restxq/RestXqWadl.java000066400000000000000000000111041274071665100262410ustar00rootroot00000000000000package org.basex.http.restxq; import static org.basex.http.restxq.RestXqText.*; import static org.basex.util.Token.*; import java.util.*; import java.util.regex.*; import org.basex.http.*; import org.basex.query.func.inspect.*; import org.basex.query.value.item.*; import org.basex.query.value.node.*; import org.basex.query.var.*; import org.basex.util.hash.*; import org.basex.util.list.*; /** * This class returns a Web Application Description Language (WADL) file, * listing all available RESTXQ services. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class RestXqWadl { /** HTTP context. */ private final HTTPContext http; /** * Constructor. * @param http HTTP context */ RestXqWadl(final HTTPContext http) { this.http = http; } /** * Returns a WADL description for all available URIs. * @param modules available modules * @return WADL description */ synchronized FElem create(final HashMap modules) { // create root nodes final FElem application = new FElem(WADL + "application", WADL_URI).declareNS(); final String base = http.req.getRequestURL().toString(); final FElem resources = elem("resources", application).add("base", base); // create children final TreeMap map = new TreeMap<>(); for(final RestXqModule mod : modules.values()) { for(final RestXqFunction func : mod.functions()) { if(func.path == null) continue; final TokenObjMap xqdoc = func.function.doc(); final String path = func.path.toString(); final String methods = func.methods.toString().replaceAll("[^A-Z ]", ""); // create resource final FElem resource = new FElem(WADL + "resource", WADL_URI).add("path", path); map.put(path + '?' + methods, resource); // add documentation for path variables final Matcher var = Pattern.compile("\\$[^}]*").matcher(path); while(var.find()) { addParam(var.group().substring(1), "template", resource, xqdoc, func); } // create method, add function documentation final FElem method = elem("method", resource).add("name", methods); final TokenList descs = xqdoc != null ? xqdoc.get(InspectText.DOC_DESCRIPTION) : null; if(descs != null) for(final byte[] desc : descs) addDoc(desc, method); // create request final FElem request = elem("request", method); for(final RestXqParam rxp : func.queryParams) addParam(rxp.name, "query", request, xqdoc, func); for(final RestXqParam rxp : func.formParams) addParam(rxp.name, "query", request, xqdoc, func); for(final RestXqParam rxp : func.headerParams) addParam(rxp.name, "header", request, xqdoc, func); // create response final FElem response = elem("response", method); final FElem representation = elem("representation", response); representation.add("mediaType", HTTPContext.mediaType(func.output).toString()); } } // add resources in sorted order for(final FElem elem : map.values()) resources.add(elem); return application; } /** * Adds a parameter and its documentation to the specified element. * @param name name of parameter * @param style style * @param root root element * @param xqdoc documentation * @param func function */ private static void addParam(final String name, final String style, final FElem root, final TokenObjMap xqdoc, final RestXqFunction func) { final FElem param = elem("param", root); param.add("name", name).add("style", style); final QNm qn = new QNm(name); for(final Var var : func.function.args) { if(var.name.eq(qn) && var.type != null) { param.add("type", var.type.toString()); } } addDoc(Inspect.doc(xqdoc, token(name)), param); } /** * Creates an element. * @param name name of element * @param parent parent node * @return element node */ private static FElem elem(final String name, final FElem parent) { final FElem elem = new FElem(WADL + name, WADL_URI); if(parent != null) parent.add(elem); return elem; } /** * Adds a documentation element to the specified element. * @param xqdoc documentation (may be {@code null}) * @param parent parent node */ private static void addDoc(final byte[] xqdoc, final FElem parent) { if(xqdoc == null) return; final FElem doc = elem("doc", parent); doc.namespaces().add(EMPTY, token(XHTML_URL)); Inspect.add(xqdoc, doc); } } basex-8.5.1/basex-api/src/main/java/org/basex/http/webdav/000077500000000000000000000000001274071665100232655ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/java/org/basex/http/webdav/WebDAVCode.java000066400000000000000000000031231274071665100257720ustar00rootroot00000000000000package org.basex.http.webdav; import java.io.*; import org.basex.server.*; import org.basex.util.*; import com.bradmcevoy.http.exceptions.*; /** * Code container. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen * @param return type */ abstract class WebDAVCode { /** Resource. */ private final WebDAVResource resource; /** * Constructor. * @param resource resource */ WebDAVCode(final WebDAVResource resource) { this.resource = resource; } /** * Runs the contained code. * @return result * @throws BadRequestException bad request exception * @throws NotAuthorizedException not authorized exception */ final E eval() throws BadRequestException, NotAuthorizedException { try { final E ret = get(); if(ret == null) run(); return ret; } catch(final LoginException ex) { throw new NotAuthorizedException(Util.message(ex), resource); } catch(final IOException ex) { throw new BadRequestException(resource, Util.message(ex)); } } /** * Runs the contained code, throwing no exception. * @return result */ final E evalNoEx() { try { return eval(); } catch(final Exception ex) { Util.errln(ex); return null; } } /** * Method to run, returning some output. * @return result * @throws IOException I/O exception */ @SuppressWarnings("unused") E get() throws IOException { return null; } /** * Method to run. * @throws IOException I/O exception */ @SuppressWarnings("unused") void run() throws IOException { } } basex-8.5.1/basex-api/src/main/java/org/basex/http/webdav/WebDAVCookie.java000066400000000000000000000034171274071665100263370ustar00rootroot00000000000000package org.basex.http.webdav; import com.bradmcevoy.http.*; /** * Wrapper around {@link javax.servlet.http.Cookie}, which in addition * implements {@link Cookie}.
* This implementation is the same as the implementation of * {@code ServletCookie} found in {@code milton-servlet}. Since this is one of * the few classes which is needed from that library, the source was integrated * into BaseX. * * @author Milton Development Team * @author BaseX Team 2005-16, BSD License * @author Rositsa Shadura * @author Dimitar Popov */ final class WebDAVCookie implements Cookie { /** Wrapped instance. */ final javax.servlet.http.Cookie cookie; /** * Constructor. * @param cookie servlet cookie */ WebDAVCookie(final javax.servlet.http.Cookie cookie) { this.cookie = cookie; } @Override public int getVersion() { return cookie.getVersion(); } @Override public void setVersion(final int version) { cookie.setVersion(version); } @Override public String getName() { return cookie.getName(); } @Override public String getValue() { return cookie.getValue(); } @Override public void setValue(final String value) { cookie.setValue(value); } @Override public boolean getSecure() { return cookie.getSecure(); } @Override public void setSecure(final boolean secure) { cookie.setSecure(secure); } @Override public int getExpiry() { return cookie.getMaxAge(); } @Override public void setExpiry(final int expiry) { cookie.setMaxAge(expiry); } @Override public String getPath() { return cookie.getPath(); } @Override public void setPath(final String path) { cookie.setPath(path); } @Override public String getDomain() { return cookie.getDomain(); } @Override public void setDomain(final String domain) { cookie.setDomain(domain); } } basex-8.5.1/basex-api/src/main/java/org/basex/http/webdav/WebDAVDatabase.java000066400000000000000000000016771274071665100266400ustar00rootroot00000000000000package org.basex.http.webdav; import java.io.*; /** * WebDAV resource representing a collection database. * * @author BaseX Team 2005-16, BSD License * @author Rositsa Shadura * @author Dimitar Popov */ final class WebDAVDatabase extends WebDAVFolder { /** * Constructor. * @param meta resource meta data * @param service service */ WebDAVDatabase(final WebDAVMetaData meta, final WebDAVService service) { super(meta, service); } @Override public String getName() { return meta.db; } @Override protected void del() throws IOException { service.dropDb(meta.db); } @Override protected void rename(final String name) throws IOException { service.renameDb(meta.db, name); } @Override protected void copyToRoot(final String name) throws IOException { service.copyDb(meta.db, name); } @Override protected void moveToRoot(final String name) throws IOException { rename(name); } } basex-8.5.1/basex-api/src/main/java/org/basex/http/webdav/WebDAVFactory.java000066400000000000000000000050151274071665100265310ustar00rootroot00000000000000package org.basex.http.webdav; import javax.servlet.http.*; import org.basex.http.*; import org.basex.server.*; import org.basex.util.*; import com.bradmcevoy.common.*; import com.bradmcevoy.http.*; /** * WebDAV resource factory. Main class for generating WebDAV resources. * * @author BaseX Team 2005-16, BSD License * @author Rositsa Shadura * @author Dimitar Popov */ final class WebDAVFactory implements ResourceFactory { /** Thread local variable to hold the current context. */ private static final ThreadLocal SERVICES = new ThreadLocal<>(); /** * Creates a new service. * @param http http context */ static void init(final HTTPContext http) { SERVICES.set(new WebDAVService(http)); } /** * Closes the database session. */ static void close() { SERVICES.get().close(); SERVICES.remove(); } @Override public Resource getResource(final String host, final String dbpath) { try { final WebDAVService service = SERVICES.get(); final HttpServletRequest r = service.http.req; Path p = Path.path(dbpath); if(!r.getContextPath().isEmpty()) p = p.getStripFirst(); if(!r.getServletPath().isEmpty()) p = p.getStripFirst(); if(p.isRoot()) return new WebDAVRoot(service); final String db = p.getFirst(); return p.getLength() > 1 ? service.resource(db, p.getStripFirst().toString()) : service.dbExists(db) ? new WebDAVDatabase(new WebDAVMetaData(db, service.timestamp(db)), service) : null; } catch(final LoginException ex) { return WebDAVNotAuthorized.NOAUTH; } catch(final Exception ex) { Util.errln(ex); } return null; } /** * Creates a new resource representing a file. * @param s service instance * @param d file meta data * @return object representing the file */ static WebDAVFile file(final WebDAVService s, final WebDAVMetaData d) { return new WebDAVFile(d, s); } /** * Creates a new resource representing a folder. * @param s service instance * @param d folder meta data * @return object representing the folder */ static WebDAVFolder folder(final WebDAVService s, final WebDAVMetaData d) { return new WebDAVFolder(d, s); } /** * Creates a new resource representing a database. * @param s service instance * @param d database meta data * @return object representing the database */ static WebDAVDatabase database(final WebDAVService s, final WebDAVMetaData d) { return new WebDAVDatabase(d, s); } } basex-8.5.1/basex-api/src/main/java/org/basex/http/webdav/WebDAVFile.java000066400000000000000000000040121274071665100257750ustar00rootroot00000000000000package org.basex.http.webdav; import static org.basex.http.webdav.WebDAVUtils.*; import java.io.*; import java.util.*; import com.bradmcevoy.http.*; import com.bradmcevoy.http.exceptions.*; /** * WebDAV resource representing a file. * * @author BaseX Team 2005-16, BSD License * @author Rositsa Shadura * @author Dimitar Popov */ final class WebDAVFile extends WebDAVResource implements FileResource { /** * Constructor. * @param meta resource meta data * @param service service implementation */ WebDAVFile(final WebDAVMetaData meta, final WebDAVService service) { super(meta, service); } @Override public Long getContentLength() { return meta.size; } @Override public Date getCreateDate() { return null; } @Override public Long getMaxAgeSeconds(final Auth auth) { return null; } @Override public String processForm(final Map parameters, final Map files) { return null; } @Override public String getContentType(final String accepts) { return meta.type.toString(); } @Override public void sendContent(final OutputStream out, final Range range, final Map params, final String contentType) throws BadRequestException, NotAuthorizedException { new WebDAVCode(this) { @Override public void run() throws IOException { service.retrieve(meta.db, meta.path, meta.raw, out); } }.eval(); } @Override protected void copyToRoot(final String name) throws IOException { // document is copied to the root: create new database with it final String nm = dbName(name); service.createDb(nm); service.copyDoc(meta.db, meta.path, nm, name); } @Override protected void copyTo(final WebDAVFolder folder, final String name) throws IOException { // folder is copied to a folder in a database service.copyDoc(meta.db, meta.path, folder.meta.db, folder.meta.path + SEP + name); service.deleteDummy(folder.meta.db, folder.meta.path); } } basex-8.5.1/basex-api/src/main/java/org/basex/http/webdav/WebDAVFolder.java000066400000000000000000000077331274071665100263460ustar00rootroot00000000000000package org.basex.http.webdav; import static org.basex.http.webdav.WebDAVUtils.*; import java.io.*; import java.util.*; import org.basex.io.in.*; import org.basex.util.*; import com.bradmcevoy.http.*; import com.bradmcevoy.http.exceptions.*; /** * WebDAV resource representing a folder within a collection database. * * @author BaseX Team 2005-16, BSD License * @author Rositsa Shadura * @author Dimitar Popov */ class WebDAVFolder extends WebDAVResource implements FolderResource, DeletableCollectionResource, LockingCollectionResource { /** * Constructor. * @param meta resource meta data * @param service service implementation */ WebDAVFolder(final WebDAVMetaData meta, final WebDAVService service) { super(meta, service); } @Override public final Long getContentLength() { return null; } @Override public final String getContentType(final String accepts) { return null; } @Override public final Date getCreateDate() { return null; } @Override public final Long getMaxAgeSeconds(final Auth auth) { return null; } @Override public final void sendContent(final OutputStream out, final Range range, final Map params, final String contentType) { } @Override public final boolean isLockedOutRecursive(final Request request) { return new WebDAVCode(this) { @Override public Boolean get() throws IOException { return service.locking.conflictingLocks(meta.db, meta.path); } }.evalNoEx(); } @Override public WebDAVFolder createCollection(final String folder) throws BadRequestException, NotAuthorizedException { return new WebDAVCode(this) { @Override public WebDAVFolder get() throws IOException { return (WebDAVFolder) service.createFolder(meta.db, meta.path, folder); } }.eval(); } @Override public WebDAVResource child(final String childName) throws BadRequestException, NotAuthorizedException { return new WebDAVCode(this) { @Override public WebDAVResource get() throws IOException { return service.resource(meta.db, meta.path + SEP + childName); } }.eval(); } @Override public List getChildren() throws BadRequestException, NotAuthorizedException { return new WebDAVCode>(this) { @Override public List get() throws IOException { return service.list(meta.db, meta.path); } }.eval(); } @Override public WebDAVResource createNew(final String newName, final InputStream input, final Long length, final String contentType) throws BadRequestException, NotAuthorizedException { return new WebDAVCode(this) { @Override public WebDAVResource get() throws IOException { return service.createFile(meta.db, meta.path, newName, input); } }.eval(); } @Override public final LockToken createAndLock(final String name, final LockTimeout timeout, final LockInfo lockInfo) { try { final WebDAVResource r = createNew(name, new ArrayInput(Token.EMPTY), 0L, null); final LockResult lockResult = r.lock(timeout, lockInfo); if(lockResult.isSuccessful()) return lockResult.getLockToken(); } catch(final Exception ex) { Util.debug("Cannot lock and create requested resource", ex); } return null; } @Override protected void copyToRoot(final String name) throws IOException { // folder is copied to the root: create new database with it final String dbname = dbName(name); service.createDb(dbname); service.copyAll(meta.db, meta.path, dbname, ""); } @Override protected final void copyTo(final WebDAVFolder folder, final String name) throws IOException { // folder is copied to a folder in a database service.copyAll(meta.db, meta.path, folder.meta.db, folder.meta.path + SEP + name); service.deleteDummy(folder.meta.db, folder.meta.path); } } basex-8.5.1/basex-api/src/main/java/org/basex/http/webdav/WebDAVLockService.java000066400000000000000000000122501274071665100273320ustar00rootroot00000000000000package org.basex.http.webdav; import static org.basex.http.webdav.WebDAVUtils.*; import static org.basex.util.Token.*; import java.io.*; import java.util.Map.Entry; import java.util.*; import org.basex.core.*; import org.basex.http.*; import org.basex.io.*; import org.basex.io.out.*; import org.basex.io.serial.*; import org.basex.query.*; import org.basex.query.value.item.*; import org.basex.util.*; import org.basex.util.list.*; /** * Service managing the WebDAV locks. * * @author BaseX Team 2005-16, BSD License * @author Dimitar Popov */ final class WebDAVLockService { /** Path to WebDAV module. */ private static final String FILE = "xquery/webdav.xqm"; /** Module contents. */ private static String module; /** HTTP context. */ private final HTTPContext http; /** * Constructor. * @param http HTTP context */ WebDAVLockService(final HTTPContext http) { this.http = http; } /** * Releases the lock for the given token. * @param token lock token * @throws IOException I/O exception */ void unlock(final String token) throws IOException { execute(new WebDAVQuery("w:delete-lock($token)").bind("token", token)); } /** * Renews the lock with the given token. * @param token lock token * @throws IOException I/O exception */ void refreshLock(final String token) throws IOException { execute(new WebDAVQuery("w:refresh-lock($token)").bind("token", token)); } /** * Creates a new lock for the specified resource. * @param db database * @param p path * @param scope lock scope * @param type lock type * @param depth lock depth * @param user lock user * @param to lock timeout * @return lock token * @throws IOException I/O exception */ String lock(final String db, final String p, final String scope, final String type, final String depth, final String user, final Long to) throws IOException { initLockDb(); final String token = UUID.randomUUID().toString(); final WebDAVQuery query = new WebDAVQuery("w:create-lock(" + "$path, $token, $scope, $type, $depth, $owner, $timeout)"); query.bind("path", db + SEP + p); query.bind("token", token); query.bind("scope", scope); query.bind("type", type); query.bind("depth", depth); query.bind("owner", user); query.bind("timeout", to == null ? Long.toString(Long.MAX_VALUE) : to.toString()); execute(query); return token; } /** * Gets lock with given token. * @param token lock token * @return lock * @throws IOException I/O exception */ String lock(final String token) throws IOException { final StringList locks = execute(new WebDAVQuery("w:lock($token)").bind("token", token)); return locks.isEmpty() ? null : locks.get(0); } /** * Gets active locks for the given resource. * @param db database * @param path path * @return locks * @throws IOException I/O exception */ String lock(final String db, final String path) throws IOException { final WebDAVQuery query = new WebDAVQuery("w:locks-on($path)").bind("path", db + SEP + path); final StringList sl = execute(query); return sl.isEmpty() ? null : sl.get(0); } /** * Checks if there are active conflicting locks for the given resource. * @param db database * @param p path * @return {@code true} if there active conflicting locks * @throws IOException I/O exception */ boolean conflictingLocks(final String db, final String p) throws IOException { return !execute(new WebDAVQuery("w:conflicting-locks(" + "" + "{ $path }" + "exclusive" + "infinity" + "{ $owner }" + ")").bind("path", db + SEP + p).bind("owner", http.username)).isEmpty(); } /** * Creates the lock database if it does not exist. * @throws IOException I/O exception */ private void initLockDb() throws IOException { execute(new WebDAVQuery("w:init-lock-db()")); } /** * Executes a query. * @param query query to be executed * @return list of serialized result items * @throws IOException error during query execution */ private StringList execute(final WebDAVQuery query) throws IOException { if(module == null) { final ClassLoader cl = getClass().getClassLoader(); final InputStream is = cl.getResourceAsStream(FILE); if(is == null) throw new IOException("WebDAV module not found: " + FILE); module = string(new IOStream(is).read()); } try(final QueryProcessor qp = new QueryProcessor(query.toString(), http.context(true))) { for(final Entry entry : query.entries()) { qp.bind(entry.getKey(), entry.getValue()); } qp.qc.parseLibrary(module, FILE, qp.sc); final StringList items = new StringList(); final ArrayOutput ao = new ArrayOutput(); final Serializer ser = qp.getSerializer(ao); for(final Item it : qp.value()) { ser.serialize(it); items.add(ao.toString()); ao.reset(); } return items; } catch(final Exception ex) { Util.errln(ex.getMessage()); throw new BaseXException(ex); } } } basex-8.5.1/basex-api/src/main/java/org/basex/http/webdav/WebDAVMetaData.java000066400000000000000000000033411274071665100266020ustar00rootroot00000000000000package org.basex.http.webdav; import static org.basex.http.webdav.WebDAVUtils.*; import java.util.*; import org.basex.util.http.*; /** * Resource meta data. * * @author BaseX Team 2005-16, BSD License * @author Dimitar Popov */ final class WebDAVMetaData { /** Database owning the resource. */ final String db; /** Resource path. */ final String path; /** Resource last modification date. */ final Date mdate; /** Raw binary file flag. */ final boolean raw; /** Resource content type. */ final MediaType type; /** Resource size in bytes. */ final Long size; /** Default constructor. */ WebDAVMetaData() { this(null, 0L); } /** * Constructor. * @param db database owning the resource * @param ms resource last modification date in milliseconds */ WebDAVMetaData(final String db, final long ms) { this(db, "", ms); } /** * Constructor. * @param db database owning the resource * @param path resource path * @param ms resource last modification date in milliseconds */ WebDAVMetaData(final String db, final String path, final long ms) { this(db, path, ms, false, null, null); } /** * Constructor. * @param db database owning the resource * @param path resource path * @param ms resource last modification date in milliseconds * @param raw raw binary file flag * @param type resource media type * @param size resource size in bytes */ WebDAVMetaData(final String db, final String path, final long ms, final boolean raw, final MediaType type, final Long size) { this.db = db; this.path = stripLeadingSlash(path); this.raw = raw; this.type = type; this.size = size; mdate = ms == -1 ? null : new Date(ms); } } basex-8.5.1/basex-api/src/main/java/org/basex/http/webdav/WebDAVNotAuthorized.java000066400000000000000000000050771274071665100277310ustar00rootroot00000000000000package org.basex.http.webdav; import java.io.*; import java.util.*; import org.basex.util.*; import com.bradmcevoy.http.*; import com.bradmcevoy.http.Request.Method; /** * Dummy resource to be returned when no authorization is provided. * * @author BaseX Team 2005-16, BSD License * @author Rositsa Shadura * @author Dimitar Popov */ final class WebDAVNotAuthorized implements FolderResource, LockableResource { /** The only instance of this class. */ static final Resource NOAUTH = new WebDAVNotAuthorized(); /** Constructor. */ private WebDAVNotAuthorized() { } @Override public String getRealm() { return Prop.NAME; } @Override public Date getModifiedDate() { return null; } @Override public String getUniqueId() { return null; } @Override public Object authenticate(final String u, final String p) { return null; } @Override public boolean authorise(final Request request, final Method method, final Auth auth) { return false; } @Override public String getName() { return null; } @Override public void sendContent(final OutputStream out, final Range range, final Map params, final String contentType) { } @Override public Long getMaxAgeSeconds(final Auth auth) { return null; } @Override public String getContentType(final String accepts) { return null; } @Override public Long getContentLength() { return null; } @Override public CollectionResource createCollection(final String newName) { return null; } @Override public Resource child(final String childName) { return null; } @Override public List getChildren() { return null; } @Override public Resource createNew(final String newName, final InputStream inputStream, final Long length, final String contentType) { return null; } @Override public void copyTo(final CollectionResource toCollection, final String name) { } @Override public void delete() { } @Override public void moveTo(final CollectionResource rDest, final String name) { } @Override public Date getCreateDate() { return null; } @Override public String checkRedirect(final Request request) { return null; } @Override public LockResult lock(final LockTimeout lockTimeout, final LockInfo lockInfo) { return null; } @Override public LockResult refreshLock(final String s) { return null; } @Override public void unlock(final String s) { } @Override public LockToken getCurrentLock() { return null; } } basex-8.5.1/basex-api/src/main/java/org/basex/http/webdav/WebDAVQuery.java000066400000000000000000000021771274071665100262350ustar00rootroot00000000000000package org.basex.http.webdav; import java.util.*; import java.util.Map.Entry; /** * Query builder. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class WebDAVQuery { /** String query. */ private final String query; /** Bindings. */ private final HashMap bindings = new HashMap<>(); /** * Constructor. * @param query query string */ WebDAVQuery(final String query) { this.query = query; } /** * Binds a variable. * @param name name of variable (without '$' sign). * @param value value of variable * @return self reference */ WebDAVQuery bind(final String name, final String value) { bindings.put(name, value); return this; } /** * Returns the hash map entries. * @return self reference */ Set> entries() { return bindings.entrySet(); } @Override public String toString() { final StringBuilder sb = new StringBuilder(); for(final String v : bindings.keySet()) { sb.append("declare variable $").append(v).append(" external;"); } return sb.append(query).toString(); } } basex-8.5.1/basex-api/src/main/java/org/basex/http/webdav/WebDAVRequest.java000066400000000000000000000172351274071665100265610ustar00rootroot00000000000000package org.basex.http.webdav; import static org.basex.http.webdav.WebDAVUtils.*; import java.io.*; import java.net.*; import java.util.*; import java.util.Map.Entry; import javax.servlet.http.*; import org.apache.commons.fileupload.*; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.servlet.*; import org.basex.util.*; import com.bradmcevoy.http.*; import com.bradmcevoy.http.Response.ContentType; import com.bradmcevoy.http.Cookie; /** * Wrapper around {@link HttpServletRequest}, which in addition implements {@link Request}.
* This implementation is the same as the implementation of {@code ServletRequest} found in * {@code milton-servlet}. Since this is one of the few classes which is needed from that library * the source is integrated into BaseX. * * @author Milton Development Team * @author BaseX Team 2005-16, BSD License * @author Rositsa Shadura * @author Dimitar Popov */ final class WebDAVRequest extends AbstractRequest { /** Destination string. */ private static final String DESTINATION = "Destination"; /** HTTP servlet request. */ private final HttpServletRequest req; /** Request method. */ private final Method method; /** Request URL. */ private final String url; /** Authentication. */ private Auth auth; /** Content types map. */ private static final Map CONTENT_TYPES = new EnumMap<>(ContentType.class); /** Type contents map. */ private static final Map TYPE_CONTENTS = new HashMap<>(); static { CONTENT_TYPES.put(ContentType.HTTP, Response.HTTP); CONTENT_TYPES.put(ContentType.MULTIPART, Response.MULTIPART); CONTENT_TYPES.put(ContentType.XML, Response.XML); for(final Entry entry : CONTENT_TYPES.entrySet()) TYPE_CONTENTS.put(entry.getValue(), entry.getKey()); } /** * Constructor. * @param req HTTP servlet request */ WebDAVRequest(final HttpServletRequest req) { this.req = req; method = Method.valueOf(req.getMethod()); url = decode(req.getRequestURL().toString()); } @Override public String getFromAddress() { return req.getRemoteHost(); } @Override public String getRequestHeader(final Header header) { final String value = req.getHeader(header.code); return header.code.equals(DESTINATION) ? decode(value) : value; } @Override public Method getMethod() { return method; } @Override public String getAbsoluteUrl() { return url; } @Override public String getRemoteAddr() { return req.getRemoteAddr(); } @Override public Auth getAuthorization() { if(auth == null) { final String enc = getRequestHeader(Header.AUTHORIZATION); if(enc != null && !enc.isEmpty()) auth = new Auth(enc); } return auth; } @Override public void setAuthorization(final Auth a) { auth = a; } @Override public InputStream getInputStream() throws IOException { return req.getInputStream(); } @Override public Map getHeaders() { final Map map = new HashMap<>(); final Enumeration en = req.getHeaderNames(); while(en.hasMoreElements()) { final String name = en.nextElement(); final String val = req.getHeader(name); map.put(name, val); } return map; } @Override public Cookie getCookie(final String name) { for(final javax.servlet.http.Cookie c : req.getCookies()) { if(c.getName().equals(name)) return new WebDAVCookie(c); } return null; } @Override public List getCookies() { final List list = new ArrayList<>(); for(final javax.servlet.http.Cookie c : req.getCookies()) { list.add(new WebDAVCookie(c)); } return list; } @Override public void parseRequestParameters(final Map params, final Map files) throws RequestParseException { try { if(isMultiPart()) { parseQueryString(params, req.getQueryString()); final List items = new ServletFileUpload().parseRequest(req); for(final FileItem item : items) { if(item.isFormField()) params.put(item.getFieldName(), item.getString()); else files.put(item.getFieldName(), new FileItemWrapper(item)); } } else { final Enumeration en = req.getParameterNames(); while(en.hasMoreElements()) { final String nm = en.nextElement(); final String val = req.getParameter(nm); params.put(nm, val); } } } catch(final FileUploadException ex) { throw new RequestParseException("FileUploadException", ex); } catch(final Throwable ex) { throw new RequestParseException(ex.getMessage(), ex); } } /** * Parse the query string. * @param map parsed key-values will be stored here * @param qs query string */ private static void parseQueryString(final Map map, final String qs) { if(qs == null) return; for(final String nv : Strings.split(qs, '&')) { final String[] parts = Strings.split(nv, '=', 2); final String key = parts[0]; String val = null; if(parts.length > 1) { try { val = URLDecoder.decode(parts[1], Strings.UTF8); } catch(final UnsupportedEncodingException ex) { throw new RuntimeException(ex); } } map.put(key, val); } } /** * Request content type. * @return the content type of the current request */ private ContentType getRequestContentType() { final String s = req.getContentType(); if(s == null) return null; if(s.contains(Response.MULTIPART)) return ContentType.MULTIPART; return TYPE_CONTENTS.get(s); } /** * Is the content type of the request a multi-part? * @return {@code true} if the request is multi-part */ private boolean isMultiPart() { return getRequestContentType() == ContentType.MULTIPART; } } /** * Wrapper around {@link FileItem}, which in addition implements * {@link com.bradmcevoy.http.FileItem}.
* This implementation is the same as the implementation of * {@code FileItemWrapper} found in {@code milton-servlet}. Since this is one of * the few classes which is needed from that library, the source is integrated * into BaseX. * @author Milton Development Team * @author BaseX Team 2005-16, BSD License * @author Rositsa Shadura * @author Dimitar Popov */ class FileItemWrapper implements com.bradmcevoy.http.FileItem { /** Wrapped file item. */ private final FileItem file; /** File name. */ private final String name; /** * Strip path information provided by IE. * @param string string * @return stripped string */ private static String fixIEFileName(final String string) { final int pos = string.lastIndexOf('\\'); return pos < 0 ? string : string.substring(pos + 1); } /** * Constructor. * @param file file item */ FileItemWrapper(final FileItem file) { this.file = file; name = fixIEFileName(file.getName()); } @Override public String getContentType() { return file.getContentType(); } @Override public String getFieldName() { return file.getFieldName(); } @Override public InputStream getInputStream() { try { return file.getInputStream(); } catch(final IOException ex) { throw new RuntimeException(ex); } } @Override public OutputStream getOutputStream() { try { return file.getOutputStream(); } catch(final IOException ex) { throw new RuntimeException(ex); } } @Override public String getName() { return name; } @Override public long getSize() { return file.getSize(); } } basex-8.5.1/basex-api/src/main/java/org/basex/http/webdav/WebDAVResource.java000066400000000000000000000247421274071665100267210ustar00rootroot00000000000000package org.basex.http.webdav; import static com.bradmcevoy.http.LockResult.*; import static org.basex.http.webdav.WebDAVUtils.*; import java.io.*; import java.util.*; import com.bradmcevoy.http.LockInfo.LockDepth; import com.bradmcevoy.http.LockInfo.LockScope; import com.bradmcevoy.http.LockInfo.LockType; import com.bradmcevoy.http.Request.Method; import org.basex.core.StaticOptions.AuthMethod; import org.basex.http.*; import org.basex.util.*; import org.xml.sax.*; import org.xml.sax.helpers.*; import com.bradmcevoy.http.*; import com.bradmcevoy.http.exceptions.*; /** * WebDAV resource representing an abstract folder within a collection database. * * @author BaseX Team 2005-16, BSD License * @author Rositsa Shadura * @author Dimitar Popov */ abstract class WebDAVResource implements CopyableResource, DeletableResource, MoveableResource, LockableResource { /** Resource meta data. */ final WebDAVMetaData meta; /** WebDAV service implementation. */ final WebDAVService service; /** * Constructor. * @param meta resource meta data * @param service service */ WebDAVResource(final WebDAVMetaData meta, final WebDAVService service) { this.meta = meta; this.service = service; } @Override public Object authenticate(final String user, final String pass) { if(user == null) return null; return new WebDAVCode(this) { @Override public String get() throws IOException { service.authenticate(user, pass); return user; } }.evalNoEx(); } @Override public boolean authorise(final Request request, final Method method, final Auth auth) { // use WebDAV authorization if no default user exists or if digest authentication is specified final HTTPContext http = service.http; if(http.username.isEmpty() || http.auth == AuthMethod.DIGEST) { if(auth == null || auth.getTag() == null) return false; } return WebDAVService.authorize(meta.db); } @Override public String checkRedirect(final Request request) { return null; } @Override public String getRealm() { return Prop.NAME; } @Override public String getUniqueId() { return null; } @Override public String getName() { return name(meta.path); } @Override public Date getModifiedDate() { return meta.mdate; } @Override public void delete() throws BadRequestException, NotAuthorizedException { new WebDAVCode(this) { @Override public void run() throws IOException { del(); } }.eval(); } @Override public void copyTo(final CollectionResource target, final String name) throws BadRequestException, NotAuthorizedException { new WebDAVCode(this) { @Override public void run() throws IOException { if(target instanceof WebDAVRoot) copyToRoot(name); else if(target instanceof WebDAVFolder) copyTo((WebDAVFolder) target, name); } }.eval(); } @Override public void moveTo(final CollectionResource target, final String name) throws BadRequestException, NotAuthorizedException { new WebDAVCode(this) { @Override public void run() throws IOException { if(target instanceof WebDAVRoot) moveToRoot(name); else if(target instanceof WebDAVFolder) moveTo((WebDAVFolder) target, name); } }.eval(); } /** * Lock this resource and return a token. * * @param timeout - in seconds, or null * @param lockInfo lock info * @return - a result containing the token representing the lock if successful, * otherwise a failure reason code */ @Override public LockResult lock(final LockTimeout timeout, final LockInfo lockInfo) throws NotAuthorizedException, PreConditionFailedException, LockedException { return new WebDAVCode(this) { @Override public LockResult get() { return lockResource(timeout, lockInfo); } }.evalNoEx(); } /** * Renew the lock and return new lock info. * * @param token lock token * @return lock result */ @Override public LockResult refreshLock(final String token) throws NotAuthorizedException, PreConditionFailedException { return new WebDAVCode(this) { @Override public LockResult get() throws IOException { return refresh(token); } }.evalNoEx(); } /** * If the resource is currently locked, and the tokenId matches the current * one, unlock the resource. * * @param tokenId lock token */ @Override public void unlock(final String tokenId) throws NotAuthorizedException, PreConditionFailedException { new WebDAVCode(this) { @Override public void run() throws IOException { service.locking.unlock(tokenId); } }.evalNoEx(); } /** * Get the active lock for the current resource. * @return - the current lock if the resource is locked, or null */ @Override public LockToken getCurrentLock() { return new WebDAVCode(this) { @Override public LockToken get() throws IOException { return getCurrentActiveLock(); } }.evalNoEx(); } /** * Delete document or folder. * @throws IOException I/O exception */ void del() throws IOException { service.delete(meta.db, meta.path); } /** * Rename document or folder. * @param n new name * @throws IOException I/O exception */ void rename(final String n) throws IOException { service.rename(meta.db, meta.path, n); } /** * Copy folder to the root, creating a new database. * @param n new name of the folder (database) * @throws IOException I/O exception */ protected abstract void copyToRoot(final String n) throws IOException; /** * Copy folder to another folder. * @param f target folder * @param n new name of the folder * @throws IOException I/O exception */ protected abstract void copyTo(final WebDAVFolder f, final String n) throws IOException; /** * Move folder to the root, creating a new database. * @param n new name of the folder (database) * @throws IOException I/O exception */ void moveToRoot(final String n) throws IOException { // folder is moved to the root: create new database with it copyToRoot(n); del(); } /** * Move folder to another folder. * @param f target folder * @param n new name of the folder * @throws IOException I/O exception */ private void moveTo(final WebDAVFolder f, final String n) throws IOException { if(f.meta.db.equals(meta.db)) { // folder is moved to a folder in the same database rename(f.meta.path + SEP + n); } else { // folder is moved to a folder in another database copyTo(f, n); del(); } } /** * Lock the current resource. * @param timeout lock timeout * @param lockInfo lock info * @return lock result */ private LockResult lockResource(final LockTimeout timeout, final LockInfo lockInfo) { try { final String tokenId = service.locking.lock( meta.db, meta.path, lockInfo.scope.name().toLowerCase(Locale.ENGLISH), lockInfo.type.name().toLowerCase(Locale.ENGLISH), lockInfo.depth.name().toLowerCase(Locale.ENGLISH), lockInfo.lockedByUser, timeout.getSeconds() ); return success(new LockToken(tokenId, lockInfo, timeout)); } catch(final IOException ex) { return failed(FailureReason.ALREADY_LOCKED); } } /** * Get the active lock on the current resource. * @return the token of the active lock or {@code null} if resource is not locked * @throws IOException I/O exception */ private LockToken getCurrentActiveLock() throws IOException { final String lockInfoStr = service.locking.lock(meta.db, meta.path); return lockInfoStr == null ? null : parseLockInfo(lockInfoStr); } /** * Renew a lock with the given token. * @param token lock token * @return lock result * @throws IOException I/O exception */ private LockResult refresh(final String token) throws IOException { service.locking.refreshLock(token); final String lockInfoStr = service.locking.lock(token); final LockToken lockToken = lockInfoStr == null ? null : parseLockInfo(lockInfoStr); return lockToken == null ? failed(FailureReason.ALREADY_LOCKED) : success(lockToken); } /** * Parse the lock info. * @param lockInfo lock info as a string * @return parsed lock info bean * @throws IOException I/O exception */ private static LockToken parseLockInfo(final String lockInfo) throws IOException { try { final XMLReader reader = XMLReaderFactory.createXMLReader(); final LockTokenSaxHandler handler = new LockTokenSaxHandler(); reader.setContentHandler(handler); reader.parse(new InputSource(new StringReader(lockInfo))); return handler.lockToken; } catch(final SAXException ex) { Util.errln("Error while parsing lock info: %", ex); return null; } } /** SAX handler for lock token. */ private static final class LockTokenSaxHandler extends DefaultHandler { /** Parsed lock token. */ private final LockToken lockToken = new LockToken(null, new LockInfo(), null); /** Current element name. */ private String elementName; @Override public void startElement(final String uri, final String localName, final String name, final Attributes attributes) throws SAXException { elementName = localName; super.startElement(uri, localName, name, attributes); } @Override public void endElement(final String uri, final String localName, final String qName) throws SAXException { elementName = null; super.endElement(uri, localName, qName); } @Override public void characters(final char[] ch, final int start, final int length) { final String v = String.valueOf(ch, start, length); if("token".equals(elementName)) lockToken.tokenId = v; else if("scope".equals(elementName)) lockToken.info.scope = LockScope.valueOf(v.toUpperCase(Locale.ENGLISH)); else if("type".equals(elementName)) lockToken.info.type = LockType.valueOf(v.toUpperCase(Locale.ENGLISH)); else if("depth".equals(elementName)) lockToken.info.depth = LockDepth.valueOf(v.toUpperCase(Locale.ENGLISH)); else if("owner".equals(elementName)) lockToken.info.lockedByUser = v; else if("timeout".equals(elementName)) lockToken.timeout = LockTimeout.parseTimeout(v); } } } basex-8.5.1/basex-api/src/main/java/org/basex/http/webdav/WebDAVResponse.java000066400000000000000000000063121274071665100267210ustar00rootroot00000000000000package org.basex.http.webdav; import java.io.*; import java.util.*; import javax.servlet.http.*; import com.bradmcevoy.http.*; import com.bradmcevoy.http.Cookie; /** * Wrapper around {@link HttpServletResponse}, which in addition implements {@link Response}.
* This implementation is the same as the implementation of {@code ServletResponse} found in * {@code milton-servlet}. Since this is one of the few classes which is needed from that library, * the source was integrated into BaseX. * * @author Milton Development Team * @author BaseX Team 2005-16, BSD License * @author Rositsa Shadura * @author Dimitar Popov */ final class WebDAVResponse extends AbstractResponse { /** HTTP servlet response. */ private final HttpServletResponse res; /** Response headers. */ private final Map headers = new HashMap<>(); /** Response status. */ private Status status; /** * Constructor. * @param res HTTP servlet response */ WebDAVResponse(final HttpServletResponse res) { this.res = res; } @Override protected void setAnyDateHeader(final Header name, final Date date) { res.setDateHeader(name.code, date.getTime()); } @Override public String getNonStandardHeader(final String code) { return headers.get(code); } @Override public void setNonStandardHeader(final String name, final String value) { res.addHeader(name, value); headers.put(name, value); } @Override public void setStatus(final Status s) { res.setStatus(s.code); status = s; } @Override public Status getStatus() { return status; } @Override public OutputStream getOutputStream() { try { return res.getOutputStream(); } catch(final IOException ex) { throw new RuntimeException(ex); } } @Override public void close() { try { res.flushBuffer(); res.getOutputStream().flush(); } catch(final IOException ex) { throw new RuntimeException(ex); } } @Override public void sendRedirect(final String url) { try { res.sendRedirect(res.encodeRedirectURL(url)); } catch(final IOException ex) { throw new RuntimeException(ex); } } @Override public Map getHeaders() { return Collections.unmodifiableMap(headers); } @Override public void setAuthenticateHeader(final List challenges) { for(final String ch : challenges) { res.addHeader(Header.WWW_AUTHENTICATE.code, ch); } } @Override public Cookie setCookie(final Cookie cookie) { if(cookie instanceof WebDAVCookie) { res.addCookie(((WebDAVCookie) cookie).cookie); return cookie; } final javax.servlet.http.Cookie c = new javax.servlet.http.Cookie( cookie.getName(), cookie.getValue()); c.setDomain(cookie.getDomain()); c.setMaxAge(cookie.getExpiry()); c.setPath(cookie.getPath()); c.setSecure(cookie.getSecure()); c.setVersion(cookie.getVersion()); res.addCookie(c); return new WebDAVCookie(c); } @Override public Cookie setCookie(final String name, final String value) { final javax.servlet.http.Cookie c = new javax.servlet.http.Cookie(name, value); res.addCookie(c); return new WebDAVCookie(c); } } basex-8.5.1/basex-api/src/main/java/org/basex/http/webdav/WebDAVRoot.java000066400000000000000000000043671274071665100260560ustar00rootroot00000000000000package org.basex.http.webdav; import static org.basex.http.webdav.WebDAVUtils.*; import java.io.*; import java.util.*; import com.bradmcevoy.http.*; import com.bradmcevoy.http.exceptions.*; /** * WebDAV resource representing the list of all databases. * * @author BaseX Team 2005-16, BSD License * @author Rositsa Shadura * @author Dimitar Popov */ final class WebDAVRoot extends WebDAVFolder { /** * Constructor. * @param service service */ WebDAVRoot(final WebDAVService service) { super(new WebDAVMetaData(), service); } @Override public String getName() { return ""; } @Override public void copyTo(final CollectionResource toCollection, final String name) { // this method must do nothing } @Override public void delete() { // this method must do nothing } @Override public void moveTo(final CollectionResource rDest, final String name) { // this method must do nothing } @Override public WebDAVResource child(final String name) { return new WebDAVCode(this) { @Override public WebDAVResource get() throws IOException { return service.dbExists(name) ? new WebDAVDatabase(new WebDAVMetaData(name, service.timestamp(name)), service) : null; } }.evalNoEx(); } @Override public List getChildren() { return new WebDAVCode>(this) { @Override public List get() throws IOException { return service.listDbs(); } }.evalNoEx(); } @Override public WebDAVDatabase createCollection(final String name) throws BadRequestException, NotAuthorizedException { return new WebDAVCode(this) { @Override public WebDAVDatabase get() throws IOException { return (WebDAVDatabase) service.createDb(dbName(name)); } }.eval(); } @Override public WebDAVResource createNew(final String name, final InputStream input, final Long length, final String contentType) throws BadRequestException, NotAuthorizedException { return new WebDAVCode(this) { @Override public WebDAVResource get() throws IOException { return service.createFile(name, input); } }.eval(); } } basex-8.5.1/basex-api/src/main/java/org/basex/http/webdav/WebDAVService.java000066400000000000000000000442311274071665100265250ustar00rootroot00000000000000package org.basex.http.webdav; import static org.basex.http.webdav.WebDAVUtils.*; import static org.basex.query.func.Function.*; import java.io.*; import java.util.*; import java.util.Map.Entry; import java.util.List; import org.basex.api.client.*; import org.basex.core.*; import org.basex.core.cmd.*; import org.basex.core.cmd.Set; import org.basex.http.*; import org.basex.io.in.*; import org.basex.io.serial.*; import org.basex.query.func.db.*; import org.basex.util.*; import org.basex.util.http.*; import org.basex.util.list.*; /** * Service handling the various WebDAV operations. * * @author BaseX Team 2005-16, BSD License * @author Dimitar Popov */ final class WebDAVService { /** Name of the database with the WebDAV locks. */ private static final String WEBDAV_DB = "~webdav"; /** HTTP context. */ final HTTPContext http; /** Locking service. */ final WebDAVLockService locking; /** Session. */ private LocalSession ls; /** * Constructor. * @param http http context */ WebDAVService(final HTTPContext http) { this.http = http; locking = new WebDAVLockService(http); } /** * Closes an open session. */ void close() { if(ls != null) ls.close(); } /** * Authenticates the user with the given password. * @param user user name * @param pass password * @throws IOException I/O exception */ void authenticate(final String user, final String pass) throws IOException { http.credentials(user, pass); session(); } /** * Checks if the user is authorized to perform the given action. * @param db database (can be {@code null}) * @return {@code true} if the user is authorized */ static boolean authorize(final String db) { return !WEBDAV_DB.equals(db); } /** * Checks a folder for a dummy document and delete it. * @param db database * @param path path * @throws IOException I/O exception */ void deleteDummy(final String db, final String path) throws IOException { final String dummy = path + SEP + DUMMY; if(!pathExists(db, dummy)) return; // path contains dummy document final LocalSession session = session(); session.execute(new Open(db)); session.execute(new Delete(dummy)); } /** * Checks if the specified database exists. * @param db database to be found * @return result of check * @throws IOException I/O exception */ boolean dbExists(final String db) throws IOException { final WebDAVQuery query = new WebDAVQuery(_DB_EXISTS.args("$db")).bind("db", db); return execute(query).equals(Text.TRUE); } /** * Retrieves the last modified timestamp of a database. * @param db database * @return timestamp in milliseconds. * @throws IOException I/O exception */ long timestamp(final String db) throws IOException { final WebDAVQuery query = new WebDAVQuery(DATA.args(_DB_INFO.args("$db") + "/descendant::" + DbFn.toName(Text.TIMESTAMP) + "[1]")).bind("db", db); return DateTime.parse(execute(query)).getTime(); } /** * Retrieves meta data about the resource at the given path. * @param db database * @param path resource path * @return resource meta data * @throws IOException I/O exception */ private WebDAVMetaData metaData(final String db, final String path) throws IOException { final WebDAVQuery query = new WebDAVQuery( "let $a := " + _DB_LIST_DETAILS.args("$db", "$path") + "[1] " + "return string-join(($a/@raw, $a/@content-type, $a/@modified-date, $a/@size, $a),out:tab())"); query.bind("db", db); query.bind("path", path); final String[] result = results(query); final boolean raw = Boolean.parseBoolean(result[0]); final MediaType type = new MediaType(result[1]); final long mod = DateTime.parse(result[2]).getTime(); final Long size = raw ? Long.valueOf(result[3]) : null; final String pth = stripLeadingSlash(result[4]); return new WebDAVMetaData(db, pth, mod, raw, type, size); } /** * Deletes a document or folder. * @param db database * @param path path * @throws IOException I/O exception */ void delete(final String db, final String path) throws IOException { final LocalSession session = session(); session.execute(new Open(db)); session.execute(new Delete(path)); // create dummy if parent is an empty folder final int ix = path.lastIndexOf(SEP); if(ix > 0) createDummy(db, path.substring(0, ix)); } /** * Renames a document or folder. * @param db database * @param path path * @param npath new path * @throws IOException I/O exception */ void rename(final String db, final String path, final String npath) throws IOException { final LocalSession session = session(); session.execute(new Open(db)); session.execute(new Rename(path, npath)); // create dummy if old parent is an empty folder final int i1 = path.lastIndexOf(SEP); if(i1 > 0) createDummy(db, path.substring(0, i1)); // delete dummy if new parent is an empty folder final int i2 = npath.lastIndexOf(SEP); if(i2 > 0) deleteDummy(db, npath.substring(0, i2)); } /** * Copies a document to the specified target. * @param db source database * @param path source path * @param tdb target database * @param tpath target path * @throws IOException I/O exception */ void copyDoc(final String db, final String path, final String tdb, final String tpath) throws IOException { final WebDAVQuery query = new WebDAVQuery( "declare option db:chop 'false';" + "if(" + _DB_IS_RAW.args("$db", "$path") + ')' + " then " + _DB_STORE.args("$tdb", "$tpath", _DB_RETRIEVE.args("$db", "$path")) + " else " + _DB_ADD.args("$tdb", _DB_OPEN.args("$db", "$path"), "$tpath")); query.bind("db", db); query.bind("path", path); query.bind("tdb", tdb); query.bind("tpath", tpath); execute(query); } /** * Copies all documents in a folder to another folder. * @param db source database * @param path source path * @param tdb target database * @param tpath target folder * @throws IOException I/O exception */ void copyAll(final String db, final String path, final String tdb, final String tpath) throws IOException { final WebDAVQuery query = new WebDAVQuery( "declare option db:chop 'false'; " + "for $d in " + _DB_LIST.args("$db", "$path") + "let $t := $tpath ||'/'|| substring($d, string-length($path) + 1) return " + "if(" + _DB_IS_RAW.args("$db", "$d") + ") " + "then " + _DB_STORE.args("$tdb", "$t", _DB_RETRIEVE.args("$db", "$d")) + " else " + _DB_ADD.args("$tdb", _DB_OPEN.args("$db", "$d"), "$t")); query.bind("db", db); query.bind("path", path); query.bind("tdb", tdb); query.bind("tpath", tpath); execute(query); } /** * Writes a file to the specified output stream. * @param db database * @param path path * @param raw is the file a raw file * @param out output stream * @throws IOException I/O exception */ void retrieve(final String db, final String path, final boolean raw, final OutputStream out) throws IOException { session().setOutputStream(out); final String string = SerializerOptions.USE_CHARACTER_MAPS.arg(" =&#xA0;") + (raw ? _DB_RETRIEVE : _DB_OPEN).args("$db", "$path") + "[1]"; final WebDAVQuery query = new WebDAVQuery(string); query.bind("db", db); query.bind("path", path); execute(query); } /** * Creates an empty database with the given name. * @param db database name * @return object representing the newly created database * @throws IOException I/O exception */ WebDAVResource createDb(final String db) throws IOException { session().execute(new CreateDB(db)); return WebDAVFactory.database(this, new WebDAVMetaData(db, timestamp(db))); } /** * Drops the database with the given name. * @param db database name * @throws IOException I/O exception */ void dropDb(final String db) throws IOException { session().execute(new DropDB(db)); } /** * Renames the database with the given name. * @param old database name * @param db new name * @throws IOException I/O exception */ void renameDb(final String old, final String db) throws IOException { session().execute(new AlterDB(old, dbName(db))); } /** * Copies the database with the given name. * @param old database name * @param db new database name * @throws IOException I/O exception */ void copyDb(final String old, final String db) throws IOException { session().execute(new Copy(old, dbName(db))); } /** * Lists the direct children of a path. * @param db database * @param path path * @return children * @throws IOException I/O exception */ List list(final String db, final String path) throws IOException { final WebDAVQuery query = new WebDAVQuery(STRING_JOIN.args( _DB_LIST_DETAILS.args("$db", "$path") + " ! (" + "@raw,@content-type,@modified-date,@size," + SUBSTRING_AFTER.args("text()", "$path") + ')', "out:tab()")); query.bind("db", db); query.bind("path", path); final String[] result = results(query); final HashSet paths = new HashSet<>(); final List ch = new ArrayList<>(); final int rs = result.length; for(int r = 0; r < rs; r += 5) { final boolean raw = Boolean.parseBoolean(result[r]); final MediaType ctype = new MediaType(result[r + 1]); final long mod = DateTime.parse(result[r + 2]).getTime(); final Long size = raw ? Long.valueOf(result[r + 3]) : null; final String pth = stripLeadingSlash(result[r + 4]); final int ix = pth.indexOf(SEP); // check if document or folder if(ix < 0) { if(!pth.equals(DUMMY)) ch.add(WebDAVFactory.file(this, new WebDAVMetaData(db, path + SEP + pth, mod, raw, ctype, size))); } else { final String dir = path + SEP + pth.substring(0, ix); if(paths.add(dir)) ch.add(WebDAVFactory.folder(this, new WebDAVMetaData(db, dir, mod))); } } return ch; } /** * Lists all databases. * @return a list of database resources. * @throws IOException I/O exception */ List listDbs() throws IOException { final WebDAVQuery query = new WebDAVQuery(STRING_JOIN.args( _DB_LIST_DETAILS.args() + "[. != $db] ! (text(), @modified-date)", "out:tab()")); query.bind("db", WEBDAV_DB); final String[] result = results(query); final List dbs = new ArrayList<>(); final int rs = result.length; for(int r = 0; r < rs; r += 2) { final String name = result[r]; final long mod = DateTime.parse(result[r + 1]).getTime(); dbs.add(WebDAVFactory.database(this, new WebDAVMetaData(name, mod))); } return dbs; } /** * Creates a folder at the given path. * @param db database * @param path path * @param name new folder name * @return new folder resource * @throws IOException I/O exception */ WebDAVResource createFolder(final String db, final String path, final String name) throws IOException { deleteDummy(db, path); final String newFolder = path + SEP + name; createDummy(db, newFolder); return WebDAVFactory.folder(this, new WebDAVMetaData(db, newFolder, timestamp(db))); } /** * Gets the resource at the given path. * @param db database * @param path path * @return resource * @throws IOException I/O exception */ WebDAVResource resource(final String db, final String path) throws IOException { return exists(db, path) ? WebDAVFactory.file(this, metaData(db, path)) : pathExists(db, path) ? WebDAVFactory.folder(this, new WebDAVMetaData(db, path, timestamp(db))) : null; } /** * Adds the given file to the specified path. * @param db database * @param path path * @param name file name * @param in file content * @return object representing the newly added file * @throws IOException I/O exception */ WebDAVResource createFile(final String db, final String path, final String name, final InputStream in) throws IOException { final LocalSession session = session(); session.execute(new Open(db)); final String dbp = path.isEmpty() ? name : path + SEP + name; // delete old resource if it already exists if(pathExists(db, dbp)) { session.execute(new Open(db)); session.execute(new Delete(dbp)); } else { // otherwise, delete dummy file deleteDummy(db, path); } return addFile(db, dbp, in); } /** * Creates a new database from the given file. * @param n file name * @param in file content * @return object representing the newly created database * @throws IOException I/O exception */ WebDAVResource createFile(final String n, final InputStream in) throws IOException { return addFile(null, n, in); } /** * Checks if any of the resources starts with the given path. * @param db name of database * @param path path * @return {@code true} if there are resources with the given prefix * @throws IOException I/O exception */ private boolean pathExists(final String db, final String path) throws IOException { final WebDAVQuery query = new WebDAVQuery(EXISTS.args(_DB_LIST.args("$db", "$path"))); query.bind("db", db); query.bind("path", path); return execute(query).equals(Text.TRUE); } /** * Checks if any resource with the specified name exists. * @param db name of database * @param path resource path * @return {@code true} if there are resources with the name * @throws IOException I/O exception */ private boolean exists(final String db, final String path) throws IOException { final WebDAVQuery query = new WebDAVQuery(_DB_EXISTS.args("$db", "$path")); query.bind("db", db); query.bind("path", path); return execute(query).equals(Text.TRUE); } /** * Creates a database with the given name and add the given document. * @param db database name * @param in data stream * @return object representing the newly created database * @throws IOException I/O exception */ private WebDAVResource createDb(final String db, final InputStream in) throws IOException { session().create(db, in); return WebDAVFactory.database(this, new WebDAVMetaData(db, timestamp(db))); } /** * Adds a document with the specified name to the given path. * @param db database * @param path path where the document will be added * @param in data stream * @return object representing the newly added XML * @throws IOException I/O exception */ private WebDAVResource addXML(final String db, final String path, final InputStream in) throws IOException { final LocalSession session = session(); session.execute(new Set(MainOptions.CHOP, false)); session.execute(new Open(db)); session.add(path, in); return WebDAVFactory.file(this, new WebDAVMetaData(db, path, timestamp(db), false, MediaType.APPLICATION_XML, null)); } /** * Adds a binary file with the specified name to the given path. * @param db database * @param path path where the file will be stored * @param in data stream * @return object representing the newly added file * @throws IOException I/O exception */ private WebDAVResource store(final String db, final String path, final InputStream in) throws IOException { final LocalSession session = session(); session.execute(new Open(db)); session.store(path, in); return WebDAVFactory.file(this, metaData(db, path)); } /** * Adds a file in to the given path. * @param db database * @param path path * @param in file content * @return object representing the newly added file * @throws IOException I/O exception */ private WebDAVResource addFile(final String db, final String path, final InputStream in) throws IOException { // use 4MB as buffer input try(final BufferInput bi = new BufferInput(in, 1 << 22)) { // guess the content type from the first character if(peek(bi) == '<') { try { // add input as XML document return db == null ? createDb(dbName(path), bi) : addXML(db, path, bi); } catch(final IOException ex) { // reset stream if it did not work out try { bi.reset(); } catch(final IOException e) { // throw original exception if input cannot be reset throw ex; } } } // add input as raw file final String d; if(db == null) { d = dbName(path); createDb(d); } else { d = db; } return store(d, path, bi); } } /** * Checks if a folder is empty and create a dummy document. * @param db database * @param path path * @throws IOException I/O exception */ private void createDummy(final String db, final String path) throws IOException { // check if path is a folder and is empty if(path.matches("[^/]") || pathExists(db, path)) return; final LocalSession session = session(); session.execute(new Open(db)); session.store(path + SEP + DUMMY, new ArrayInput(Token.EMPTY)); } /** * Executes a query. * @param query query to be executed * @return result * @throws IOException error during query execution */ private String execute(final WebDAVQuery query) throws IOException { final XQuery xquery = new XQuery(query.toString()); for(final Entry entry : query.entries()) { xquery.bind(entry.getKey(), entry.getValue()); } return session().execute(xquery); } /** * Executes a query and returns all results as a list. * @param query query to be executed * @return result * @throws IOException error during query execution */ private String[] results(final WebDAVQuery query) throws IOException { final StringList sl = new StringList(); for(final String result : Strings.split(execute(query), '\t')) { if(!result.isEmpty()) sl.add(result); } return sl.finish(); } /** * Constructor. * @return local session * @throws IOException I/O exception */ private LocalSession session() throws IOException { if(ls == null) ls = new LocalSession(http.context(true)); return ls; } } basex-8.5.1/basex-api/src/main/java/org/basex/http/webdav/WebDAVServlet.java000066400000000000000000000020441274071665100265450ustar00rootroot00000000000000package org.basex.http.webdav; import javax.servlet.*; import org.basex.http.*; import com.bradmcevoy.http.*; /** * WebDAV servlet. * * @author BaseX Team 2005-16, BSD License * @author Dimitar Popov */ public final class WebDAVServlet extends BaseXServlet { /** Http Manager (must be a singleton). */ private HttpManager manager; @Override public void init(final ServletConfig config) throws ServletException { super.init(config); manager = new HttpManager(new WebDAVFactory()); } @Override protected void run(final HTTPContext http) { // authorize request final WebDAVRequest request = new WebDAVRequest(http.req); final Auth a = request.getAuthorization(); if(a != null) http.credentials(a.getUser(), a.getPassword()); // initialize resource factory WebDAVFactory.init(http); // create response final WebDAVResponse response = new WebDAVResponse(http.res); try { manager.process(request, response); } finally { WebDAVFactory.close(); response.close(); } } } basex-8.5.1/basex-api/src/main/java/org/basex/http/webdav/WebDAVUtils.java000066400000000000000000000040141274071665100262200ustar00rootroot00000000000000package org.basex.http.webdav; import java.io.*; import java.net.*; import org.basex.io.*; import org.basex.io.in.*; import org.basex.util.*; /** * WebDAV utility methods. * * @author BaseX Team 2005-16, BSD License * @author Dimitar Popov */ final class WebDAVUtils { /** File path separator. */ static final char SEP = '/'; /** Dummy file for empty folder. */ static final String DUMMY = ".empty"; /** Private constructor. */ private WebDAVUtils() { } /** * Strips leading slash if available. * @param s string to modify * @return string without leading slash */ static String stripLeadingSlash(final String s) { return s == null || s.isEmpty() || s.charAt(0) != SEP ? s : s.substring(1); } /** * Gets the name from the given path. * @param path path * @return name of the resource identified by the path */ static String name(final String path) { return IO.get(path).name(); } /** * Gets a valid database name from a general file name. * @param db name of database * @return valid database name */ static String dbName(final String db) { return IO.get(db).dbName(); } /** * Decodes a url. Character set must be guessed, because it cannot be derived from the request. * @param url url to be decoded * @return decoded url */ static String decode(final String url) { if(url.indexOf('%') != -1) { try { final String ud = URLDecoder.decode(url, Strings.UTF8); return ud.contains("\uFFFD") ? URLDecoder.decode(url, Strings.ISO88591) : ud; } catch(final Exception ignore) { Util.errln(ignore); } } return url; } /** * Peeks the next byte in the given buffer. * @param bi buffer * @return the next byte in the buffer * @throws IOException I/O exception */ static int peek(final BufferInput bi) throws IOException { final TextInput ti = new TextInput(bi); final int c = ti.read(); try { bi.reset(); } catch(final IOException ignore) { } return c; } } basex-8.5.1/basex-api/src/main/java/org/basex/modules/000077500000000000000000000000001274071665100225065ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/java/org/basex/modules/ASession.java000066400000000000000000000057501274071665100251040ustar00rootroot00000000000000package org.basex.modules; import java.util.*; import javax.servlet.http.*; import org.basex.data.*; import org.basex.query.*; import org.basex.query.value.*; import org.basex.query.value.item.*; import org.basex.query.value.node.*; import org.basex.query.value.seq.*; import org.basex.util.*; import org.basex.util.list.*; /** * This module contains functions for processing global sessions. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class ASession { /** Query context. */ private final QueryContext qc; /** Session. */ private final HttpSession session; /** * Constructor. * @param session session * @param qc query context */ ASession(final HttpSession session, final QueryContext qc) { this.session = session; this.qc = qc; } /** * Returns the session ID. * @return session id */ Str id() { return Str.get(session.getId()); } /** * Returns the creation time. * @return creation time */ Dtm created() { return Dtm.get(session.getCreationTime()); } /** * Returns the last access time. * @return creation time */ Dtm accessed() { return Dtm.get(session.getLastAccessedTime()); } /** * Returns all session attributes. * @return session attributes */ Value names() { final TokenList tl = new TokenList(); final Enumeration en = session.getAttributeNames(); while(en.hasMoreElements()) tl.add(en.nextElement()); return StrSeq.get(tl); } /** * Returns a session attribute. * @param key key to be requested * @return session attribute * @throws QueryException query exception */ Value get(final Str key) throws QueryException { return get(key, null); } /** * Returns a session attribute. * @param key key to be requested * @param def default value (can be {@code null}) * @return session attribute * @throws QueryException query exception */ Value get(final Str key, final Value def) throws QueryException { final Object o = session.getAttribute(key.toJava()); if(o == null) return def; if(o instanceof Value) return (Value) o; throw SessionErrors.noAttribute(Util.className(o)); } /** * Updates a session attribute. * @param key key of the attribute * @param value value to be stored * @throws QueryException query exception */ void set(final Str key, final Value value) throws QueryException { final ValueBuilder vb = new ValueBuilder(); for(final Item item : value) { if(item instanceof FItem) throw SessionErrors.functionItem(); final Data d = item.data(); vb.add(d == null || d.inMemory() ? item : ((ANode) item).deepCopy(qc.context.options)); } session.setAttribute(key.toJava(), vb.value()); } /** * Removes a session attribute. * @param key key of the attribute */ void delete(final Str key) { session.removeAttribute(key.toJava()); } /** * Closes a session. */ void close() { session.invalidate(); } } basex-8.5.1/basex-api/src/main/java/org/basex/modules/Session.java000066400000000000000000000056271274071665100250060ustar00rootroot00000000000000package org.basex.modules; import org.basex.http.*; import org.basex.query.*; import org.basex.query.value.*; import org.basex.query.value.item.*; /** * This module contains functions for processing server-side session data. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ public final class Session extends QueryModule { /** * Returns the session ID. * @return session id * @throws QueryException query exception */ @Requires(Permission.NONE) public Str id() throws QueryException { return session().id(); } /** * Returns the creation time of the session. * @return creation time * @throws QueryException query exception */ @Requires(Permission.NONE) public Dtm created() throws QueryException { return session().created(); } /** * Returns the last access time of the session. * @return creation time * @throws QueryException query exception */ @Requires(Permission.NONE) public Dtm accessed() throws QueryException { return session().accessed(); } /** * Returns all session attributes. * @return session attributes * @throws QueryException query exception */ @Requires(Permission.NONE) public Value names() throws QueryException { return session().names(); } /** * Returns a session attribute. * @param key key to be requested * @return session attribute * @throws QueryException query exception */ @Requires(Permission.NONE) public Value get(final Str key) throws QueryException { return session().get(key); } /** * Returns a session attribute. * @param key key to be requested * @param def default value * @return session attribute * @throws QueryException query exception */ @Requires(Permission.NONE) public Value get(final Str key, final Value def) throws QueryException { return session().get(key, def); } /** * Updates a session attribute. * @param key key of the attribute * @param value value to be stored * @throws QueryException query exception */ @Requires(Permission.NONE) public void set(final Str key, final Value value) throws QueryException { session().set(key, value); } /** * Removes a session attribute. * @param key key of the attribute * @throws QueryException query exception */ @Requires(Permission.NONE) public void delete(final Str key) throws QueryException { session().delete(key); } /** * Closes a session. * @throws QueryException query exception */ @Requires(Permission.NONE) public void close() throws QueryException { session().close(); } /** * Returns a session instance. * @return request * @throws QueryException query exception */ private ASession session() throws QueryException { if(queryContext.http == null) throw SessionErrors.noContext(); return new ASession(((HTTPContext) queryContext.http).req.getSession(), queryContext); } } basex-8.5.1/basex-api/src/main/java/org/basex/modules/SessionErrors.java000066400000000000000000000035471274071665100262020ustar00rootroot00000000000000package org.basex.modules; import org.basex.query.*; import org.basex.query.value.item.*; import org.basex.util.*; /** * This module contains static error functions for the Session module. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ final class SessionErrors { /** Error namespace. */ private static final byte[] NS = QueryText.BXERRORS_URI; /** Namespace and error code prefix. */ private static final String PREFIX = new TokenBuilder(QueryText.BXERR_PREFIX).add(':').add("BXSE").toString(); /** Private constructor, preventing instantiation. */ private SessionErrors() { } /** * BXSE0001: function items cannot be stored in sessions. * @return query exception */ static QueryException functionItem() { return thrw(1, "Function items cannot be stored in sessions."); } /** * BXSE0002: stored attribute cannot be retrieved. * @param name name of attribute * @return query exception */ static QueryException noAttribute(final Object name) { return thrw(2, "Stored attribute cannot be retrieved: %.", name); } /** * BXSE0003: servlet context required. * @return query exception */ static QueryException noContext() { return thrw(3, "Servlet context required."); } /** * BXSE0004: session not found. * @param id session id * @return query exception */ static QueryException whichSession(final Object id) { return thrw(4, "Session not found: %.", id); } /** * Creates a new exception. * @param code error code * @param msg error message * @param ext optional error extension * @return query exception */ private static QueryException thrw(final int code, final String msg, final Object... ext) { final QNm name = new QNm(String.format("%s%04d", PREFIX, code), NS); return new QueryException(null, name, msg, ext); } } basex-8.5.1/basex-api/src/main/java/org/basex/modules/Sessions.java000066400000000000000000000070161274071665100251630ustar00rootroot00000000000000package org.basex.modules; import java.util.*; import javax.servlet.http.*; import org.basex.http.*; import org.basex.query.*; import org.basex.query.value.*; import org.basex.query.value.item.*; import org.basex.query.value.seq.*; import org.basex.util.list.*; /** * This module contains functions for processing global sessions. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ public final class Sessions extends QueryModule { /** * Returns the ids of all registered sessions. * @return session ids */ public static Value ids() { final HashMap http = SessionListener.sessions(); final TokenList tl = new TokenList(http.size()); for(final String s : http.keySet()) tl.add(s); return StrSeq.get(tl); } /** * Returns the creation time of the session. * @param id session id * @return creation time * @throws QueryException query exception */ @Requires(Permission.NONE) public Dtm created(final Str id) throws QueryException { return session(id).created(); } /** * Returns the last access time of the session. * @param id session id * @return creation time * @throws QueryException query exception */ @Requires(Permission.NONE) public Dtm accessed(final Str id) throws QueryException { return session(id).accessed(); } /** * Returns all attributes of a session. * @param id session id * @return session attributes * @throws QueryException query exception */ public Value names(final Str id) throws QueryException { return session(id).names(); } /** * Returns the specified session attribute of a session. * @param id session id * @param key key to be requested * @return session attribute * @throws QueryException query exception */ public Value get(final Str id, final Str key) throws QueryException { return session(id).get(key); } /** * Returns the specified session attribute of a session. * @param id session id * @param key key to be requested * @param def default item * @return session attribute * @throws QueryException query exception */ public Value get(final Str id, final Str key, final Item def) throws QueryException { return session(id).get(key, def); } /** * Updates a session attribute. * @param id session id * @param key key of the attribute * @param item item to be stored * @throws QueryException query exception */ public void set(final Str id, final Str key, final Item item) throws QueryException { session(id).set(key, item); } /** * Removes a session attribute. * @param id session id * @param key key of the attribute * @throws QueryException query exception */ public void delete(final Str id, final Str key) throws QueryException { session(id).delete(key); } /** * Closes a session. * @param id session id * @throws QueryException query exception */ public void close(final Str id) throws QueryException { session(id).close(); } /** * Returns a session instance. * @param id session id * @return request * @throws QueryException query exception */ private ASession session(final Str id) throws QueryException { if(queryContext.http == null) throw SessionErrors.noContext(); final HashMap http = SessionListener.sessions(); final HttpSession session = id != null ? http.get(id.toJava()) : null; if(session == null) throw SessionErrors.whichSession(id); return new ASession(session, queryContext); } } basex-8.5.1/basex-api/src/main/java/org/expath/000077500000000000000000000000001274071665100212255ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/java/org/expath/ns/000077500000000000000000000000001274071665100216455ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/java/org/expath/ns/Geo.java000066400000000000000000000635641274071665100232400ustar00rootroot00000000000000package org.expath.ns; import static org.basex.query.QueryError.*; import static org.basex.util.Token.*; import java.io.*; import org.basex.build.*; import org.basex.build.xml.*; import org.basex.io.*; import org.basex.query.*; import org.basex.query.value.item.*; import org.basex.query.value.node.*; import org.basex.query.value.type.*; import com.vividsolutions.jts.geom.*; import com.vividsolutions.jts.io.*; import com.vividsolutions.jts.io.gml2.*; /** * This module contains geo spatial functions for the Geo module. * * @author BaseX Team 2005-16, BSD License * @author Masoumeh Seydi */ public final class Geo extends QueryModule { /** GML URI. */ private static final byte[] URI = token("http://www.opengis.net/gml"); /** Prefix: "gml". */ private static final byte[] GML = token("gml"); /** QName gml:Point. */ private static final QNm Q_GML_POINT = new QNm(GML, "Point", URI); /** QName gml:MultiPoint. */ private static final QNm Q_GML_MULTIPOINT = new QNm(GML, "MultiPoint", URI); /** QName gml:LineString. */ private static final QNm Q_GML_LINESTRING = new QNm(GML, "LineString", URI); /** QName gml:LinearRing. */ private static final QNm Q_GML_LINEARRING = new QNm(GML, "LinearRing", URI); /** QName gml:Polygon. */ private static final QNm Q_GML_POLYGON = new QNm(GML, "Polygon", URI); /** QName gml:MultiPolygon. */ private static final QNm Q_GML_MULTIPOLYGON = new QNm(GML, "MultiPolygon", URI); /** QName gml:MultiLineString. */ private static final QNm Q_GML_MULTILINESTRING = new QNm(GML, "MultiLineString", URI); /** Array containing all QNames. */ private static final QNm[] QNAMES = { Q_GML_POINT, Q_GML_LINESTRING, Q_GML_POLYGON, Q_GML_MULTIPOINT, Q_GML_MULTILINESTRING, Q_GML_MULTIPOLYGON, Q_GML_LINEARRING }; /** * Returns the dimension of an item. * @param node xml element containing gml object(s) * @return dimension * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Int dimension(final ANode node) throws QueryException { return Int.get(checkGeo(node).getDimension()); } /** * Returns the name of the geometry type in the GML namespace, or the empty sequence. * @param node xml element containing gml object(s) * @return geometry type * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public QNm geometryType(final ANode node) throws QueryException { return new QNm(GML, checkGeo(node).getGeometryType(), URI); } /** * Returns the name of the geometry type in the GML namespace, or the empty sequence. * @param node xml element containing gml object(s) * @return integer value of CRS of the geometry * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Uri srid(final ANode node) throws QueryException { return Uri.uri(token(checkGeo(node).getSRID())); } /** * Returns the gml:Envelope of the specified geometry. * The envelope is the minimum bounding box of this geometry. * @param node xml element containing gml object(s) * @return envelop element * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public ANode envelope(final ANode node) throws QueryException { return toElement(checkGeo(node).getEnvelope()); } /** * Returns the WKT format of a geometry. * @param node xml element containing gml object(s) * @return Well-Known Text geometry representation * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Str asText(final ANode node) throws QueryException { return Str.get(new WKTWriter().write(checkGeo(node))); } /** * Returns the WKB format of a geometry. * @param node xml element containing gml object(s) * @return Well-Known Binary geometry representation * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public B64 asBinary(final ANode node) throws QueryException { return new B64(new WKBWriter().write(checkGeo(node))); } /** * Returns a boolean value which shows if the specified geometry is empty or not. * @param node xml element containing gml object(s) * @return boolean value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Bln isEmpty(final ANode node) throws QueryException { if(node == null) return Bln.FALSE; checkGeo(node); return Bln.TRUE; } /** * Returns a boolean value which shows if the specified geometry is simple or not, * which has no anomalous geometric points, such as self intersection or self tangency. * @param node xml element containing gml object(s) * @return boolean value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Bln isSimple(final ANode node) throws QueryException { return Bln.get(checkGeo(node).isSimple()); } /** * Returns the boundary of the geometry, in GML. * The return value is a sequence of either gml:Point or gml:LinearRing elements. * @param node xml element containing gml object(s) * @return boundary element (geometry) * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public ANode boundary(final ANode node) throws QueryException { return toElement(checkGeo(node).getBoundary()); } /** * Returns a boolean value that shows if two geometries are equal or not. * @param node1 xml element containing gml object(s) * @param node2 xml element containing gml object(s) * @return boolean value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Bln equals(final ANode node1, final ANode node2) throws QueryException { final Geometry geo1 = checkGeo(node1); final Geometry geo2 = checkGeo(node2); return Bln.get(geo1.equals(geo2)); } /** * Returns a boolean value that shows if this geometry is disjoint to another geometry. * @param node1 xml element containing gml object(s) * @param node2 xml element containing gml object(s) * @return boolean value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Bln disjoint(final ANode node1, final ANode node2) throws QueryException { final Geometry geo1 = checkGeo(node1); final Geometry geo2 = checkGeo(node2); return Bln.get(geo1.disjoint(geo2)); } /** * Returns a boolean value that shows if this geometry intersects another geometry. * @param node1 xml element containing gml object(s) * @param node2 xml element containing gml object(s) * @return boolean value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Bln intersects(final ANode node1, final ANode node2) throws QueryException { final Geometry geo1 = checkGeo(node1); final Geometry geo2 = checkGeo(node2); return Bln.get(geo1.intersects(geo2)); } /** * Returns a boolean value that shows if this geometry touches the specified geometry. * @param node1 xml element containing gml object(s) * @param node2 xml element containing gml object(s) * @return boolean value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Bln touches(final ANode node1, final ANode node2) throws QueryException { final Geometry geo1 = checkGeo(node1); final Geometry geo2 = checkGeo(node2); return Bln.get(geo1.touches(geo2)); } /** * Returns a boolean value that shows if this geometry crosses the specified geometry. * @param node1 xml element containing gml object(s) * @param node2 xml element containing gml object(s) * @return boolean value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Bln crosses(final ANode node1, final ANode node2) throws QueryException { final Geometry geo1 = checkGeo(node1); final Geometry geo2 = checkGeo(node2); return Bln.get(geo1.crosses(geo2)); } /** * Returns a boolean value that shows if this geometry is within the specified geometry. * @param node1 xml element containing gml object(s) * @param node2 xml element containing gml object(s) * @return boolean value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Bln within(final ANode node1, final ANode node2) throws QueryException { final Geometry geo1 = checkGeo(node1); final Geometry geo2 = checkGeo(node2); return Bln.get(geo1.within(geo2)); } /** * Returns a boolean value that shows if this geometry contains the specified geometry. * @param node1 xml element containing gml object(s) * @param node2 xml element containing gml object(s) * @return boolean value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Bln contains(final ANode node1, final ANode node2) throws QueryException { final Geometry geo1 = checkGeo(node1); final Geometry geo2 = checkGeo(node2); return Bln.get(geo1.contains(geo2)); } /** * Returns a boolean value that shows if this geometry overlaps the specified geometry. * @param node1 xml element containing gml object(s) * @param node2 xml element containing gml object(s) * @return boolean value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Bln overlaps(final ANode node1, final ANode node2) throws QueryException { final Geometry geo1 = checkGeo(node1); final Geometry geo2 = checkGeo(node2); return Bln.get(geo1.overlaps(geo2)); } /** * Returns a boolean value that shows if whether relationships between the boundaries, * interiors and exteriors of two geometries match * the pattern specified in intersection-matrix-pattern. * @param node1 xml element containing gml object(s) * @param node2 xml element containing gml object(s) * @param intersectionMatrix intersection matrix for two geometries * @return boolean value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Bln relate(final ANode node1, final ANode node2, final Str intersectionMatrix) throws QueryException { final Geometry geo1 = checkGeo(node1); final Geometry geo2 = checkGeo(node2); try { return Bln.get(geo1.relate(geo2, intersectionMatrix.toJava())); } catch(final IllegalArgumentException ex) { throw GeoErrors.illegalArg(intersectionMatrix); } } /** * Returns the shortest distance in the units of the spatial reference system * of geometry, between the geometries. * The distance is the distance between a point on each of the geometries. * @param node1 xml element containing gml object(s) * @param node2 xml element containing gml object(s) * @return distance double value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Dbl distance(final ANode node1, final ANode node2) throws QueryException { final Geometry geo1 = checkGeo(node1); final Geometry geo2 = checkGeo(node2); return Dbl.get(geo1.distance(geo2)); } /** * Returns a polygon that represents all Points whose distance from this * geometric object is less than or equal to distance. * The returned element must be either gml:Polygon, gml:LineString or gml:Point. * @param node xml element containing gml object(s) * @param distance specific distance from the $geometry (the buffer width) * @return buffer geometry as gml element * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public ANode buffer(final ANode node, final ANum distance) throws QueryException { return toElement(checkGeo(node).buffer(distance.dbl())); } /** * Returns the convex hull geometry of a geometry in GML, or the empty sequence. * The returned element must be either gml:Polygon, gml:LineString or gml:Point. * @param node xml element containing gml object(s) * @return convex hull geometry as a gml element * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public ANode convexHull(final ANode node) throws QueryException { return toElement(checkGeo(node).convexHull()); } /** * Returns a geometric object representing the Point set intersection of two geometries. * @param node1 xml element containing gml object(s) * @param node2 xml element containing gml object(s) * @return intersection geometry as a gml element * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public ANode intersection(final ANode node1, final ANode node2) throws QueryException { final Geometry geo1 = checkGeo(node1); final Geometry geo2 = checkGeo(node2); return toElement(geo1.intersection(geo2)); } /** * Returns a geometric object that represents the Point set union of two geometries. * @param node1 xml element containing gml object(s) * @param node2 xml element containing gml object(s) * @return union geometry as a gml element * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public ANode union(final ANode node1, final ANode node2) throws QueryException { final Geometry geo1 = checkGeo(node1); final Geometry geo2 = checkGeo(node2); return toElement(geo1.union(geo2)); } /** * Returns a geometric object that represents the * Point set difference of two geometries. * @param node1 xml element containing gml object(s) * @param node2 xml element containing gml object(s) * @return difference geometry as a gml element * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public ANode difference(final ANode node1, final ANode node2) throws QueryException { final Geometry geo1 = checkGeo(node1); final Geometry geo2 = checkGeo(node2); return toElement(geo1.difference(geo2)); } /** * Returns a geometric object that represents the * Point set symmetric difference of two geometries. * @param node1 xml element containing gml object(s) * @param node2 xml element containing gml object(s) * @return symmetric difference geometry as a gml element * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public ANode symDifference(final ANode node1, final ANode node2) throws QueryException { final Geometry geo1 = checkGeo(node1); final Geometry geo2 = checkGeo(node2); return toElement(geo1.symDifference(geo2)); } /** * Returns number of geometries in a geometry collection, * or 1 if the input is not a collection. * @param node xml element containing gml object(s) * @return integer value of number of geometries * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Int numGeometries(final ANode node) throws QueryException { return Int.get(checkGeo(node).getNumGeometries()); } /** * Returns the nth geometry of a geometry collection, * or the geometry if the input is not a collection. * @param node xml element containing gml object(s) * @param number integer number as the index of nth geometry * @return geometry as a gml element * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public ANode geometryN(final ANode node, final Int number) throws QueryException { final Geometry geo = checkGeo(node); final long n = number.itr(); if(n < 1 || n > geo.getNumGeometries()) throw GeoErrors.outOfRangeIdx(number); return toElement(geo.getGeometryN((int) n - 1)); } /** * Returns the x-coordinate value for point. * @param node xml element containing gml object(s) * @return x double value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Dbl x(final ANode node) throws QueryException { final Geometry geo = geo(node, "Point", Q_GML_POINT); return Dbl.get(geo.getCoordinate().x); } /** * Returns the y-coordinate value for point. * @param node xml element containing gml object(s) * @return y double value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Dbl y(final ANode node) throws QueryException { final Geometry geo = geo(node, "Point", Q_GML_POINT); return Dbl.get(geo.getCoordinate().y); } /** * Returns the z-coordinate value for point. * @param node xml element containing gml object(s) * @return z double value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Dbl z(final ANode node) throws QueryException { final Geometry geo = geo(node, "Point", Q_GML_POINT); return Dbl.get(geo.getCoordinate().z); } /** * Returns the length of this Geometry. Linear geometries return their length. * Areal geometries return their parameter. Others return 0.0 * @param node xml element containing gml object(s) * @return length double value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Dbl length(final ANode node) throws QueryException { return Dbl.get(checkGeo(node).getLength()); } /** * Returns the start point of a line. * @param node xml element containing gml object(s) * @return start point geometry as a gml element * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public ANode startPoint(final ANode node) throws QueryException { final Geometry geo = geo(node, "Line", Q_GML_LINEARRING, Q_GML_LINESTRING); return toElement(((LineString) geo).getStartPoint()); } /** * Returns the end point of a line. * @param node xml element containing gml object(s) * @return end point geometry as a gml element * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public ANode endPoint(final ANode node) throws QueryException { final Geometry geo = geo(node, "Line", Q_GML_LINEARRING, Q_GML_LINESTRING); return toElement(((LineString) geo).getEndPoint()); } /** * Checks if the line is closed loop. * That is, if the start Point is same with end Point. * @param node xml element containing gml object(s) * @return boolean value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Bln isClosed(final ANode node) throws QueryException { final Geometry geo = geo(node, "Line", Q_GML_LINEARRING, Q_GML_LINESTRING, Q_GML_MULTILINESTRING); return Bln.get(geo instanceof LineString ? ((LineString) geo).isClosed() : ((MultiLineString) geo).isClosed()); } /** * Return a boolean value that shows weather the line is a ring or not. * A line is a ring if it is closed and simple. * @param node xml element containing gml object(s) * @return boolean value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Bln isRing(final ANode node) throws QueryException { final Geometry geo = geo(node, "Line", Q_GML_LINEARRING, Q_GML_LINESTRING); return Bln.get(((LineString) geo).isRing()); } /** * Returns the number of points in a geometry. * @param node xml element containing gml object(s) * @return number of points int value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Int numPoints(final ANode node) throws QueryException { return Int.get(checkGeo(node).getNumPoints()); } /** * Returns the nth point of a line. * @param node xml element containing gml object(s) * @param number index of i-th point * @return n-th point as a gml element * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public ANode pointN(final ANode node, final Int number) throws QueryException { final Geometry geo = geo(node, "Line", Q_GML_LINEARRING, Q_GML_LINESTRING); final int max = geo.getNumPoints(); final long n = number.itr(); if(n < 1 || n > max) throw GeoErrors.outOfRangeIdx(number); return toElement(((LineString) geo).getPointN((int) n - 1)); } /** * Returns the area of a Geometry. Areal Geometries have a non-zero area. * Returns zero for Point and Lines. * @param node xml element containing gml object(s) * @return geometry area as a double vaue * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Dbl area(final ANode node) throws QueryException { return Dbl.get(checkGeo(node).getArea()); } /** * Returns the mathematical centroid of the geometry as a gml:Point. * The point is not guaranteed to be on the surface. * @param node xml element containing gml object(s) * @return centroid geometry as a gml element * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public ANode centroid(final ANode node) throws QueryException { return toElement(checkGeo(node).getCentroid()); } /** * Returns a gml:Point that is interior of this geometry. * If it cannot be inside the geometry, then it will be on the boundary. * @param node xml element containing gml object(s) * @return a point as a gml element * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public ANode pointOnSurface(final ANode node) throws QueryException { return toElement(checkGeo(node).getInteriorPoint()); } /** * Returns the outer ring of a polygon, in GML. * @param node xml element containing gml object(s) * @return exterior ring geometry (LineString) as a gml element * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public ANode exteriorRing(final ANode node) throws QueryException { final Geometry geo = geo(node, "Polygon", Q_GML_POLYGON); return toElement(((Polygon) geo).getExteriorRing()); } /** * Returns the number of interior rings in a polygon. * @param node xml element containing gml object(s) * @return integer number of interior rings * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Int numInteriorRing(final ANode node) throws QueryException { final Geometry geo = geo(node, "Polygon", Q_GML_POLYGON); return Int.get(((Polygon) geo).getNumInteriorRing()); } /** * Returns the nth geometry of a geometry collection. * @param node xml element containing gml object(s) * @param number index of i-th interior ring * @return n-th interior ring geometry (LineString) as a gml element * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public ANode interiorRingN(final ANode node, final Int number) throws QueryException { final Geometry geo = geo(node, "Polygon", Q_GML_POLYGON); final long n = number.itr(); final int max = ((Polygon) geo).getNumInteriorRing(); if(n < 1 || n > max) throw GeoErrors.outOfRangeIdx(number); return toElement(((Polygon) geo).getInteriorRingN((int) n - 1)); } // PRIVATE METHODS (hidden from user of module) ======================================== /** * Reads an element as a gml node. Returns a geometry element * or throws an exception if the element is of the wrong type. * @param node xml node containing gml object(s) * @return geometry * @throws QueryException query exception */ private static Geometry checkGeo(final ANode node) throws QueryException { final Geometry geo = geo(node, QNAMES); if(geo == null) throw GeoErrors.unrecognizedGeo(node.qname().local()); return geo; } /** * Reads an element as a gml node. Returns a geometry element or raises an error if the element * does not match one of the specified types. * @param node xml node containing gml object(s) * @param type expected type (can be {@code null}) * @param names allowed geometry types * @return geometry, or {@code null} * @throws QueryException query exception */ private static Geometry geo(final ANode node, final String type, final QNm... names) throws QueryException { final Geometry geo = geo(node, names); if(geo == null) { checkGeo(node); throw GeoErrors.geoType(node.qname().local(), type); } return geo; } /** * Reads an element as a gml node. Returns a geometry element or {@code null}. * @param node xml node containing gml object(s) * @param names allowed geometry types * @return geometry, or {@code null} * @throws QueryException query exception */ private static Geometry geo(final ANode node, final QNm... names) throws QueryException { if(node.type != NodeType.ELM) throw castError(node, NodeType.ELM, null); final QNm qname = node.qname(); for(final QNm geo : names) { if(!qname.eq(geo)) continue; // type found... create reader and geometry element try { final String input = node.serialize().toString(); final GMLReader gmlReader = new GMLReader(); final GeometryFactory geoFactory = new GeometryFactory(); return gmlReader.read(input, geoFactory); } catch(final Throwable ex) { throw GeoErrors.gmlReaderErr(ex); } } return null; } /** * Writes an geometry and returns a new element. * @param geometry geometry * @return DBNode database node * @throws QueryException exception */ private ANode toElement(final Geometry geometry) throws QueryException { final String geo; try { // write geometry and add namespace declaration geo = new GMLWriter().write(geometry).replaceAll( "^", ""); } catch(final Exception ex) { throw GeoErrors.gmlWriterErr(ex); } try { final XMLParser parser = new XMLParser(new IOContent(geo), queryContext.context.options); return new DBNode(MemBuilder.build(parser)).children().next(); } catch(final IOException ex) { throw IOERR_X.get(null, ex); } } }basex-8.5.1/basex-api/src/main/java/org/expath/ns/GeoErrors.java000066400000000000000000000045401274071665100244220ustar00rootroot00000000000000package org.expath.ns; import org.basex.query.*; import org.basex.query.value.item.*; import org.basex.util.*; /** * This module contains static error functions for the Geo module. * * @author BaseX Team 2005-16, BSD License * @author Masoumeh Seydi */ final class GeoErrors { /** Private constructor, preventing instantiation. */ private GeoErrors() { } /** * GEO0001: Unrecognized geo object. * @param name name of element * @return query exception */ static QueryException unrecognizedGeo(final byte[] name) { return thrw(1, "Unrecognized Geo type: %", name); } /** * GEO0002: GML reader error massage (JTS). * @param th throwable * @return query exception */ static QueryException gmlReaderErr(final Throwable th) { return thrw(2, "Parsing GML 2.0: %", th); } /** * GEO0003: Inappropriate input geometry. * @param name name of element * @param geo exact Geometry object * @return query exception */ static QueryException geoType(final byte[] name, final String geo) { return thrw(3, "% is not an appropiate geometry for this function. " + "The input geometry should be a %.", name, geo); } /** * GEO0004: Out of range index. * @param geoNumber index * @return query exception */ static QueryException outOfRangeIdx(final Int geoNumber) { return thrw(4, "Out of range input index: %", geoNumber); } /** * GEO0005: GML writer error massage (JTS). * @param th throwable * @return query exception */ static QueryException gmlWriterErr(final Throwable th) { return thrw(5, "%", th); } /** * GEO0006: Illegal argument. * @param arg argument * @return query exception */ static QueryException illegalArg(final Str arg) { return thrw(6, "Illegal argument: %", arg); } /** * Creates an error QName for the specified code. * @param code code * @return query exception */ static QNm qname(final int code) { return new QNm(Util.inf("%s:GEO%04d", QueryText.EXPERR_PREFIX, code), QueryText.EXPERROR_URI); } /** * Returns a query exception. * @param code code * @param msg message * @param ext extension * @return query exception */ private static QueryException thrw(final int code, final String msg, final Object... ext) { return new QueryException(null, qname(code), msg, ext); } } basex-8.5.1/basex-api/src/main/java/org/exquery/000077500000000000000000000000001274071665100214365ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/java/org/exquery/ns/000077500000000000000000000000001274071665100220565ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/java/org/exquery/ns/Request.java000066400000000000000000000214271274071665100243570ustar00rootroot00000000000000package org.exquery.ns; import java.io.*; import java.util.*; import javax.servlet.http.*; import org.basex.http.*; import org.basex.query.*; import org.basex.query.value.*; import org.basex.query.value.item.*; import org.basex.query.value.seq.*; import org.basex.util.hash.*; import org.basex.util.list.*; /** * This module contains functions for handling servlet requests. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ public final class Request extends QueryModule { /** * Returns the method of a request. * @return method * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Str method() throws QueryException { return Str.get(request().getMethod()); } /** * Returns the Scheme component of a request. * @return scheme * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Str scheme() throws QueryException { return Str.get(request().getScheme()); } /** * Returns the Hostname fragment of a request. * @return host name * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Str hostname() throws QueryException { return Str.get(request().getServerName()); } /** * Returns the Port fragment of a request. * @return port * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Int port() throws QueryException { return Int.get(request().getServerPort()); } /** * Returns the path of the request. * @return path * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Str path() throws QueryException { return Str.get(request().getRequestURI()); } /** * Returns the query string of a request. * @return query string * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Str query() throws QueryException { final String query = request().getQueryString(); return query == null ? null : Str.get(query); } /** * Returns the URI of a request. * @return URI * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Uri uri() throws QueryException { return Uri.uri(request().getRequestURL().toString()); } /** * Returns the context path of a request. * @return context path * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Str contextPath() throws QueryException { return Str.get(request().getContextPath()); } /** * Returns the address of a request. * @return address * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Str address() throws QueryException { return Str.get(request().getLocalAddr()); } /** * Returns the remote host name of a request. * @return host name * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Str remoteHostname() throws QueryException { return Str.get(request().getRemoteHost()); } /** * Returns the remote address of a request. * @return remote address * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Str remoteAddress() throws QueryException { return Str.get(request().getRemoteAddr()); } /** * Returns the remote port of a request. * @return remote port * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Int remotePort() throws QueryException { return Int.get(request().getRemotePort()); } /** * Returns the names of all query parameters. * @return parameter names * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Value parameterNames() throws QueryException { try { final HTTPParams params = context().params; final TokenSet cache = new TokenSet(); for(final String name : params.query().keySet()) cache.add(name); for(final String name : params.form(queryContext.context.options).keySet()) cache.add(name); final TokenList names = new TokenList(cache.size()); for(final byte[] name : cache) names.add(name); return StrSeq.get(names); } catch(final IOException ex) { throw new QueryException(ex); } } /** * Returns the value of a specific query parameter. * @param key key to be requested * @return parameter value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Value parameter(final Str key) throws QueryException { return parameter(key, null); } /** * Returns the value of a specific query parameter. * @param key key to be requested * @param def default value * @return parameter value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Value parameter(final Str key, final Value def) throws QueryException { try { final String name = key.toJava(); final HTTPParams params = context().params; final Value query = params.query().get(name); final Value form = params.form(queryContext.context.options).get(name); if(query == null && form == null) return def; if(query == null) return form; if(form == null) return query; return new ValueBuilder().add(query).add(form).value(); } catch(final IOException ex) { throw new QueryException(ex); } } /** * Returns the names of all header parameters. * @return parameter names * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Value headerNames() throws QueryException { final TokenList tl = new TokenList(); final Enumeration en = request().getHeaderNames(); while(en.hasMoreElements()) tl.add(en.nextElement()); return StrSeq.get(tl); } /** * Returns the value of a specific header parameter. * @param key key to be requested * @return parameter value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Str header(final Str key) throws QueryException { return header(key, null); } /** * Returns the value of a specific header parameter. * @param key key to be requested * @param def default value * @return parameter value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Str header(final Str key, final Str def) throws QueryException { final String val = request().getHeader(key.toJava()); return val == null ? def : Str.get(val); } /** * Returns all cookie names. * @return parameter names * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Value cookieNames() throws QueryException { final TokenList tl = new TokenList(); for(final Cookie c : request().getCookies()) tl.add(c.getName()); return StrSeq.get(tl); } /** * Returns the value of a specific cookie. * @param key key to be requested * @return parameter value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Str cookie(final Str key) throws QueryException { return cookie(key, null); } /** * Returns the value of a specific cookie. * @param key key to be requested * @param def default value * @return parameter value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Str cookie(final Str key, final Str def) throws QueryException { final String k = key.toJava(); for(final Cookie c : request().getCookies()) { if(c.getName().equals(k)) return Str.get(c.getValue()); } return def; } /** * Returns the value of a specific attribute. * @param key key to be requested * @return attribute value * @throws QueryException query exception */ @Deterministic @Requires(Permission.NONE) public Str attribute(final Str key) throws QueryException { final Object query = request().getAttribute(key.toJava()); return query == null ? null : Str.get(query.toString()); } // PRIVATE FUNCTIONS ============================================================================ /** * Returns the servlet request instance. * @return request * @throws QueryException query exception */ private HttpServletRequest request() throws QueryException { return context().req; } /** * Returns the servlet HTTP context. * @return context * @throws QueryException query exception */ private HTTPContext context() throws QueryException { if(queryContext.http != null) return (HTTPContext) queryContext.http; throw new QueryException("Servlet context required."); } } basex-8.5.1/basex-api/src/main/java/org/exquery/ns/Response.java000066400000000000000000000003731274071665100245220ustar00rootroot00000000000000package org.exquery.ns; import org.basex.query.*; /** * This module contains functions for handling servlet responses. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ public final class Response extends QueryModule { } basex-8.5.1/basex-api/src/main/java/org/exquery/ns/Restxq.java000066400000000000000000000030551274071665100242120ustar00rootroot00000000000000package org.exquery.ns; import org.basex.http.*; import org.basex.http.restxq.*; import org.basex.query.*; import org.basex.query.value.item.*; import org.basex.query.value.node.*; /** * This module contains standard RESTXQ functions. * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ public final class Restxq extends QueryModule { /** * Returns an {Code application.wadl} description including all RESTXQ services. * @return wadl description * @throws QueryException query exception */ public FElem wadl() throws QueryException { return RestXqModules.get().wadl(http()); } /** * Returns the base URI of the resource function. * @return base uri * @throws QueryException query exception */ public Uri baseUri() throws QueryException { final HTTPContext http = http(); return Uri.uri(http.req.getRequestURI().replace(http.req.getPathInfo(), "")); } /** * Returns the base URI of the resource function. * @return base uri * @throws QueryException query exception */ public Uri uri() throws QueryException { return Uri.uri(http().req.getRequestURI()); } /** * Initializes the RESTXQ module cache. */ public void init() { RestXqModules.get().init(); } /** * Returns the servlet request instance. * @return request * @throws QueryException query exception */ private HTTPContext http() throws QueryException { if(queryContext.http != null) return (HTTPContext) queryContext.http; throw new QueryException("Servlet context required."); } } basex-8.5.1/basex-api/src/main/lisp/000077500000000000000000000000001274071665100171735ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/lisp/BaseXClient.lisp000066400000000000000000000053251274071665100222320ustar00rootroot00000000000000; Lisp client for BaseX. ; Works with BaseX 6.x (but not with BaseX 7 and later) ; ; Documentation: http://docs.basex.org/wiki/Clients ; ; (C) Andy Chambers, Formedix Ltd 2010, BSD License (defpackage :basex (:use :cl :usocket) (:export :session :execute :close-session :info :result)) (in-package :basex) (defconstant +null+ (code-char 0)) (defclass session () ((host :initarg :host :initform "localhost") (port :initarg :port :initform 1984) (user :initarg :user :initform "admin") (pw :initarg :pw :initform "admin") (sock :initform nil) (result :initform nil :accessor result) (info :initform nil :accessor info))) (defmethod initialize-instance :after ((self session) &key) (with-slots (host port user pw sock) self (setf sock (socket-connect host port :element-type '(unsigned-byte 8))) (unless (hand-shake self) (error "Could not initiate connection")))) (defun hand-shake (session) (declare (optimize debug)) (labels ((md5 (str) (string-downcase (with-output-to-string (s) (loop for hex across (sb-md5:md5sum-string str) do (format s "~2,'0x" hex))))) (auth-token (pw timestamp) (md5 (format nil "~a~a" (md5 pw) timestamp)))) (with-slots (user pw sock) session (let* ((ts (read-null-terminated (socket-stream sock))) (auth (auth-token pw ts))) (write-null-terminated user (socket-stream sock)) (write-null-terminated auth (socket-stream sock)) (force-output (socket-stream sock)) (eq 0 (read-byte (socket-stream sock))))))) (defun read-null-terminated (in) (with-output-to-string (s) (loop for char = (code-char (read-byte in)) until (char= char +null+) do (write-char char s)))) (defun write-null-terminated (string out) (loop for char across string do (write-byte (char-code char) out)) (write-byte (char-code +null+) out)) (defmethod execute ((self session) query) (with-slots (sock) self (let ((stream (socket-stream sock))) (write-null-terminated query stream) (force-output stream) (setf (result self) (read-null-terminated stream) (info self) (read-null-terminated stream)) (eq 0 (read-byte (socket-stream sock)))))) (defmethod open-session ((self session)) (unwind-protect (unless (hand-shake self) (error "Could not open session")) (close-session self))) (defmethod close-session ((self session)) (with-slots (sock) self (write-null-terminated "exit" (socket-stream sock)) (socket-close sock))) basex-8.5.1/basex-api/src/main/lisp/Example.lisp000066400000000000000000000006661274071665100214670ustar00rootroot00000000000000; This example shows how database commands can be executed. ; ; Documentation: http://docs.basex.org/wiki/Clients ; ; (C) Andy Chambers, Formedix Ltd 2010, BSD License (defpackage :basex-user (:use :cl :basex)) (in-package :basex-user) (time (let ((session (make-instance 'session))) (if (execute session "xquery 1 to 10") (print (result session)) (print (info session))) (close-session session))) basex-8.5.1/basex-api/src/main/perl/000077500000000000000000000000001274071665100171665ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/perl/AddExample.pl000066400000000000000000000015621274071665100215330ustar00rootroot00000000000000# This example shows how new documents can be added. # # Documentation: http://docs.basex.org/wiki/Clients # # (C) BaseX Team 2005-12, BSD License use BaseXClient; use warnings; use strict; eval { # create session my $session = Session->new("localhost", 1984, "admin", "admin"); # create empty database $session->execute("create db database"); print $session->info()."\n"; # add document $session->add("world/World.xml", "Hello World!"); print $session->info()."\n"; # add document $session->add("Universe.xml", "Hello Universe!"); print $session->info()."\n"; # run query on database print $session->execute("xquery collection('database')")."\n"; # drop database $session->execute("drop db database"); # close session $session->close(); }; # print exception print $@ if $@; basex-8.5.1/basex-api/src/main/perl/BaseXClient.pm000066400000000000000000000100321274071665100216610ustar00rootroot00000000000000# Perl client for BaseX. # Works with BaseX 7.0 and later # # Documentation: http://docs.basex.org/wiki/Clients # # (C) BaseX Team 2005-12, BSD License use Digest::MD5; use IO::Socket; use warnings; use strict; package Session; sub new { my $class = shift; my $host = shift; my $port = shift; my $user = shift; my $pw = shift; my $self = bless({}, $class); # create server connection $self->{sock} = IO::Socket::INET->new( PeerAddr => $host, PeerPort => $port, Proto => "tcp") or die "Can't communicate with the server."; # receive server response my $code; my $nonce; my @response = split(':', $self->_receive()); if(@response > 1) { # support for digest authentication $code = "$user:$response[0]:$pw"; $nonce = $response[1]; } else { # support for cram-md5 (Version < 8.0) $code = $pw; $nonce = $response[0]; } # send username and hashed password/timestamp my $codemd5 = Digest::MD5->new()->add($code)->hexdigest(); my $complete = Digest::MD5->new()->add($codemd5.$nonce)->hexdigest(); $self->send($user.chr(0).$complete); # evaluate success flag return $self if !$self->_read() or die "Access denied."; } sub execute { my $self = shift; my $cmd = shift; # send command to server and receive result $self->send($cmd); $self->{result} = $self->_receive(); $self->{info} = $self->_receive(); die $self->{info} if !$self->ok(); return $self->{result}; } sub query { return Query->new(shift, shift); } sub create { shift->sendInput(8, shift, shift); } sub add { shift->sendInput(9, shift, shift); } sub replace { shift->sendInput(12, shift, shift); } sub store { shift->sendInput(13, shift, shift); } sub info { return shift->{info}; } sub close { my $self = shift; $self->send("exit"); close($self->{sock}); } # Receives a string from the socket. sub _receive { my $self = shift; my $data = ""; while($self->_read()) { $self->_read() if ord($_) == 255; $data .= $_; } return $data; } # Returns a single byte from the socket. sub _read { shift->{sock}->recv($_, 1); return ord(); } # Returns success check. sub ok { return !shift->_read(); } # Sends the specified string. sub send { shift->{sock}->send((shift).chr(0)); } # Sends the specified input. sub sendInput { my $self = shift; my $code = shift; my $str = shift; my $input = shift; $self->send(chr($code).$str); foreach my $b(unpack("C*", $input)) { if($b == 0xFF || $b == 0x00) { $self->{sock}->send(0xFF); } $self->{sock}->send(chr($b)); } $self->{sock}->send(chr(0)); $self->{info} = $self->_receive(); die $self->{info} if !$self->ok(); } 1; package Query; our $session; our @cache; our $pos; our $id; sub new { my $class = shift; $session = shift; my $cmd = shift; my $self = bless({}, $class); $id = exc(chr(0), $cmd); return $self; } sub bind { shift; my $name = shift; my $value = shift; my $type = shift; $type = "" if !$type; exc(chr(3), $id.chr(0).$name.chr(0).$value.chr(0).$type); undef @cache; } sub context { shift; my $value = shift; my $type = shift; $type = "" if !$type; exc(chr(14), $id.chr(0).$value.chr(0).$type); undef @cache; } sub execute { return exc(chr(5), $id); } sub more { if(!@cache) { $session->send(chr(4).$id.chr(0)); push(@cache, $session->_receive()) while $session->_read(); die $session->_receive() if !$session->ok(); $pos = 0; } my $more = $pos < @cache; undef @cache if !$more; return $more; } sub next { return more() && $cache[$pos++]; } sub info { return exc(chr(6), $id); } sub options { return exc(chr(7), $id); } sub close { exc(chr(2), $id); } sub exc { my $cmd = shift; my $arg = shift; $session->send($cmd.$arg); my $s = $session->_receive(); die $session->_receive() if !$session->ok(); return $s; } 1; basex-8.5.1/basex-api/src/main/perl/CreateExample.pl000066400000000000000000000012231274071665100222400ustar00rootroot00000000000000# This example shows how new databases can be created. # # Documentation: http://docs.basex.org/wiki/Clients # # (C) BaseX Team 2005-12, BSD License use BaseXClient; use warnings; use strict; eval { # create session my $session = Session->new("localhost", 1984, "admin", "admin"); # create new database $session->create("database", "Hello World!"); print $session->info()."\n"; # run query on database print $session->execute("xquery doc('database')")."\n"; # drop database $session->execute("drop db database"); # close session $session->close(); }; # print exception print $@ if $@; basex-8.5.1/basex-api/src/main/perl/Example.pl000066400000000000000000000012521274071665100211160ustar00rootroot00000000000000# This example shows how database commands can be executed. # # Documentation: http://docs.basex.org/wiki/Clients # # (C) BaseX Team 2005-12, BSD License use BaseXClient; use Time::HiRes; use warnings; use strict; eval { # initialize timer my $start = [ Time::HiRes::gettimeofday( ) ]; # create session my $session = Session->new("localhost", 1984, "admin", "admin"); # perform command and print returned string print $session->execute("xquery 1 to 10"); # close session $session->close(); # print time needed my $time = Time::HiRes::tv_interval($start) * 1000; print "\n$time ms.\n"; }; # print exception print $@ if $@; basex-8.5.1/basex-api/src/main/perl/QueryBindExample.pl000066400000000000000000000013741274071665100227460ustar00rootroot00000000000000# This example shows how external variables can be bound to XQuery expressions. # # Documentation: http://docs.basex.org/wiki/Clients # # (C) BaseX Team 2005-12, BSD License use BaseXClient; use Time::HiRes; use warnings; use strict; eval { # create session my $session = Session->new("localhost", 1984, "admin", "admin"); # create query instance my $input = "declare variable \$name external; ". "for \$i in 1 to 10 return element { \$name } { \$i }"; my $query = $session->query($input); # bind variable $query->bind("name", "number", ""); # print result print $query->execute()."\n"; # close query $query->close(); # close session $session->close(); }; # print exception print $@ if $@; basex-8.5.1/basex-api/src/main/perl/QueryExample.pl000066400000000000000000000012641274071665100221470ustar00rootroot00000000000000# This example shows how queries can be executed in an iterative manner. # # Documentation: http://docs.basex.org/wiki/Clients # # (C) BaseX Team 2005-12, BSD License use BaseXClient; use Time::HiRes; use warnings; use strict; eval { # create session my $session = Session->new("localhost", 1984, "admin", "admin"); # create query instance my $input = "for \$i in 1 to 10 return Text { \$i }"; my $query = $session->query($input); # loop through all results while ($query->more()) { print $query->next()."\n"; } # close query $query->close(); # close session $session->close(); }; # print exception print $@ if $@;basex-8.5.1/basex-api/src/main/php/000077500000000000000000000000001274071665100170135ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/php/AddExample.php000066400000000000000000000016151274071665100215330ustar00rootroot00000000000000execute("create db database"); print $session->info(); // add document $session->add("world/World.xml", "Hello World!"); print "
".$session->info(); // add document $session->add("Universe.xml", "Hello Universe!"); print "
".$session->info(); // run query on database print "
".$session->execute("xquery /"); // drop database $session->execute("drop db database"); // close session $session->close(); } catch (Exception $e) { // print exception print $e->getMessage(); } ?> basex-8.5.1/basex-api/src/main/php/BaseXClient.php000066400000000000000000000107731274071665100216750ustar00rootroot00000000000000socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if(!socket_connect($this->socket, $h, $p)) { throw new Exception("Can't communicate with server."); } // receive timestamp $ts = $this->readString(); // Hash container if (false !== strpos($ts, ':')) { // digest-auth $challenge = explode(':', $ts, 2); $md5 = hash("md5", hash("md5", $user . ':' . $challenge[0] . ':' . $pw) . $challenge[1]); } else { // Legacy: cram-md5 $md5 = hash("md5", hash("md5", $pw) . $ts); } // send username and hashed password/timestamp socket_write($this->socket, $user . chr(0) . $md5 . chr(0)); // receives success flag if(socket_read($this->socket, 1) != chr(0)) { throw new Exception("Access denied."); } } public function execute($com) { // send command to server socket_write($this->socket, $com.chr(0)); // receive result $result = $this->receive(); $this->info = $this->readString(); if($this->ok() != True) { throw new Exception($this->info); } return $result; } public function query($q) { return new Query($this, $q); } public function create($name, $input) { $this->sendCmd(8, $name, $input); } public function add($path, $input) { $this->sendCmd(9, $path, $input); } public function replace($path, $input) { $this->sendCmd(12, $path, $input); } public function store($path, $input) { $this->sendCmd(13, $path, $input); } public function info() { return $this->info; } public function close() { socket_write($this->socket, "exit".chr(0)); socket_close($this->socket); } private function init() { $this->bpos = 0; $this->bsize = 0; } public function readString() { $com = ""; while(($d = $this->read()) != chr(0)) { $com .= $d; } return $com; } private function read() { if($this->bpos == $this->bsize) { $this->bsize = socket_recv($this->socket, $this->buffer, 4096, 0); $this->bpos = 0; } return $this->buffer[$this->bpos++]; } private function sendCmd($code, $arg, $input) { socket_write($this->socket, chr($code).$arg.chr(0).$input.chr(0)); $this->info = $this->receive(); if($this->ok() != True) { throw new Exception($this->info); } } public function send($str) { socket_write($this->socket, $str.chr(0)); } public function ok() { return $this->read() == chr(0); } public function receive() { $this->init(); return $this->readString(); } } class Query { var $session, $id, $open, $cache; public function __construct($s, $q) { $this->session = $s; $this->id = $this->exec(chr(0), $q); } public function bind($name, $value, $type = "") { $this->exec(chr(3), $this->id.chr(0).$name.chr(0).$value.chr(0).$type); } public function context($value, $type = "") { $this->exec(chr(14), $this->id.chr(0).$value.chr(0).$type); } public function execute() { return $this->exec(chr(5), $this->id); } public function more() { if($this->cache == NULL) { $this->pos = 0; $this->session->send(chr(4).$this->id.chr(0)); while(!$this->session->ok()) { $this->cache[] = $this->session->readString(); } if(!$this->session->ok()) { throw new Exception($this->session->readString()); } } if($this->pos < count($this->cache)) return true; $this->cache = NULL; return false; } public function next() { if($this->more()) { return $this->cache[$this->pos++]; } } public function info() { return $this->exec(chr(6), $this->id); } public function options() { return $this->exec(chr(7), $this->id); } public function close() { $this->exec(chr(2), $this->id); } public function exec($cmd, $arg) { $this->session->send($cmd.$arg); $s = $this->session->receive(); if($this->session->ok() != True) { throw new Exception($this->session->readString()); } return $s; } } ?> basex-8.5.1/basex-api/src/main/php/CreateExample.php000066400000000000000000000012571274071665100222500ustar00rootroot00000000000000create("database", "Hello World!"); print $session->info(); // run query on database print "
".$session->execute("xquery /"); // drop database $session->execute("drop db database"); // close session $session->close(); } catch (Exception $e) { // print exception print $e->getMessage(); } ?> basex-8.5.1/basex-api/src/main/php/Example.php000066400000000000000000000012501274071665100211150ustar00rootroot00000000000000execute("xquery 1 to 10"); // close session $session->close(); // print time needed $time = (microtime(true) - $start) * 1000; print "\n$time ms\n"; } catch (Exception $e) { // print exception print $e->getMessage(); } ?> basex-8.5.1/basex-api/src/main/php/QueryBindExample.php000066400000000000000000000015761274071665100227530ustar00rootroot00000000000000query($input); // bind variable $query->bind("name", "number"); // print results print $query->execute()."\n"; // close query instance $query->close(); } catch (Exception $e) { // print exception print $e->getMessage(); } // close session $session->close(); } catch (Exception $e) { // print exception print $e->getMessage(); } ?> basex-8.5.1/basex-api/src/main/php/QueryExample.php000066400000000000000000000016251274071665100221510ustar00rootroot00000000000000Text { $i }'; $query = $session->query($input); // loop through all results while($query->more()) { print $query->next()."\n"; } // close query instance $query->close(); } catch (Exception $e) { // print exception print $e->getMessage(); } // close session $session->close(); } catch (Exception $e) { // print exception print $e->getMessage(); } ?> basex-8.5.1/basex-api/src/main/python/000077500000000000000000000000001274071665100175455ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/python/AddExample.py000066400000000000000000000014561274071665100221310ustar00rootroot00000000000000# -*- coding: utf-8 -*- # This example shows how new documents can be added. # # Documentation: http://docs.basex.org/wiki/Clients # # (C) BaseX Team 2005-12, BSD License import BaseXClient # create session session = BaseXClient.Session('localhost', 1984, 'admin', 'admin') try: # create empty database session.execute("create db database") print(session.info()) # add document session.add("world/World.xml", "Hello World!") print(session.info()) # add document session.add("Universe.xml", "Hello Universe!") print(session.info()) # run query on database print("\n" + session.execute("xquery collection('database')")) # drop database session.execute("drop db database") finally: # close session if session: session.close() basex-8.5.1/basex-api/src/main/python/BaseXClient.py000066400000000000000000000237561274071665100222750ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ Python 2.7.3 and 3.x client for BaseX. Works with BaseX 7.0 and later Requires Python 3.x or Python 2.x having some backports like bytearray. (I've tested Python 3.2.3, and Python 2.7.3 on Fedora 16 linux x86_64.) LIMITATIONS: * binary content would corrupt, maybe. (I didn't test it) * also, will fail to extract stored binary content, maybe. (both my code, and original don't care escaped 0xff.) Documentation: http://docs.basex.org/wiki/Clients (C) 2012, Hiroaki Itoh. BSD License updated 2014 by Marc van Grootel """ import hashlib import socket import threading # --------------------------------- # class SocketWrapper(object): """a wrapper to python native socket module.""" def __init__(self, sock, receive_bytes_encoding='utf-8', send_bytes_encoding='utf-8'): self.receive_bytes_encoding = receive_bytes_encoding self.send_bytes_encoding = send_bytes_encoding self.terminator = bytearray(chr(0), self.receive_bytes_encoding) self.__s = sock self.__buf = bytearray(chr(0) * 0x1000, self.receive_bytes_encoding) self.__bpos = 0 self.__bsize = 0 def clear_buffer(self): """reset buffer status for next invocation ``recv_until_terminator()`` or ``recv_single_byte()``.""" self.__bpos = 0 self.__bsize = 0 def __fill_buffer(self): """cache next bytes""" if self.__bpos >= self.__bsize: self.__bsize = self.__s.recv_into(self.__buf) self.__bpos = 0 # Returns a single byte from the socket. def recv_single_byte(self): """recv a single byte from previously fetched buffer.""" self.__fill_buffer() result_byte = self.__buf[self.__bpos] self.__bpos += 1 return result_byte # Reads until terminator byte is found. def recv_until_terminator(self): """recv a nul(or specified as terminator_byte)-terminated whole string from previously fetched buffer.""" result_bytes = bytearray() while True: self.__fill_buffer() pos = self.__buf.find(self.terminator, self.__bpos, self.__bsize) if pos >= 0: result_bytes.extend(self.__buf[self.__bpos:pos]) self.__bpos = pos + 1 break else: result_bytes.extend(self.__buf[self.__bpos:self.__bsize]) self.__bpos = self.__bsize return result_bytes.decode(self.receive_bytes_encoding) def sendall(self, data): """sendall with specified byte encoding if data is not bytearray, bytes (maybe str). if data is bytearray or bytes, it will be passed to native sendall API directly.""" if isinstance(data, (bytearray, bytes)): return self.__s.sendall(data) return self.__s.sendall(bytearray(data, self.send_bytes_encoding)) def __getattr__(self, name): return lambda *arg, **kw: getattr(self.__s, name)(*arg, **kw) # --------------------------------- # class Session(object): """class Session. see http://docs.basex.org/wiki/Server_Protocol """ def __init__(self, host, port, user, password, receive_bytes_encoding='utf-8', send_bytes_encoding='utf-8'): """Create and return session with host, port, user name and password""" self.__info = None # create server connection self.__swrapper = SocketWrapper( socket.socket(socket.AF_INET, socket.SOCK_STREAM), receive_bytes_encoding=receive_bytes_encoding, send_bytes_encoding=send_bytes_encoding) self.__swrapper.connect((host, port)) # receive timestamp response = self.recv_c_str().split(':') # send username and hashed password/timestamp hfun = hashlib.md5() if len(response) > 1: code = "%s:%s:%s" % (user, response[0], password) nonce = response[1] else: code = password nonce = response[0] hfun.update(hashlib.md5(code.encode('us-ascii')).hexdigest().encode('us-ascii')) hfun.update(nonce.encode('us-ascii')) self.send(user + chr(0) + hfun.hexdigest()) # evaluate success flag if not self.server_response_success(): raise IOError('Access Denied.') def execute(self, com): """Execute a command and return the result""" # send command to server self.send(com) # receive result result = self.receive() self.__info = self.recv_c_str() if not self.server_response_success(): raise IOError(self.__info) return result def query(self, querytxt): """Creates a new query instance (having id returned from server).""" return Query(self, querytxt) def create(self, name, content): """Creates a new database with the specified input (may be empty).""" self.__send_input(8, name, content) def add(self, path, content): """Adds a new resource to the opened database.""" self.__send_input(9, path, content) def replace(self, path, content): """Replaces a resource with the specified input.""" self.__send_input(12, path, content) def store(self, path, content): """Stores a binary resource in the opened database. api won't escape 0x00, 0xff automatically, so you must do it yourself explicitly.""" # ------------------------------------------ # chr(13) + path + chr(0) + content + chr(0) self.__send_binary_input(13, path, content) # # ------------------------------------------ def info(self): """Return process information""" return self.__info def close(self): """Close the session""" self.send('exit') self.__swrapper.close() def recv_c_str(self): """Retrieve a string from the socket""" return self.__swrapper.recv_until_terminator() def send(self, value): """Send the defined string""" self.__swrapper.sendall(value + chr(0)) def __send_input(self, code, arg, content): """internal. don't care.""" self.__swrapper.sendall(chr(code) + arg + chr(0) + content + chr(0)) self.__info = self.recv_c_str() if not self.server_response_success(): raise IOError(self.info()) def __send_binary_input(self, code, path, content): """internal. don't care.""" # at this time, we can't use __send_input itself because of encoding # problem. we have to build bytearray directly. if not isinstance(content, (bytearray, bytes)): raise ValueError("Sorry, content must be bytearray or bytes, not " + str(type(content))) # ------------------------------------------ # chr(code) + path + chr(0) + content + chr(0) data = bytearray([code]) try: data.extend(path) except: data.extend(path.encode('utf-8')) data.extend([0]) data.extend(content) data.extend([0]) # # ------------------------------------------ self.__swrapper.sendall(data) self.__info = self.recv_c_str() if not self.server_response_success(): raise IOError(self.info()) def server_response_success(self): """Return success check""" return self.__swrapper.recv_single_byte() == 0 def receive(self): """Return received string""" self.__swrapper.clear_buffer() return self.recv_c_str() def iter_receive(self): """iter_receive() -> (typecode, item) iterate while the query returns items. typecode list is in http://docs.basex.org/wiki/Server_Protocol:_Types """ self.__swrapper.clear_buffer() typecode = self.__swrapper.recv_single_byte() while typecode > 0: string = self.recv_c_str() yield (typecode, string) typecode = self.__swrapper.recv_single_byte() if not self.server_response_success(): raise IOError(self.recv_c_str()) # --------------------------------- # class Query(): """class Query. see http://docs.basex.org/wiki/Server_Protocol """ def __init__(self, session, querytxt): """Create query object with session and query""" self.__session = session self.__id = self.__exc(chr(0), querytxt) def bind(self, name, value, datatype=''): """Binds a value to a variable. An empty string can be specified as data type.""" self.__exc(chr(3), self.__id + chr(0) + name + chr(0) + value + chr(0) + datatype) def context(self, value, datatype=''): """Bind the context item""" self.__exc(chr(14), self.__id + chr(0) + value + chr(0) + datatype) def iter(self): """iterate while the query returns items""" self.__session.send(chr(4) + self.__id) return self.__session.iter_receive() def execute(self): """Execute the query and return the result""" return self.__exc(chr(5), self.__id) def info(self): """Return query information""" return self.__exc(chr(6), self.__id) def options(self): """Return serialization parameters""" return self.__exc(chr(7), self.__id) def updating(self): """Returns true if the query may perform updates; false otherwise.""" return self.__exc(chr(30), self.__id) def full(self): """Returns all resulting items as strings, prefixed by XDM Meta Data.""" return self.__exc(chr(31), self.__id) def close(self): """Close the query""" self.__exc(chr(2), self.__id) def __exc(self, cmd, arg): """internal. don't care.""" # should we expose this? # (this makes sense only when mismatch between C/S is existing.) self.__session.send(cmd + arg) result = self.__session.receive() if not self.__session.server_response_success(): raise IOError(self.__session.recv_c_str()) return result basex-8.5.1/basex-api/src/main/python/CreateExample.py000066400000000000000000000011741274071665100226410ustar00rootroot00000000000000# -*- coding: utf-8 -*- # This example shows how new databases can be created. # # Documentation: http://docs.basex.org/wiki/Clients # # (C) BaseX Team 2005-12, BSD License import BaseXClient # create session session = BaseXClient.Session('localhost', 1984, 'admin', 'admin') try: # create new database session.create("database", "Hello World!") print(session.info()) # run query on database print("\n" + session.execute("xquery doc('database')")) # drop database session.execute("drop db database") print(session.info()) finally: # close session if session: session.close() basex-8.5.1/basex-api/src/main/python/Example.py000066400000000000000000000010231274071665100215060ustar00rootroot00000000000000# -*- coding: utf-8 -*- # This example shows how database commands can be executed. # # Documentation: http://docs.basex.org/wiki/Clients # # (C) BaseX Team 2005-12, BSD License import BaseXClient, time # initialize timer start = time.clock() # create session session = BaseXClient.Session('localhost', 1984, 'admin', 'admin') # perform command and print returned string print(session.execute("xquery 1 to 10")) # close session session.close() # print time needed time = (time.clock() - start) * 1000 print("%.2f ms" % time) basex-8.5.1/basex-api/src/main/python/QueryBindExample.py000066400000000000000000000012531274071665100233360ustar00rootroot00000000000000# -*- coding: utf-8 -*- # This example shows how external variables can be bound to XQuery expressions. # # Documentation: http://docs.basex.org/wiki/Clients # # (C) BaseX Team 2005-12, BSD License import BaseXClient # create session session = BaseXClient.Session('localhost', 1984, 'admin', 'admin') try: # create query instance input = "declare variable $name external; for $i in 1 to 10 return element { $name } { $i }" query = session.query(input) # bind variable query.bind("$name", "number") # print result print(query.execute()) # close query object query.close() finally: # close session if session: session.close() basex-8.5.1/basex-api/src/main/python/QueryExample.py000066400000000000000000000013751274071665100225460ustar00rootroot00000000000000# -*- coding: utf-8 -*- # This example shows how queries can be executed in an iterative manner. # Iterative evaluation will be slower, as more server requests are performed. # # Documentation: http://docs.basex.org/wiki/Clients # # (C) BaseX Team 2005-12, BSD License import BaseXClient # create session session = BaseXClient.Session('localhost', 1984, 'admin', 'admin') try: # create query instance input = "for $i in 1 to 10 return Text { $i }" query = session.query(input) # loop through all results for typecode, item in query.iter(): print("typecode=%d" % typecode) print("item=%s" % item) # close query object query.close() finally: # close session if session: session.close() basex-8.5.1/basex-api/src/main/python/UTF16Example.py000066400000000000000000000025651274071665100222500ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Documentation: http://docs.basex.org/wiki/Clients # # (C) BaseX Team 2005-12, BSD License import BaseXClient import sys if sys.version < '3': # i'm testing with Python 2.7.3 import codecs sys.stdout = codecs.getwriter('utf-8')(sys.stdout) import xml.dom.minidom # input encoding is utf-16le doc = xml.dom.minidom.parse("UTF16example.xml") # expat parser will decode it to real unicode, and rewrite processing instruction. # so, we can send this (->toxml()) as content for basex, safely. content = doc.toxml() # str if Python 3.x, unicode if Python 2.x. # (both are actually real unicode. (ucs2 or ucs4.)) print(type(content)) # create session session = BaseXClient.Session('localhost', 1984, 'admin', 'admin') try: # create empty database session.execute("create db py3clientexample") print(session.info()) # add document session.add("py3clientexample/originally_u16le.xml", content) print(session.info()) # run query on database query = session.query("""doc('py3clientexample')""") for typecode, item in query.iter(): print("typecode=%d" % typecode) print("item=%s" % item) # drop database session.execute("drop db py3clientexample") print(session.info()) except Exception as e: # print exception print(repr(e)) finally: # close session if session: session.close() basex-8.5.1/basex-api/src/main/python/UTF16example.xml000066400000000000000000000005041274071665100224470ustar00rootroot00000000000000<?xml version="1.0" encoding="utf-16le"?> <document> <list> <item id="1">X</item> <item id="2">_</item> <item id="3">S</item> </list> </document> basex-8.5.1/basex-api/src/main/qt/000077500000000000000000000000001274071665100166505ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/qt/BaseX.pro000066400000000000000000000005711274071665100203770ustar00rootroot00000000000000#------------------------------------------------- # # Project created by QtCreator 2011-09-11T18:10:19 # #------------------------------------------------- QT += core gui network TARGET = BaseX TEMPLATE = app SOURCES += main.cpp\ mainwindow.cpp \ basexthread.cpp HEADERS += mainwindow.h \ basexthread.h FORMS += mainwindow.ui RESOURCES += basex-8.5.1/basex-api/src/main/qt/basexthread.cpp000066400000000000000000000076041274071665100216550ustar00rootroot00000000000000#include "basexthread.h" BaseXThread::BaseXThread(QString &host, quint16 port, QObject *parent) : QThread(parent), hostName(host),port(port), quit(false), Timeout(5*1000), dbuser("admin"), dbpasswd("admin"), firstRun(true) { END_RES = QByteArray::fromHex("0000"); RES_FAIL = QByteArray::fromHex("0001"); } BaseXThread::~BaseXThread(){ mutex.lock(); quit = true; cond.wakeOne(); mutex.unlock(); qDebug()<<"destroy"; wait(); } int BaseXThread::addRequest(QString request){ QMutexLocker locker(&mutex); requestQueue.enqueue(request); if (!isRunning()) start(); else cond.wakeOne(); return 1; } void BaseXThread::run(){ if (firstRun){ socket = new QTcpSocket(); firstRun=false; } while(!quit){ mutex.lock(); QString actualQuery = requestQueue.dequeue(); mutex.unlock(); if(!socket->isOpen()){ socket->connectToHost(hostName, port); if (!socket->waitForConnected(Timeout)) { emit socketError(socket->error(), socket->errorString()); return; } if(!connectToBasex()) { return; } } char* q = actualQuery.toUtf8().data(); QByteArray * ts = new QByteArray(); socket->write(q, strlen(q)+1); if (!socket->waitForBytesWritten(Timeout)) { emit socketError(socket->error(), socket->errorString()); //return false; }else{ bool leave = false; bool xqueryError = false; while (!leave) { if (!socket->waitForReadyRead(Timeout)) { emit socketError(socket->error(), socket->errorString()); }else{ ts->append(socket->readAll()); if (ts->endsWith(END_RES)) leave=true; if (ts->endsWith(RES_FAIL)){ leave=true; xqueryError=true; } } } // QString resString(*ts); // qDebug()<<"res: "<<(xqueryError?"error:":"")<isOpen()){ socket->write("exit",5); socket->flush(); } } } bool BaseXThread::connectToBasex(){ // wait for timestamp bool leave = false; QByteArray ts; while (!leave) { if (!socket->waitForReadyRead(Timeout)) { emit socketError(socket->error(), socket->errorString()); return false; }else{ ts = socket->readAll(); ts.chop(1); // skip NullTermintation leave = true; } } QByteArray codingArray; codingArray.append(QCryptographicHash::hash(dbpasswd.toUtf8(), QCryptographicHash::Md5).toHex()); codingArray.append(ts); codingArray = QCryptographicHash::hash(codingArray, QCryptographicHash::Md5).toHex(); QByteArray nameArray = dbuser.toUtf8(); //send name char* nameChar = nameArray.data(); qDebug()<< socket->write(nameChar,strlen(nameChar)+1); //send md(md(passwd) + timestamp) char* codeChar = codingArray.data(); qDebug()<< socket->write(codeChar,strlen(codeChar)+1); ts.clear(); if (!socket->waitForReadyRead(Timeout)) { emit socketError(socket->error(), socket->errorString()); return false; }else{ ts.append(socket->readAll()); if (ts.at(0)=='\0'){ qDebug()<<"connection ok"; }else{ emit socketError(1000, "BASEX user authentification failed."); return false; } } return true; } basex-8.5.1/basex-api/src/main/qt/basexthread.h000066400000000000000000000015401274071665100213130ustar00rootroot00000000000000#ifndef BASEXTHREAD_H #define BASEXTHREAD_H #include #include #include #include class BaseXThread : public QThread { Q_OBJECT public: explicit BaseXThread(QString &host, quint16 port, QObject *parent = 0); ~BaseXThread(); int addRequest(QString request); void run(); signals: void newResult(const QByteArray *res); void socketError(int socketError, const QString &message); void error(int error, const QString &message); private: QString hostName; quint16 port; QMutex mutex; QWaitCondition cond; bool quit; bool firstRun; QString dbuser; QString dbpasswd; QQueue requestQueue; QByteArray END_RES; QByteArray RES_FAIL; QTcpSocket *socket; bool connectToBasex(); const int Timeout; }; #endif // BASEXTHREAD_H basex-8.5.1/basex-api/src/main/qt/example.xml000066400000000000000000000105151274071665100210270ustar00rootroot00000000000000 Gambardella, Matthew XML Developer's Guide Computer 44.95 2000-10-01 An in-depth look at creating applications with XML. Ralls, Kim Midnight Rain Fantasy 5.95 2000-12-16 A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world. Corets, Eva Maeve Ascendant Fantasy 5.95 2000-11-17 After the collapse of a nanotechnology society in England, the young survivors lay the foundation for a new society. Corets, Eva Oberon's Legacy Fantasy 5.95 2001-03-10 In post-apocalypse England, the mysterious agent known only as Oberon helps to create a new life for the inhabitants of London. Sequel to Maeve Ascendant. Corets, Eva The Sundered Grail Fantasy 5.95 2001-09-10 The two daughters of Maeve, half-sisters, battle one another for control of England. Sequel to Oberon's Legacy. Randall, Cynthia Lover Birds Romance 4.95 2000-09-02 When Carla meets Paul at an ornithology conference, tempers fly as feathers get ruffled. Thurman, Paula Splish Splash Romance 4.95 2000-11-02 A deep sea diver finds true love twenty thousand leagues beneath the sea. Knorr, Stefan Creepy Crawlies Horror 4.95 2000-12-06 An anthology of horror stories about roaches, centipedes, scorpions and other insects. Kress, Peter Paradox Lost Science Fiction 6.95 2000-11-02 After an inadvertant trip through a Heisenberg Uncertainty Device, James Salway discovers the problems of being quantum. O'Brien, Tim Microsoft .NET: The Programming Bible Computer 36.95 2000-12-09 Microsoft's .NET initiative is explored in detail in this deep programmer's reference. O'Brien, Tim MSXML3: A Comprehensive Guide Computer 36.95 2000-12-01 The Microsoft MSXML3 parser is covered in detail, with attention to XML DOM interfaces, XSLT processing, SAX and more. Galos, Mike Visual Studio 7: A Comprehensive Guide Computer 49.95 2001-04-16 Microsoft Visual Studio 7 is explored in depth, looking at how Visual Basic, Visual C++, C#, and ASP+ are integrated into a comprehensive development environment. basex-8.5.1/basex-api/src/main/qt/main.cpp000066400000000000000000000002621274071665100203000ustar00rootroot00000000000000#include #include "mainwindow.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); } basex-8.5.1/basex-api/src/main/qt/mainwindow.cpp000066400000000000000000000043171274071665100215350ustar00rootroot00000000000000#include "mainwindow.h" #include "ui_mainwindow.h" #include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); QString host("localhost"); bxth = new BaseXThread(host,1984, this); connect(bxth, SIGNAL(socketError(int,const QString&)), SLOT(displayError(int,const QString&))); connect(ui->doItButton, SIGNAL(clicked()), SLOT(buttonDo())); connect(bxth,SIGNAL(newResult(const QByteArray*)),SLOT(basexFeedBack(const QByteArray*))); } void MainWindow::displayError(int socketError, const QString &errorString) { switch (socketError) { case QAbstractSocket::RemoteHostClosedError: break; case QAbstractSocket::HostNotFoundError: QMessageBox::information(this, tr("BaseX Client"), tr("The host was not found. Please check the " "host name and port settings.")); break; case QAbstractSocket::ConnectionRefusedError: QMessageBox::information(this, tr("BaseX Client"), tr("The connection was refused by the peer. " "Make sure the BaseX server is running, " "and check that the host name and port " "settings are correct.")); break; default: QMessageBox::information(this, tr("BaseX Client"), tr("The following error occurred:\n %1.") .arg(errorString)); } } MainWindow::~MainWindow() { delete ui; } void MainWindow::basexFeedBack(const QByteArray *array) { QString abc(ui->report->toPlainText()); abc.append(*array); ui->report->setPlainText(abc); delete array; } void MainWindow::buttonDo() { QString fileName = QFileDialog::getOpenFileName(this, tr("Open example.xml"), ".", tr("Example File (*example.xml)")); qDebug()<0){ bxth->addRequest(QString("create db test0815 %1").arg(fileName)); bxth->addRequest("xquery //book"); bxth->addRequest("close"); bxth->addRequest("drop database test0815"); } } basex-8.5.1/basex-api/src/main/qt/mainwindow.h000066400000000000000000000010171274071665100211740ustar00rootroot00000000000000#ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include #include "basexthread.h" namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private: Ui::MainWindow *ui; BaseXThread * bxth; private slots: void displayError(int socketError, const QString &errorString); void basexFeedBack(const QByteArray * array); void buttonDo(); }; #endif // MAINWINDOW_H basex-8.5.1/basex-api/src/main/qt/mainwindow.ui000066400000000000000000000033061274071665100213650ustar00rootroot00000000000000 MainWindow 0 0 931 833 MainWindow open example.xml and query Qt::Vertical 20 40 0 0 931 22 TopToolBarArea false basex-8.5.1/basex-api/src/main/rebol/000077500000000000000000000000001274071665100173275ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/rebol/basexclient.r000066400000000000000000000111731274071665100220160ustar00rootroot00000000000000Rebol [ Title: "Rebol client for BaseX" Author: "Sabu Francis" Date: "March 10, 2010" LastUpdated: "June 5, 2010" Version: "0.1" Copyright: "Sabu Francis, Navi Mumbai, India" License: "Perl Artistic License 2.0" LicenseTextUrl: http://www.perlfoundation.org/artistic_license_2_0 Purpose: {"BaseX Rebol client; works with BaseX 6.x (but not with BaseX 7.0 and later)"} ;following data are for www.rebol.org library ;you can find a lot of rebol script there library: [ level: 'beginner platform: 'all type: [api library] domain: [console] tested-under: [windows linux] support: none license: [artistic_license_2_0] see-also: http://code.google.com/p/reb-basex ] ChangeHistory: { June 5, 2010: Created a Google Project space for it and uploaded it there. } ToDo: { a) Error reporting. } ] getmd5: func [ { An Md5 function, the way it is done in other languages. For some reason Rebol gives the MD5 in uppercase. This takes in a string and gives out its md5 hash } s [string!] ] [ lowercase replace/all replace/all mold checksum/method copy s 'md5 "#{" "" "}" "" ] gethashed: func[ { Two params: 1st is the password, 2nd the time stamp It concantenates the md5 hash of the password with the password and then make an md5 hash of the entire concatenation That is how BaseX likes it } passwd [string!] ts [string!] /local p5 ] [ getmd5 rejoin [ getmd5 passwd ts ] ] copyC: func [myport /local response data] [ wait myport ;;; waiting at the port is important!!! data: copy myport data ] execBaseX: func [ { 1st param: username 2nd: Password. 3rd: BaseX command Example of 3rd param: "XQUERY basex:db('test')//author" Refinement /p specifies port Refinement /w specifies timeout (not used in this version) This does complete execution of one BaseX command right from login to logout It has a refinement /p using which you can specify the port to which to write to } user [string!] passwd [string!] Xqs [string!] /p port [any-type!] /w waittime [number!] /local c ts r1 rslt] [ system/schemes/default/timeout: 0.1 either p [ c: open/no-wait port] ;;TCP Port should ONLY be opened using no-wait else it blocks! [ c: open/no-wait tcp://localhost:1984] ;print "Wokay, opened port" ts: copyC c ;print ["Timestamp: " ts] replace ts "^@" "" ;Remove the null character at end sent by BaseX insert c user insert c "^@" insert c gethashed passwd ts insert c "^@" rslt: copyC c ; At this point we should get an empty string if login successful ;print ["Result of login: " rslt ] insert c Xqs ;;Now we send the command to be executed to BaseX on this session ;"XQUERY basex:db('test')//author" insert c "^@" rslt: replace copyC c "^@" "" insert c "EXIT^@" ;This is the logout function in BaseX. It closes the session close c rslt;;; returns back the string that was result of the command given to the BaseX engine in this session ] BaseXLogin: func [ { 1st param: username 2nd: Password. Refinement /p specifies port Refinement /w specifies timeout (not used in this version) This returns a connection port that can be used later After you finish using the connection, make sure you do close it by using BaseXLogout } user [string!] passwd [string!] /p port [any-type!] /w waittime [number!] /local c ts r1 rslt] [ system/schemes/default/timeout: 0.1 either p [ c: open/no-wait port] ;;TCP Port should ONLY be opened using no-wait else it blocks! [ c: open/no-wait tcp://localhost:1984] ;print "Wokay, opened port" ts: copyC c ;print ["Timestamp: " ts] replace ts "^@" "" ;Remove the null character at end sent by BaseX insert c user insert c "^@" insert c gethashed passwd ts insert c "^@" rslt: copyC c ; At this point we should get an empty string if login successful ;print ["Result of login: " rslt ] return c ;;; return the Port for this session ] BaseXExecute: func [ { This takes two arguments. 1st one is a connection port, 2nd is the BaseX command to be executed Example: "XQUERY basex:db('test')//author" } c [any-type!] Xqs [string!] ] [ insert c Xqs ;;Now we send the command to be executed to BaseX on this session insert c "^@" rslt: replace copyC c "^@" "" return rslt ] BaseXLogout: func [ {This has to be called to close the BaseX connection} c [any-type!] ] [ insert c "EXIT^@" ;This is the logout function in BaseX. It closes the session close c ;;; and also closes the port! ]basex-8.5.1/basex-api/src/main/resources/000077500000000000000000000000001274071665100202365ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/resources/.gitignore000066400000000000000000000000111274071665100222160ustar00rootroot00000000000000/WEB-INF basex-8.5.1/basex-api/src/main/resources/xquery/000077500000000000000000000000001274071665100215735ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/resources/xquery/webdav.xqm000066400000000000000000000134311274071665100235740ustar00rootroot00000000000000(:~ : This module contains helper functions for locking documents in WebDAV. : : @author BaseX Team 2005-16, BSD License :) module namespace w = 'http://basex.org/modules/webdav'; (: Lock database name. :) declare variable $w:locks-db := '~webdav'; (: Lock error. :) declare variable $w:err-locked := QName('http://basex.org/modules/webdav', 'w:locked_423'); (:~ : Decomposes a path into segments. : @param $path path : @return segments :) declare function w:path-segments( $path as xs:string ) as xs:string* { tokenize($path, '/')[.] }; (:~ : Checks if the specified strings are equal. : @param $x first strings : @param $y second strings : @return result of check :) declare function w:is-prefix( $x as xs:string*, $y as xs:string* ) as xs:boolean { every $r in for-each-pair($x, $y, function($a, $b) { $a eq $b }) satisfies $r }; (:~ : Checks if a lock with the given path has locked : (possibly indirectly) another resource. : @param $ancestor ancestor resource : @param $descendant descendant resource : @param $depth depth : @return result of check :) declare function w:has-locked( $ancestor as xs:string, $descendant as xs:string, $depth as xs:string ) as xs:boolean { let $ancestor-segments := w:path-segments($ancestor), $descendant-segments := w:path-segments($descendant) return switch($depth) case '0' case '1' return count($ancestor-segments) + $depth eq count($descendant-segments) and w:is-prefix($ancestor-segments, $descendant-segments) case 'infinity' return w:is-prefix($ancestor-segments, $descendant-segments) default return false() }; (:~ : Calculates the lock expiration date-time given its timeout in seconds. : @param $timeout time out : @return lock expiration :) declare function w:expiry-dateTime( $timeout as xs:integer ) as xs:dateTime { current-dateTime() + xs:dayTimeDuration('PT' || $timeout || 'S') }; (:~ : Checks if the lock database exists. : @return result of check :) declare function w:lock-db-exists() as xs:boolean { db:exists($w:locks-db) }; (:~ : Creates the database for the WebDAV locks if it does not exist. :) declare %updating function w:init-lock-db() { if(w:lock-db-exists()) then () else db:create($w:locks-db, , $w:locks-db) }; (:~ : Opens the lock database if it exists; otherwise, returns an empty sequence. : @return database or empty sequence :) declare function w:open-lock-db() as document-node()? { if(w:lock-db-exists()) then db:open($w:locks-db) else () }; (:~ : Finds all active locks of the given path. : @param $path path to check : @return active lock info elements :) declare function w:locks-on( $path as xs:string ) as element(w:lockinfo)* { w:open-lock-db()/w:locks/w:lockinfo[ w:has-locked(w:path, $path, 'infinity') and xs:dateTime(w:expiry) gt current-dateTime() ] }; (:~ : Checks if two locks are in conflict. : @param $lock1 first lock : @param $lock2 second lock : @return result of check :) declare function w:in-conflict( $lock1 as element(w:lockinfo), $lock2 as element(w:lockinfo) ) as xs:boolean { (w:has-locked($lock1/w:path, $lock2/w:path, $lock1/w:depth) or w:has-locked($lock2/w:path, $lock1/w:path, $lock2/w:depth)) and (w:is-exclusive($lock1) or w:is-exclusive($lock2)) }; (:~ : Checks if a lock is exclusive. : @param $lock lock to check : @return result of check :) declare function w:is-exclusive( $lock as element(w:lockinfo) ) as xs:boolean { $lock/w:scope eq 'exclusive' }; (:~ : Gets all locks which are in conflict with the given one. : @param $requested-lock requested lock : @return elements with conflicting locks :) declare function w:conflicting-locks( $requested-lock as element(w:lockinfo) ) as element(w:lockinfo)* { for $existing-lock in w:locks-on($requested-lock/w:path) where w:in-conflict($requested-lock, $existing-lock) return $existing-lock }; (:~ : Renews a lock with the given token. : @param $token lock token :) declare %updating function w:refresh-lock( $token as xs:string ) { for $lock in w:lock($token) return replace value of node $lock/w:expiry with w:expiry-dateTime($lock/w:timeout) }; (:~ : Returns the lock with the given token. : @param $token lock token : @return lock element :) declare function w:lock( $token as xs:string ) as element(w:lockinfo)* { w:open-lock-db()/w:locks/w:lockinfo[w:token eq $token] }; (:~ : Creates a new lock for a given path. : @param $path path : @param $token token : @param $scope scope : @param $type type : @param $depth depth : @param $owner owner : @param $timeout timeout :) declare %updating function w:create-lock( $path as xs:string, $token as xs:string, $scope as xs:string, $type as xs:string, $depth as xs:string, $owner as xs:string, $timeout as xs:integer ) { (: limit timeout to one year (avoid overflow) :) let $timeout := min((xs:integer($timeout), 31700000)) let $expiry := w:expiry-dateTime($timeout), $requested-lock := element w:lockinfo { element w:path { $path }, element w:token { $token }, element w:scope { $scope }, element w:type { $type }, element w:depth { $depth }, element w:owner { $owner }, element w:timeout { $timeout }, element w:expiry { $expiry } } return if(w:conflicting-locks($requested-lock)) then error($w:err-locked, 'Resource has a conflicting lock', $path) else insert node $requested-lock as last into w:open-lock-db()/w:locks }; (:~ : Removes a lock given its token. : @param $token lock token :) declare %updating function w:delete-lock( $token as xs:string ) { delete node w:lock($token) }; basex-8.5.1/basex-api/src/main/ruby/000077500000000000000000000000001274071665100172055ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/ruby/AddExample.rb000066400000000000000000000015621274071665100215420ustar00rootroot00000000000000# This example shows how new documents can be added. # # Documentation: http://docs.basex.org/wiki/Clients # # (C) BaseX Team 2005-12, BSD License require './BaseXClient.rb' begin # create session session = BaseXClient::Session.new("localhost", 1984, "admin", "admin") # create empty database session.execute("create db database") print "\n" + session.info() # add document session.add("world/World.xml", "Hello World!") print "\n" + session.info() # add document session.add("Universe.xml", "Hello Universe!") print "\n" + session.info() + "\n" # run query on database print "\n" + session.execute("xquery collection('database')") + "\n" # drop database session.execute("drop db database") # close session session.close rescue Exception => e # print exception puts e end basex-8.5.1/basex-api/src/main/ruby/BaseXClient.rb000066400000000000000000000070411274071665100216750ustar00rootroot00000000000000# Ruby client for BaseX. # Works with BaseX 7.0 and later # # Documentation: http://docs.basex.org/wiki/Clients # # (C) BaseX Team 2005-12, BSD License require 'socket' require 'digest/md5' module BaseXClient class Session def initialize(host, port, username, pw) # create server connection @socket = TCPSocket.open(host, port) # authenticate hash = Digest::MD5.new # response either {nonce} or {realm:nonce} rec = receive.split(':') if rec.length == 1 hash.update(hash.hexdigest(pw)) hash.update(rec[0]) else hash.update(hash.hexdigest([username, rec[0], pw].join(':'))) hash.update(rec[1]) end send(username) send(hash.hexdigest()) # evaluate success flag if read != 0.chr raise "Access denied." end @char_lead_byte = "\xFF" @char_lead_byte.force_encoding('ASCII-8BIT') end def execute(com) # send command to server send(com) # receive result result = receive @info = receive if !ok raise @info end return result end def query(cmd) return Query.new(self, cmd) end def create(name, input) sendCmd(8.chr, name, input) end def add(path, input) sendCmd(9.chr, path, input) end def replace(path, input) sendCmd(12.chr, path, input) end def store(path, input) sendCmd(13.chr, path, input) end def info() return @info end def close() send("exit") @socket.close end # Receives a string from the socket. def receive() complete = "" while (t = read) != 0.chr if t == @char_lead_byte then t = read end complete << t end return complete end # Sends the defined str. def send(str) @socket.write(str + 0.chr) end def sendCmd(cmd, arg, input) send(cmd + arg + 0.chr + input) @info = receive if !ok raise @info end end # Returns a single byte from the socket. def read() return @socket.read(1) end def write(i) @socket.write(i) end # Returns success check. def ok() return read == 0.chr end end class Query def initialize(s, q) @session = s @id = exec(0.chr, q) @cache = [] @pos = 0 end def bind(name, value, type="") exec(3.chr, @id + 0.chr + name + 0.chr + value + 0.chr + type) end def context(value, type="") exec(14.chr, @id + 0.chr + value + 0.chr + type) end def more() if @cache.length == 0 @session.write(4.chr) @session.send(@id) while @session.read > 0.chr @cache << @session.receive end if !@session.ok raise @session.receive end end return @pos < @cache.length end def next if more() @pos += 1 return @cache[@pos - 1] end end def execute() return exec(5.chr, @id) end def info() return exec(6.chr, @id) end def close() return exec(2.chr, @id) end def exec(cmd, arg) @session.send(cmd + arg) s = @session.receive if !@session.ok raise @session.receive end return s end end end basex-8.5.1/basex-api/src/main/ruby/CreateExample.rb000066400000000000000000000012211274071665100222450ustar00rootroot00000000000000# This example shows how new databases can be created. # Documentation: http://docs.basex.org/wiki/Clients # # (C) BaseX Team 2005-12, BSD License require './BaseXClient.rb' begin # create session session = BaseXClient::Session.new("localhost", 1984, "admin", "admin") # create new database session.create("database", "Hello World!") print "\n" + session.info() # run query on database print "\n" + session.execute("xquery doc('database')") + "\n" # drop database session.execute("drop db database") # close session session.close rescue Exception => e # print exception puts e end basex-8.5.1/basex-api/src/main/ruby/Example.rb000066400000000000000000000011531274071665100211250ustar00rootroot00000000000000# This example shows how database commands can be executed. # Documentation: http://docs.basex.org/wiki/Clients # # (C) BaseX Team 2005-12, BSD License require './BaseXClient.rb' begin # initialize timer start_time = Time.now # create session session = BaseXClient::Session.new("localhost", 1984, "admin", "admin") # perform command and print returned string print session.execute("xquery 1 to 10") # close session session.close # print time needed time = (Time.now - start_time) * 1000 puts " #{time} ms." rescue Exception => e # print exception puts e end basex-8.5.1/basex-api/src/main/ruby/QueryBindExample.rb000066400000000000000000000014671274071665100227600ustar00rootroot00000000000000# This example shows how external variables can be bound to XQuery expressions. # # Documentation: http://docs.basex.org/wiki/Clients # # (C) BaseX Team 2005-12, BSD License require './BaseXClient.rb' begin # create session session = BaseXClient::Session.new("localhost", 1984, "admin", "admin") begin # create query instance input = "declare variable $name external; for $i in 1 to 10 return element { $name } { $i }" query = session.query(input) # bind variable query.bind("$name", "number") # print result print query.execute # close query instance print query.close() rescue Exception => e # print exception puts e end # close session session.close rescue Exception => e # print exception puts e end basex-8.5.1/basex-api/src/main/ruby/QueryExample.rb000066400000000000000000000013711274071665100221550ustar00rootroot00000000000000# This example shows how queries can be executed in an iterative manner. # Documentation: http://docs.basex.org/wiki/Clients # # (C) BaseX Team 2005-12, BSD License require './BaseXClient.rb' begin # create session session = BaseXClient::Session.new("localhost", 1984, "admin", "admin") begin # create query instance input = "for $i in 1 to 10 return Text { $i }" query = session.query(input) # loop through all results while query.more do print query.next end # close query instance print query.close() rescue Exception => e # print exception puts e end # close session session.close rescue Exception => e # print exception puts e end basex-8.5.1/basex-api/src/main/scala/000077500000000000000000000000001274071665100173075ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/scala/addexample.scala000066400000000000000000000021551274071665100224230ustar00rootroot00000000000000import java.io._ /** * This example shows how documents can be added to databases. * Documentation: http://docs.basex.org/wiki/Clients * * @author BaseX Team 2005-12, BSD License */ object addexample { /** * Main method. * @param args command-line arguments */ def main(args: Array[String]) { // create session val session = new BaseXClient("localhost", 1984, "admin", "admin") // create empty database session.execute("create db database") println(session.info) // define input stream var bais = new ByteArrayInputStream("Hello World!".getBytes) // add document session.add("world/world.xml", bais) println(session.info) // define input stream bais = new ByteArrayInputStream("Hello Universe!".getBytes) // add document session.add("universe.xml", bais) println(session.info) // run query on database println println(session.execute("xquery collection('database')")) // drop database session.execute("drop db database") // close session session.close } } basex-8.5.1/basex-api/src/main/scala/basexclient.scala000066400000000000000000000157551274071665100226320ustar00rootroot00000000000000import java.io._ import java.net._ import java.security._ import java.util._ /** * Scala client for BaseX. * Works with BaseX 7.x (but not with BaseX 8.0 and later) * Does not support all bindings yet; your extensions are welcome. * * Documentation: http://docs.basex.org/wiki/Clients * * (C) BaseX Team 2005-12, BSD License */ /** * Session constructor. * @param host server name * @param port server port * @param usern user name * @param pw password * @throws IOException I/O exception */ class BaseXClient(host: String, port: Int, usern: String, pw: String) { var inf = "" val socket = new Socket socket.connect(new InetSocketAddress(host, port), 5000) val in = new BufferedInputStream(socket.getInputStream) val out = socket.getOutputStream val ts = receive send(usern) send(md5(md5(pw) + ts)) if(!ok) throw new IOException("Access denied.") /** * Executes a command and serializes the result to an output stream. * @param cmd command * @param os output stream * @throws IOException I/O Exception */ def execute(cmd: String, os: OutputStream) { send(cmd) receive(in, os) inf = receive if(!ok) throw new IOException(inf) } /** * Executes a command and returns the result. * @param cmd command * @return result * @throws IOException I/O Exception */ def execute(cmd: String) : String = { val os = new ByteArrayOutputStream execute(cmd, os) os.toString("UTF-8") } /** * Creates a query object. * @param query query string * @return query * @throws IOException I/O Exception */ def query(query: String) : Query = { new Query(query) } /** * Creates a database. * @param name name of database * @param input xml input * @throws IOException I/O exception */ def create(name: String, input: InputStream) { out.write(8) send(name) send(input) } /** * Adds a database. * @param path source path * @param input xml input * @throws IOException I/O exception */ def add(path: String, input: InputStream) { out.write(9) send(path) send(input) } /** * Replaces a resource. * @param path source path * @param input xml input * @throws IOException I/O exception */ def replace(path: String, input: InputStream) { out.write(12) send(path) send(input) } /** * Stores a binary resource. * @param path source path * @param input binary input * @throws IOException I/O exception */ def store(path: String, input: InputStream) { out.write(13) send(path) send(input) } /** * Returns command information. * @return string info */ def info() : String = { inf } /** * Closes the session. * @throws IOException I/O Exception */ def close() { send("exit") out.flush socket.close } /** * Sends an input stream to the server. * @param input xml input * @throws IOException I/O exception */ private def send(input: InputStream) { val is = new BufferedInputStream(input) val os = new BufferedOutputStream(out) var b = 0 while({ b = is.read; b != -1 }) os.write(b) os.write(0) os.flush inf = receive if(!ok) throw new IOException(inf) } /** * Checks the next success flag. * @return value of check * @throws IOException I/O Exception */ private def ok() : Boolean = { out.flush in.read == 0 } /** * Returns the next received string. * @return String result or info * @throws IOException I/O exception */ private def receive() : String = { val os = new ByteArrayOutputStream receive(in, os) os.toString("UTF-8") } /** * Sends a string to the server. * @param s string to be sent * @throws IOException I/O exception */ private def send(s: String) { out.write((s + '\0').getBytes("UTF8")) } /** * Receives a string and writes it to the specified output stream. * @param bis input stream * @param o output stream * @throws IOException I/O exception */ private def receive(is: InputStream, os: OutputStream) { var b = 0 while({ b = is.read; b != 0 && b != -1 }) os.write(b) } /** * Returns an MD5 hash. * @param pw String * @return String */ private def md5(pw: String) : String = { val sb = new StringBuilder try { val md = MessageDigest.getInstance("MD5") md.update(pw.getBytes) for(b <- md.digest) { val s = Integer.toHexString(b & 0xFF) if(s.length == 1) sb.append('0') sb.append(s) } } catch { case ex: NoSuchAlgorithmException => ex.printStackTrace case ex : Exception => throw ex } sb.toString } /** * Query constructor. * @param query query string * @throws IOException I/O exception */ class Query(query: String) { val id = exec(0, query) /** * Binds a variable. * @param name name of variable * @param value value * @throws IOException I/O exception */ def bind(name: String, value: String) { bind(name, value, "") } /** * Binds a variable with a specific data type. * @param name name of variable * @param value value * @param type data type * @throws IOException I/O exception */ def bind(name: String, value: String, type: String) { exec(3, id + '\0' + name + '\0' + value + '\0') } /** * Binds the context item. * @param value value * @throws IOException I/O exception */ def context(value: String) { context(value, "") } /** * Binds the context item with a specific data type. * @param value value * @param type data type * @throws IOException I/O exception */ def context(value: String, type: String) { exec(14, id + '\0' + value + '\0') } /** * Returns the whole result of the query. * @return query result * @throws IOException I/O Exception */ def execute() : String = { exec(5, id) } /** * Returns query info as a string, regardless of whether an output stream * was specified. * @return query info * @throws IOException I/O exception */ def info() : String = { exec(6, id) } /** * Closes the query. * @return result footer * @throws IOException I/O exception */ def close() { exec(2, id) } /** * Executes the specified command. * @param cmd command * @param arg argument * @return resulting string * @throws IOException I/O exception */ private def exec(cmd: Int, arg: String) : String = { out.write(cmd) send(arg) val s = receive if(!ok) throw new IOException(receive) s } } } basex-8.5.1/basex-api/src/main/scala/createexample.scala000066400000000000000000000014671274071665100231430ustar00rootroot00000000000000import java.io._ /** * This example shows how new databases can be created. * Documentation: http://docs.basex.org/wiki/Clients * * @author BaseX Team 2005-12, BSD License */ object createexample { /** * Main method. * @param args command-line arguments */ def main(args: Array[String]) { // create session val session = new BaseXClient("localhost", 1984, "admin", "admin") // define input stream val bais = new ByteArrayInputStream("Hello World!".getBytes) // create new database session.create("database", bais) println(session.info) // run query on database println(session.execute("xquery doc('database')")) // drop database session.execute("drop db database") // close session session.close } } basex-8.5.1/basex-api/src/main/scala/example.scala000066400000000000000000000016161274071665100217530ustar00rootroot00000000000000import java.io._ /** * This example shows how commands can be executed via the server instance. * * Documentation: http://docs.basex.org/wiki/Clients * * @author BaseX Team 2005-12, BSD License */ object example { /** * Main method. * @param args command-line arguments */ def main(args: Array[String]) { // initialize timer val time = System.nanoTime // create session val session = new BaseXClient("localhost", 1984, "admin", "admin") // version 1: perform command and print returned string println(session.execute("info")) // version 2 (faster): perform command and pass on result to output stream val out = System.out session.execute("xquery 1 to 10", out) // close session session.close // print time needed val ms = (System.nanoTime - time) / 1000000d println("\n\n" + ms + " ms") } } basex-8.5.1/basex-api/src/main/scala/querybindexample.scala000066400000000000000000000015151274071665100236740ustar00rootroot00000000000000import java.io._ /** * This example shows how external variables can be bound to XQuery expressions. * * Documentation: http://docs.basex.org/wiki/Clients * * @author BaseX Team 2005-12, BSD License */ object querybindexample { /** * Main method. * @param args command-line arguments */ def main(args: Array[String]) { // create session val session = new BaseXClient("localhost", 1984, "admin", "admin") // create query instance val input = "declare variable $name external; " + "for $i in 1 to 10 return element { $name } { $i }" val query = session.query(input) // bind variable query.bind("$name", "number"); // print result println(query.execute) // close query instance query.close // close session session.close } } basex-8.5.1/basex-api/src/main/scala/queryexample.scala000066400000000000000000000015001274071665100230310ustar00rootroot00000000000000import java.io._ /** * This example shows how queries can be executed in an iterative manner. * Iterative evaluation will be slower, as more server requests are performed. * * Documentation: http://docs.basex.org/wiki/Clients * * @author BaseX Team 2005-12, BSD License */ object queryexample { /** * Main method. * @param args command-line arguments */ def main(args: Array[String]) { // create session val session = new BaseXClient("localhost", 1984, "admin", "admin") // create query instance val input = "for $i in 1 to 10 return Text { $i }" val query = session.query(input) // loop through all results while(query.more) println(query.next) // close query instance query.close // close session session.close } } basex-8.5.1/basex-api/src/main/vb/000077500000000000000000000000001274071665100166335ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/vb/AddExample.vb000066400000000000000000000023671274071665100212000ustar00rootroot00000000000000' This example shows how new documents can be added. ' ' Documentation: http://docs.basex.org/wiki/Clients ' ' (C) BaseX Team 2005-12, BSD License Imports System Imports System.IO Module CreateExample Sub Main() Try ' create session Dim session As New Session("localhost", 1984, "admin", "admin") ' create empty database session.Execute("create db database") Console.WriteLine(session.Info) ' define InputStream Dim ms As New MemoryStream(System.Text.Encoding.UTF8.GetBytes("Hello World!")) ' add document session.Add("world/World.xml", ms) Console.WriteLine(session.Info) ' define InputStream Dim ms As New MemoryStream(System.Text.Encoding.UTF8.GetBytes("Hello Universe!")) ' add document session.Add("Universe.xml", ms) Console.WriteLine(session.Info) ' run query on database Console.WriteLine(session.Execute("xquery /")) ' drop database session.Execute("drop db database") ' close session session.Close() Catch e As IOException ' print exception Console.WriteLine(e.Message) End Try End Sub End Modulebasex-8.5.1/basex-api/src/main/vb/BaseXClient.vb000066400000000000000000000137451274071665100213370ustar00rootroot00000000000000' Visual Basic client for BaseX. ' Works with BaseX 7.x (but not with BaseX 8.0 and later) ' Does not support all bindings yet; your extensions are welcome. ' ' Documentation: http://docs.basex.org/wiki/Clients ' ' (C) BaseX Team 2005-12, BSD License Imports System Imports System.Net.Sockets Imports System.Security.Cryptography Imports System.Text Imports System.Collections.Generic Imports System.IO Module BaseXClient Class Session Private cache As Byte() = New Byte(4096) {} Public stream As NetworkStream Private socket As TcpClient Private m_info As String = "" Private bpos As Integer Private bsize As Integer Public Sub New(host As String, port As Integer, username As String, pw As String) socket = New TcpClient(host, port) stream = socket.GetStream() Dim ts As String = Receive() Send(username) Dim tmp As String = MD5StringHash(pw) Send(MD5StringHash(tmp & ts)) If stream.ReadByte() <> 0 Then Throw New IOException("Access denied.") End If End Sub Public Sub Execute(com As String, ms As Stream) Send(com) Init() Receive(ms) m_info = Receive() If Not Ok() Then Throw New IOException(m_info) End If End Sub Public Function Execute(com As String) As [String] Dim ms As New MemoryStream() Execute(com, ms) Return System.Text.Encoding.UTF8.GetString(ms.ToArray()) End Function Public Sub Create(name As String, ms As Stream) stream.WriteByte(8) Send(name) While True: Dim t As Integer = ms.ReadByte() If t = -1 Then Exit While End If stream.WriteByte(Convert.ToByte(t)) End While stream.WriteByte(0) m_info = Receive() If Not Ok() Then Throw New IOException(m_info) End If End Sub Public Sub Create(name As String, target As String, ms As Stream) stream.WriteByte(9) Send(name) Send(target) While True: Dim t As Integer = ms.ReadByte() If t = -1 Then Exit While End If stream.WriteByte(Convert.ToByte(t)) End While stream.WriteByte(0) m_info = Receive() If Not Ok() Then Throw New IOException(m_info) End If End Sub Public Function Query(q As String) As Query Return New Query(Me, q) End Function Public ReadOnly Property Info() As String Get Return m_info End Get End Property Public Sub Close() Send("exit") socket.Close() End Sub ' Initializes the byte transfer. Private Sub Init() bpos = 0 bsize = 0 End Sub ' Returns a single byte from the socket. Private Function Read() As Byte If bpos = bsize Then bsize = stream.Read(cache, 0, 4096) bpos = 0 End If Dim b as Byte = cache(bpos) bpos += 1 Return b End Function ' Receives a string from the socket. Private Sub Receive(ms As Stream) While True Dim b As Byte = Read() If b = 0 Then Exit While End If ms.WriteByte(b) End While End Sub ' Receives a string from the socket. Public Function Receive() As String Dim ms As New MemoryStream() Receive(ms) Return System.Text.Encoding.UTF8.GetString(ms.ToArray()) End Function ' Sends strings to server. Public Sub Send(message As String) Dim msg As Byte() = System.Text.Encoding.UTF8.GetBytes(message) stream.Write(msg, 0, msg.Length) stream.WriteByte(0) End Sub ' Returns success check. Public Function Ok() As Boolean Return Read() = 0 End Function ' Returns the md5 hash of a string. Private Function MD5StringHash(ByVal strString As String) As String Dim MD5 As New MD5CryptoServiceProvider Dim Data As Byte() Dim Result As Byte() Dim Res As String = "" Dim Tmp As String = "" Data = Encoding.ASCII.GetBytes(strString) Result = MD5.ComputeHash(Data) For i As Integer = 0 To Result.Length - 1 Tmp = Hex(Result(i)) If Len(Tmp) = 1 Then Tmp = "0" & Tmp Res += Tmp Next Return Res.ToLower End Function End Class Class Query Private session As Session Private id As String Public Sub New(s As Session, query As String) session = s id = Exec(0, query) End Sub Public Sub Bind(name As String, value As String) Bind(name, value, "") End Sub Public Sub Bind(name As String, value As String, type As String) session.stream.WriteByte(3) session.Send(id) session.Send(name) session.Send(value) session.Send(type) Dim Res As String = session.Receive() If Not session.Ok() Then Throw New IOException(session.Receive()) End If End Sub Public Sub Context(value As String) Context(value, "") End Sub Public Sub Context(value As String, type As String) session.stream.WriteByte(14) session.Send(id) session.Send(value) session.Send(type) Dim Res As String = session.Receive() If Not session.Ok() Then Throw New IOException(session.Receive()) End If End Sub Public Function Execute() As String Return Exec(5, id) End Function Public Function Info() As String Return Exec(6, id) End Function Public Function Close() Exec(2, id) End Function Public Function Exec(cmd As Integer, arg As String) As String session.stream.WriteByte(cmd) session.Send(arg) Dim Res As String = session.Receive() If Not session.Ok() Then Throw New IOException(session.Receive()) End If Return Res End Function End Class End Module basex-8.5.1/basex-api/src/main/vb/CreateExample.vb000066400000000000000000000015731274071665100217110ustar00rootroot00000000000000' This example shows how new databases can be created. ' ' Documentation: http://docs.basex.org/wiki/Clients ' ' (C) BaseX Team 2005-12, BSD License Imports System Imports System.IO Module CreateExample Sub Main() Try ' create session Dim session As New Session("localhost", 1984, "admin", "admin") ' define InputStream Dim ms As New MemoryStream(System.Text.Encoding.UTF8.GetBytes("Hello World!")) ' create database session.Create("database", ms) Console.WriteLine(session.Info) ' run query on database Console.WriteLine(session.Execute("xquery /")) ' drop database session.Execute("drop db database") ' close session session.Close() Catch e As IOException ' print exception Console.WriteLine(e.Message) End Try End Sub End Modulebasex-8.5.1/basex-api/src/main/vb/Example.vb000066400000000000000000000020351274071665100205570ustar00rootroot00000000000000' This example shows how database commands can be executed. ' ' Documentation: http://docs.basex.org/wiki/Clients ' ' (C) BaseX Team 2005-12, BSD License Imports System Imports System.Diagnostics Imports System.IO Module Example Sub Main() Try ' initialize timer Dim watch As New Stopwatch() watch.Start() ' create session Dim session As New Session("localhost", 1984, "admin", "admin") ' version 1: perform command and print returned string Console.WriteLine(session.Execute("info")) ' version 2 (faster): perform command and pass on result to output stream Dim stream As Stream = Console.OpenStandardOutput() session.Execute("xquery 1 to 10", stream) ' close session session.Close() ' print time needed Console.WriteLine(vbLf & watch.ElapsedMilliseconds & " ms.") Catch e As IOException ' print exception Console.WriteLine(e.Message) Console.ReadLine() End Try End Sub End Modulebasex-8.5.1/basex-api/src/main/vb/QueryBindExample.vb000066400000000000000000000024241274071665100224040ustar00rootroot00000000000000' This example shows how external variables can be bound to XQuery expressions. ' ' Documentation: http://docs.basex.org/wiki/Clients ' ' (C) BaseX Team 2005-12, BSD License Imports System Imports System.Diagnostics Imports System.IO Module QueryExample Sub Main() Try ' initialize timer Dim watch As New Stopwatch() watch.Start() ' create session Dim session As New Session("localhost", 1984, "admin", "admin") Try ' create query instance Dim input As String = "declare variable $name external; for $i in 1 to 10 return element { $name } { $i }" Dim query As Query = session.Query(input) ' bind variable query.Bind("$name", "number") ' loop through all results Console.WriteLine(query.Execute()) ' close query instance query.Close() Catch e As IOException ' print exception Console.WriteLine(e.Message) End Try ' close session session.Close() ' print time needed Console.WriteLine(vbLf & watch.ElapsedMilliseconds & " ms.") Console.ReadLine() Catch e As IOException ' print exception Console.WriteLine(e.Message) End Try End Sub End Module basex-8.5.1/basex-api/src/main/vb/QueryExample.vb000066400000000000000000000024551274071665100216130ustar00rootroot00000000000000' This example shows how queries can be executed in an iterative manner. ' Iterative evaluation will be slower, as more server requests are performed. ' ' Documentation: http://docs.basex.org/wiki/Clients ' ' (C) BaseX Team 2005-12, BSD License Imports System Imports System.Diagnostics Imports System.IO Module QueryExample Sub Main() Try ' initialize timer Dim watch As New Stopwatch() watch.Start() ' create session Dim session As New Session("localhost", 1984, "admin", "admin") Try ' create query instance Dim input As String = "for $i in 1 to 10 return Text { $i }" Dim query As Query = session.Query(input) ' loop through all results While query.More() Console.WriteLine(query.Nexty()) End While ' close query instance query.Close() Catch e As IOException ' print exception Console.WriteLine(e.Message) End Try ' close session session.Close() ' print time needed Console.WriteLine(vbLf & watch.ElapsedMilliseconds & " ms.") Console.ReadLine() Catch e As IOException ' print exception Console.WriteLine(e.Message) End Try End Sub End Module basex-8.5.1/basex-api/src/main/webapp/000077500000000000000000000000001274071665100175025ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/webapp/WEB-INF/000077500000000000000000000000001274071665100205315ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/webapp/WEB-INF/data/000077500000000000000000000000001274071665100214425ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/webapp/WEB-INF/data/.empty000066400000000000000000000000001274071665100225670ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/webapp/WEB-INF/jetty.xml000066400000000000000000000027241274071665100224170ustar00rootroot00000000000000 0.0.0.0 8984 60000 true 2 basex-8.5.1/basex-api/src/main/webapp/WEB-INF/repo/000077500000000000000000000000001274071665100214765ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/webapp/WEB-INF/repo/.empty000066400000000000000000000000001274071665100226230ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/webapp/WEB-INF/web.xml000066400000000000000000000074471274071665100220440ustar00rootroot00000000000000 BaseX: The XML Database and XQuery Processor HTTP Services org.basex.http.SessionListener org.basex.http.ServletListener RESTXQ org.basex.http.restxq.RestXqServlet 1 RESTXQ /* REST org.basex.http.rest.RESTServlet REST /rest/* WebDAV org.basex.http.webdav.WebDAVServlet WebDAV /webdav/* default useFileMappedBuffer false default /static/* basex-8.5.1/basex-api/src/main/webapp/dba/000077500000000000000000000000001274071665100202305ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/webapp/dba/Dockerfile000066400000000000000000000014041274071665100222210ustar00rootroot00000000000000# This Dockerfile provides an image for the BaseX DBA application. # At the same time, it demonstrates general useage of the basexhttp # Docker image for RestXQ applications. # For production applications, better choose a fixed version tag FROM basex/basexhttp:latest MAINTAINER BaseX Team # If you need to install additional Java dependencies, switch to root # USER root # RUN apt-get update && \ # apt-get install some-debian-package # Don't forget to switch back to the BaseX user! # USER basex # Setting environment variables like CLASSPATH and JVM options # to increase BaseX' memory limit # ENV BASEX_JVM="-Xmx2048m" # Add source code COPY . /srv/BaseXWeb # RUN is inherited from basex/basexhttp and not required again basex-8.5.1/basex-api/src/main/webapp/dba/common.xqm000066400000000000000000000026551274071665100222570ustar00rootroot00000000000000(:~ : Common RESTXQ access points. : : @author Christian Grün, BaseX Team, 2014-16 :) module namespace _ = 'dba/common'; import module namespace Request = 'http://exquery.org/ns/request'; import module namespace cons = 'dba/cons' at 'modules/cons.xqm'; import module namespace tmpl = 'dba/tmpl' at 'modules/tmpl.xqm'; (:~ : Redirects to the start page. :) declare %rest:path("/dba") function _:redirect( ) { web:redirect("/dba/databases") }; (:~ : Returns a file. : @param $file file or unknown path : @return rest response and binary file :) declare %rest:path("/dba/static/{$file=.+}") function _:file( $file as xs:string ) as item()+ { let $path := file:base-dir() || 'static/' || $file return ( web:response-header(map { 'media-type': web:content-type($path) }), file:read-binary($path) ) }; (:~ : Shows a page not found error. : @param $unknown unknown page : @return page :) declare %rest:path("/dba/{$unknown}") %output:method("html") function _:any( $unknown as xs:string ) as element(html) { cons:check(), tmpl:wrap(

Page not found!

  • Page: dba/{ $unknown }
  • Method: { Request:method() }
) }; (:~ : Login error: redirects to the login page. :) declare %rest:error("basex:login") function _:error-login( ) { web:redirect("login") }; basex-8.5.1/basex-api/src/main/webapp/dba/databases/000077500000000000000000000000001274071665100221575ustar00rootroot00000000000000basex-8.5.1/basex-api/src/main/webapp/dba/databases/alter-db.xqm000066400000000000000000000050611274071665100244020ustar00rootroot00000000000000(:~ : Rename database. : : @author Christian Grün, BaseX Team, 2014-16 :) module namespace _ = 'dba/databases'; import module namespace cons = 'dba/cons' at '../modules/cons.xqm'; import module namespace html = 'dba/html' at '../modules/html.xqm'; import module namespace tmpl = 'dba/tmpl' at '../modules/tmpl.xqm'; import module namespace util = 'dba/util' at '../modules/util.xqm'; (:~ Top category :) declare variable $_:CAT := 'databases'; (:~ Sub category :) declare variable $_:SUB := 'database'; (:~ : Form for renaming a database. : @param $name name of database : @param $newname new name : @param $error error string : @return page :) declare %rest:GET %rest:path("/dba/alter-db") %rest:query-param("name", "{$name}") %rest:query-param("newname", "{$newname}") %rest:query-param("error", "{$error}") %output:method("html") function _:alter( $name as xs:string, $newname as xs:string?, $error as xs:string? ) as element(html) { cons:check(), tmpl:wrap(map { 'top': $_:SUB, 'error': $error },

Databases » { html:link($name, $_:SUB, map { 'name': $name } ) } » { html:button('alter', 'Rename') }

Name: { html:focus('newname') }
) }; (:~ : Renames a database. : @param $name name of database : @param $newname new name :) declare %updating %rest:POST %rest:path("/dba/alter-db") %rest:query-param("name", "{$name}") %rest:query-param("newname", "{$newname}") function _:alter( $name as xs:string, $newname as xs:string ) { cons:check(), try { util:update("if(db:exists($newname)) then ( error((), 'Database already exists: ' || $newname || '.') ) else ( db:alter($name, $newname) )", map { 'name': $name, 'newname': $newname }), db:output(web:redirect($_:SUB, map { 'info': 'Database was renamed.', 'name': $newname })) } catch * { db:output(web:redirect("alter-db", map { 'error': $err:description, 'name': $name, 'newname': $newname })) } }; basex-8.5.1/basex-api/src/main/webapp/dba/databases/backups.xqm000066400000000000000000000041141274071665100243360ustar00rootroot00000000000000(:~ : Backup operations. : : @author Christian Grün, BaseX Team, 2014-16 :) module namespace _ = 'dba/databases'; import module namespace cons = 'dba/cons' at '../modules/cons.xqm'; import module namespace util = 'dba/util' at '../modules/util.xqm'; (:~ Sub category :) declare variable $_:SUB := 'database'; (:~ : Creates a database backup. : @param $name name of database :) declare %updating %rest:GET %rest:path("/dba/create-backup") %rest:query-param("name", "{$name}") function _:create-backup( $name as xs:string ) { _:action($name, 'Backup was created.', "db:create-backup($n)", map { 'n': $name }) }; (:~ : Drops a database backup. : @param $name name of database : @param $backups backup files :) declare %updating %rest:GET %rest:path("/dba/drop-backup") %rest:query-param("name", "{$name}") %rest:query-param("backup", "{$backups}") function _:drop-backup( $name as xs:string, $backups as xs:string* ) { let $n := count($backups) let $info := if($n = 1) then 'Backup was dropped.' else $n || ' backups were dropped.' return _:action($name, $info, "$b ! db:drop-backup(.)", map { 'b': $backups }) }; (:~ : Restores a database backup. : @param $name database : @param $backup backup file :) declare %updating %rest:GET %rest:path("/dba/restore") %rest:query-param("name", "{$name}") %rest:query-param("backup", "{$backup}") function _:restore( $name as xs:string, $backup as xs:string ) { _:action($name, 'Database was restored.', "db:restore($b)", map { 'b': $backup }) }; (:~ : Performs a backup operation. : @param $name database : @param $info info string : @param $query query to execute : @param $args query arguments :) declare %updating function _:action( $name as xs:string, $info as xs:string, $query as xs:string, $args as map(*) ) { cons:check(), try { util:update($query, $args), db:output(web:redirect($_:SUB, map { 'name': $name, 'info': $info })) } catch * { db:output(web:redirect($_:SUB, map { 'name': $name, 'error': $err:description })) } }; basex-8.5.1/basex-api/src/main/webapp/dba/databases/copy.xqm000066400000000000000000000046171274071665100236700ustar00rootroot00000000000000(:~ : Copy database. : : @author Christian Grün, BaseX Team, 2014-16 :) module namespace _ = 'dba/databases'; import module namespace cons = 'dba/cons' at '../modules/cons.xqm'; import module namespace html = 'dba/html' at '../modules/html.xqm'; import module namespace tmpl = 'dba/tmpl' at '../modules/tmpl.xqm'; import module namespace util = 'dba/util' at '../modules/util.xqm'; (:~ Top category :) declare variable $_:CAT := 'databases'; (:~ Sub category :) declare variable $_:SUB := 'database'; (:~ : Form for copying a database. : @param $name name of database : @param $newname new name : @param $error error string : @return page :) declare %rest:GET %rest:path("/dba/copy") %rest:query-param("name", "{$name}") %rest:query-param("newname", "{$newname}") %rest:query-param("error", "{$error}") %output:method("html") function _:copy( $name as xs:string, $newname as xs:string?, $error as xs:string? ) as element(html) { cons:check(), tmpl:wrap(map { 'top': $_:SUB, 'error': $error },

Databases » { html:link($name, $_:SUB, map { 'name': $name } ) } » { html:button('copy', 'Copy') }

New name: { html:focus('newname') }
) }; (:~ : Copies a database. : @param $name name of database : @param $newname new name :) declare %updating %rest:POST %rest:path("/dba/copy") %rest:query-param("name", "{$name}") %rest:query-param("newname", "{$newname}") function _:copy( $name as xs:string, $newname as xs:string ) { cons:check(), try { util:update("db:copy($n, $m)", map { 'n': $name, 'm': $newname }), db:output(web:redirect($_:SUB, map { 'info': 'Database was copied.', 'name': $newname })) } catch * { db:output(web:redirect("copy", map { 'error': $err:description, 'name': $name, 'newname': $newname })) } }; basex-8.5.1/basex-api/src/main/webapp/dba/databases/create-db.xqm000066400000000000000000000076721274071665100245500ustar00rootroot00000000000000(:~ : Create new database. : : @author Christian Grün, BaseX Team, 2014-16 :) module namespace _ = 'dba/databases'; import module namespace cons = 'dba/cons' at '../modules/cons.xqm'; import module namespace html = 'dba/html' at '../modules/html.xqm'; import module namespace tmpl = 'dba/tmpl' at '../modules/tmpl.xqm'; import module namespace util = 'dba/util' at '../modules/util.xqm'; (:~ Top category :) declare variable $_:CAT := 'databases'; (:~ Sub category :) declare variable $_:SUB := 'database'; (:~ : Form for creating a new database. : @param $name entered name : @param $opts chosen database options : @param $lang entered language : @param $error error string : @return page :) declare %rest:GET %rest:path("/dba/create-db") %rest:query-param("name", "{$name}") %rest:query-param("opts", "{$opts}") %rest:query-param("lang", "{$lang}", "en") %rest:query-param("error", "{$error}") %output:method("html") function _:create( $name as xs:string?, $opts as xs:string*, $lang as xs:string?, $error as xs:string? ) as element(html) { cons:check(), let $opts := if($opts = 'x') then $opts else ('textindex', 'attrindex') return tmpl:wrap(map { 'top': $_:CAT, 'error': $error },

Databases » { html:button('create', 'Create') }

Name: { html:focus('name') }
{

{ html:option('textindex', 'Text Index', $opts) }

,

{ html:option('attrindex', 'Attribute Index', $opts) }

,

{ html:option('tokenindex', 'Token Index', $opts) }

, html:option('updindex', 'Incremental Indexing', $opts),
,

{ html:option('ftindex', 'Fulltext Indexing', $opts) }

}
{ html:option('stemming', 'Stemming', $opts), html:option('casesens', 'Case Sensitivity', $opts), html:option('diacritics', 'Diacritics', $opts) }
Language:
) }; (:~ : Creates a database. : @param $name database : @param $opts database options : @param $lang language :) declare %updating %rest:POST %rest:path("/dba/create-db") %rest:query-param("name", "{$name}") %rest:query-param("opts", "{$opts}") %rest:query-param("lang", "{$lang}") function _:create( $name as xs:string, $opts as xs:string*, $lang as xs:string? ) { cons:check(), try { util:update("if(db:exists($name)) then ( error((), 'Database already exists: ' || $name || '.') ) else ( db:create($name, (), (), map:merge(( (('textindex','attrindex','tokenindex','ftindex','stemming','casesens','diacritics','updindex') ! map:entry(., $opts = .)), $lang ! map:entry('language', .)) )) )", map { 'name': $name, 'lang': $lang, 'opts': $opts }), db:output(web:redirect($_:SUB, map { 'info': 'Created Database: ' || $name, 'name': $name })) } catch * { db:output(web:redirect("create-db", map { 'error': $err:description, 'name': $name, 'opts': $opts, 'lang': $lang })) } }; basex-8.5.1/basex-api/src/main/webapp/dba/databases/database.xqm000066400000000000000000000141431274071665100244550ustar00rootroot00000000000000(:~ : Database main page. : : @author Christian Grün, BaseX Team, 2014-16 :) module namespace _ = 'dba/databases'; import module namespace cons = 'dba/cons' at '../modules/cons.xqm'; import module namespace html = 'dba/html' at '../modules/html.xqm'; import module namespace tmpl = 'dba/tmpl' at '../modules/tmpl.xqm'; import module namespace util = 'dba/util' at '../modules/util.xqm'; (:~ Top category :) declare variable $_:CAT := 'databases'; (:~ Sub category :) declare variable $_:SUB := 'database'; (:~ : Manages a single database. : @param $name database : @param $resource resource : @param $error error string : @param $info info string : @return page :) declare %rest:GET %rest:path("/dba/database") %rest:query-param("name", "{$name}") %rest:query-param("resource", "{$resource}") %rest:query-param("error", "{$error}") %rest:query-param("info", "{$info}") %output:method("html") function _:database( $name as xs:string, $resource as xs:string?, $error as xs:string?, $info as xs:string? ) as element(html) { cons:check(), (: request data in a single step :) let $data := try { util:eval('element result { let $found := db:exists($name) return ( element found { $found }, if($found) then ( element databases { db:list-details($name)[position() = 1 to $max] }, element info { db:info($name) } ) else (), element backups { db:backups($name) } ) }', map { 'name': $name, 'max': $cons:OPTION($cons:K-MAX-ROWS) + 1 }) } catch * { element error { $cons:DATA-ERROR || ': ' || $err:description } } let $found := $data/found = 'true' let $error := ($data/self::error/string(), $error)[1] return tmpl:wrap( map { 'top': $_:CAT, 'info': $info, 'error': $error, 'css': 'codemirror/lib/codemirror.css', 'scripts': ('codemirror/lib/codemirror.js', 'codemirror/mode/xml/xml.js') },

Databases » { $name ! (if(empty($resource)) then . else html:link(., $_:SUB, map { 'name': $name } )) }

{ if(not($found)) then () else ( let $entries := $data/databases/* ! let $headers := ( { html:label($entries, ('Resource', 'Resources')) }, Content type, Raw, Size (factor) ) let $buttons := ( html:button('add', 'Add…'), html:button('delete', 'Delete', true()), html:button('copy', 'Copy…', false()), html:button('alter-db', 'Rename…', false()), html:button('optimize', 'Optimize…', false(), 'global') ) let $map := map { 'name': $name } let $link := function($value) { $_:SUB } return html:table($entries, $headers, $buttons, $map, (), $link) ) }

Backups

{ let $entries := for $backup in $data/backups/* order by $backup descending return let $headers := ( { html:label($entries, ('Backup', 'Backups')) }, Size ) let $buttons := ( html:button('create-backup', 'Create', false(), 'global') update ( if($found) then () else insert node attribute disabled { '' } into . ), html:button('restore', 'Restore', true()), html:button('drop-backup', 'Drop', true()) ) let $map := map { 'name': $name } let $link := function($value) { 'backup/' || $value || '.zip' } return html:table($entries, $headers, $buttons, $map, (), $link) } { if($resource) then <_>

{ $resource }

{ html:button('rename', 'Rename…') } { html:button('download', 'Download') } { html:button('replace', 'Replace…') }
XQuery: { html:focus('input') }