commit 1c81d899acb3148e5365f6112ea328e36b58d2df
parent 5eb41df7cab498ca1373696c0f9f893e55cc291e
Author: dankert <devnull@localhost>
Date: Mon, 14 Sep 2009 23:36:20 +0200
Client-Bibliotheken für PHP und Java.
Diffstat:
3 files changed, 707 insertions(+), 0 deletions(-)
diff --git a/client/java/de/openrat/client/CMSRequest.java b/client/java/de/openrat/client/CMSRequest.java
@@ -0,0 +1,387 @@
+/*
+OpenRat Java-Client
+Copyright (C) 2009 Jan Dankert
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; if not, write to the
+Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+Boston, MA 02110-1301, USA.
+
+ */
+package de.openrat.client;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.UnsupportedEncodingException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * API-Request to the OpenRat Content Management System. <br>
+ * <br>
+ * The call to the CMS server is done via a (non-SSL) HTTP connection.<br>
+ * <br>
+ * Before a call you are able to set some key/value-pairs as parameters. After calling the CMS a
+ * DOM-document is returned, which contains the server response.<br>
+ * Example <br>
+ *
+ * <pre>
+ * CMSRequest request = new CMSRequest("your.openrat.example.com");
+ * //prints tracing information to stdout.
+ * request.trace = true;
+ * try {
+ * request.parameter.put("action", "index");
+ * request.parameter.put("subaction", "showlogin"); // login page
+ * request.parameter.put("...", "...");
+ * Document response = request.call();
+ * // now traverse through the dom tree and get your information.
+ * }
+ * catch (IOException e) {
+ * // your error handling.
+ * }
+ * </pre>
+ *
+ * @author Jan Dankert
+ */
+public class CMSRequest {
+
+ // some constants...
+ private static final String CHARSET_UTF8 = "UTF-8";
+ private static final String HTTP_GET = "GET";
+ private static final String HTTP_POST = "POST";
+
+ /**
+ * if <code>true</code>, Tracing-Output will be logged to stdout. Default: <code>false</code>.
+ */
+ // this is public, for easier use.
+ public boolean trace = false;
+
+ /**
+ * HTTP-method, must be "GET" or "POST", default: "GET".
+ */
+ private String method = HTTP_GET;
+
+ /**
+ * Parameter map.
+ */
+ private Map<String, String> parameter = new HashMap<String, String>();
+
+ private String serverPath;
+ private String serverHost;
+ private int serverPort;
+
+ private String proxyHostname;
+ private int proxyPort;
+ private SocketAddress socketAddress;
+
+ private String cookieName;
+ private String cookieValue;
+
+ /**
+ * Setting a HTTP-Cookie.
+ *
+ * @param name name
+ * @param value value
+ */
+ public void setCookie(String name, String value) {
+
+ this.cookieName = this.urlEncode(name);
+ this.cookieValue = this.urlEncode(value);
+ }
+
+ /**
+ * URL-Encoder.
+ *
+ * @param value
+ * @return url-encoded value
+ */
+ private String urlEncode(String value) {
+
+ try {
+ return URLEncoder.encode(value, CHARSET_UTF8);
+ }
+ catch (UnsupportedEncodingException e) {
+ // maybe... this would be strange
+ throw new IllegalStateException(CHARSET_UTF8 + " ist not supported by this VM");
+ }
+ }
+
+ /**
+ * Setting a HTTP-Proxy.
+ *
+ * @param host hostname
+ * @param port port
+ */
+ public void setProxy(String host, int port) {
+
+ this.proxyHostname = host;
+ this.proxyPort = port;
+ }
+
+ /**
+ * Set the HTTP Method. Default is "GET".
+ *
+ * @param method HTTP-method
+ */
+ public void setMethod(String method) {
+
+ if (!HTTP_GET.equalsIgnoreCase(method) && !HTTP_POST.equalsIgnoreCase(method))
+ throw new IllegalArgumentException("Method must be '" + HTTP_POST + "' or '" + HTTP_GET
+ + "'.");
+
+ this.method = method.toUpperCase();
+ }
+
+ /**
+ * Clear parameter values.
+ */
+ public void clearParameters() {
+
+ parameter.clear();
+ }
+
+ /**
+ * Setting a parameter value. <strong>DO NOT url-encode your values</strong> as this is done
+ * automatically inside this method!
+ *
+ * @param paramName name
+ * @param paramValue value
+ */
+ public void setParameter(String paramName, String paramValue) {
+
+ if (paramName == null || paramValue == null || "" == paramName)
+ throw new IllegalArgumentException("parameter name and value must have values");
+
+ parameter.put(paramName, paramValue);
+ }
+
+ /**
+ * Constructs a CMS-Request to the specified server.<br>
+ * Server-Path is "/", Server-Port is 80.
+ *
+ * @param host hostname
+ */
+ public CMSRequest(String host) {
+
+ super();
+ this.serverHost = host;
+ this.serverPath = "/";
+ this.serverPort = 80;
+ }
+
+ /**
+ * Constructs a CMS-Request to the specified server/path.<br>
+ * Server-Port is 80.
+ *
+ * @param host hostname
+ * @param path path
+ */
+ public CMSRequest(String host, String path) {
+
+ super();
+ this.serverHost = host;
+ this.serverPath = path;
+ this.serverPort = 80;
+ }
+
+ /**
+ * Constructs a CMS-Request to the specified server/path/port.
+ *
+ * @param host hostname
+ * @param path path
+ * @param port port-number
+ */
+ public CMSRequest(String host, String path, int port) {
+
+ super();
+ this.serverHost = host;
+ this.serverPath = path;
+ this.serverPort = port;
+ }
+
+ /**
+ * Sends a request to the openrat-server and parses the response into a DOM tree document.
+ *
+ * @return server response as a DOM tree
+ * @throws IOException if server is unrechable or responds non-wellformed XML
+ */
+ public Document performRequest() throws IOException {
+
+ final Socket socket = new Socket();
+
+ try {
+
+ final boolean useProxy = this.proxyHostname != null;
+ final boolean useCookie = this.cookieName != null;
+
+ if (serverPath == null)
+ this.serverPath = "/";
+ if (!serverPath.startsWith("/"))
+ this.serverPath = "/" + this.serverPath;
+
+ // When a client uses a proxy, it typically sends all requests to that proxy, instead
+ // of to the servers in the URLs. Requests to a proxy differ from normal requests in one
+ // way: in the first line, they use the complete URL of the resource being requested,
+ // instead of just the path.
+ if (useProxy) {
+ socketAddress = new InetSocketAddress(this.proxyHostname, this.proxyPort);
+ } else {
+ socketAddress = new InetSocketAddress(this.serverHost, serverPort);
+ }
+
+ socket.setKeepAlive(false);
+ socket.setReuseAddress(false);
+ socket.connect(socketAddress, 5000);
+
+ final StringBuffer header = new StringBuffer();
+
+ final StringBuffer parameterList = new StringBuffer();
+
+ for (Entry<String, String> entry : this.parameter.entrySet()) {
+ if (parameterList.length() > 0)
+ parameterList.append("&");
+ parameterList.append(this.urlEncode(entry.getKey()));
+ parameterList.append("=");
+ parameterList.append(this.urlEncode(entry.getValue()));
+ }
+
+ String httpUrl = this.serverPath;
+
+ if (useProxy)
+ // See RFC 2616 Section 5.1.2 "Request-URI"
+ // "The absolute URI form is REQUIRED when the request is being made to a proxy"
+ httpUrl = "http://" + this.serverHost + httpUrl;
+
+ if (HTTP_GET.equals(this.method))
+ httpUrl = httpUrl + "?" + parameterList;
+
+ // using HTTP/1.0 as this is supported by all HTTP-servers and proxys.
+ // We have no need for HTTP/1.1 at the moment.
+ header.append(this.method + " " + httpUrl + " HTTP/1.0\n");
+
+ // Setting the HTTP Header
+ header.append("Host: " + this.serverHost + "\n");
+ header.append("User-Agent: Mozilla/5.0; compatible (OpenRat java-client)\n");
+ header.append("Accept: application/xml\n");
+ header.append("Accept-Language: " + Locale.getDefault().getLanguage() + "\n");
+ header.append("Accept-Charset: utf-8\n");
+ header.append("Connection: close\n");
+ if (useCookie)
+ header.append("Cookie: " + cookieName + "=" + cookieValue + "\n");
+
+ if (HTTP_POST.equals(this.method)) {
+ header.append("Content-Type: application/x-www-form-urlencoded" + "\n");
+ header.append("Content-Length: " + parameterList.length() + "\n");
+ }
+
+ header.append("\n");
+
+ if (HTTP_POST.equals(this.method))
+ header.append(parameterList);
+
+ if (this.trace)
+ System.out.println("--- request ---");
+ if (this.trace)
+ System.out.println(header.toString());
+
+ final PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true);
+ printWriter.write(header.toString());
+ printWriter.flush();
+
+ final InputStream inputStream = socket.getInputStream();
+ final int available = inputStream.available();
+
+ final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket
+ .getInputStream()));
+
+ final String httpResponse = bufferedReader.readLine().trim();
+ final String httpRetCode = httpResponse.substring(9, 12);
+
+ if (this.trace)
+ System.out.println("--- response ---");
+ if (this.trace)
+ System.out.println(httpResponse);
+
+ // Check if we got the status 200=OK.
+ if (!httpRetCode.equals("200")) {
+
+ // non-200-status seems to be an error.
+ throw new IOException("No HTTP 200: Status=" + httpRetCode + " (" + httpResponse
+ + ")");
+ }
+
+ while (true) {
+
+ String responseHeader = bufferedReader.readLine().trim();
+
+ if (responseHeader.equals(""))
+ break;
+
+ if (this.trace)
+ System.out.println(responseHeader);
+ }
+
+ StringBuffer response = new StringBuffer();
+ while (bufferedReader.ready()) {
+
+ response.append(bufferedReader.readLine() + "\n");
+ }
+ socket.close();
+
+ if (this.trace)
+ System.out.println("--- response body ---");
+ if (this.trace)
+ System.out.println(response + "\n\n\n");
+
+ final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ final DocumentBuilder builder = factory.newDocumentBuilder();
+ final Document document = builder.parse(new InputSource(new StringReader(response
+ .toString())));
+
+ return document;
+
+ }
+ catch (ParserConfigurationException e) {
+ throw new IOException("XML-Parser-Configuration invalid" + e.getMessage());
+ }
+ catch (SAXException e) {
+ throw new IOException("Server did not return a valid XML-document" + e.getMessage());
+ }
+ finally {
+ try {
+ socket.close(); // Clean up the socket.
+ }
+ catch (IOException e) {
+ // We have done our very best.
+ }
+ }
+ }
+}
diff --git a/client/java/de/openrat/client/test/CMSRequestTest.java b/client/java/de/openrat/client/test/CMSRequestTest.java
@@ -0,0 +1,173 @@
+package de.openrat.client.test;
+
+import java.io.IOException;
+
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import junit.framework.TestCase;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
+
+import de.openrat.client.CMSRequest;
+
+/**
+ * @author Jan Dankert
+ */
+public class CMSRequestTest extends TestCase {
+
+ /**
+ */
+ public void testRequest() {
+
+ // Call the DEMO-Server of OpenRat.
+ CMSRequest request = new CMSRequest("demo.openrat.de", "", 80);
+
+ // prints tracing information to stdout.
+ request.trace = true;
+
+ // setting a HTTP proxy
+ request.setProxy("proxy.somewhere.example", 8080);
+
+ // Now we do some example requests to the openrat server.
+ try {
+ // requesting the login page
+ request.setParameter("action", "index");
+ request.setParameter("subaction", "showlogin");
+ Document document = request.performRequest();
+
+ // Evaluating the session id.
+ String sessionName = this.getText(document, "/server/session/name");
+ String sessionId = this.getText(document, "/server/session/id");
+ request.setCookie(sessionName, sessionId);
+
+ // lets try login with a wrong password
+ request.clearParameters();
+ request.setMethod("POST");
+ request.setParameter("action", "index");
+ request.setParameter("subaction", "login");
+ request.setParameter("login_name", "admin");
+ request.setParameter("login_password", "wrongpassword"); // forcing an login error
+ request.setParameter("dbid", "db1");
+
+ document = request.performRequest(); // will answer with an error element, see stdout.
+ // we are NOT logged in now.
+
+ // requesting a page which is only available for authenticated users
+ request.setMethod("get");
+ request.clearParameters();
+ request.setParameter("action", "index");
+ request.setParameter("subaction", "projectmenu");
+
+ try {
+ document = request.performRequest();
+ fail();
+ }
+ catch (IOException e) {
+ // should crash with HTTP-status=403, as we are NOT logged in.
+ }
+
+ // requesting a unknown action, the server should throw an error
+ request.clearParameters();
+ request.setParameter("action", "index");
+ request.setParameter("subaction", "didntknow");
+
+ try {
+ document = request.performRequest();
+ fail();
+ }
+ catch (IOException e) {
+ // should crash with HTTP-status=501, as the subaction does not exist.
+ }
+
+ // OK, lets try a real login now.
+ request.setMethod("POST");
+ request.clearParameters();
+ request.setParameter("action", "index");
+ request.setParameter("subaction", "login");
+ request.setParameter("login_name", "admin");
+ request.setParameter("login_password", "admin");
+ request.setParameter("dbid", "db1");
+
+ document = request.performRequest();
+
+ // lets see, what projects are available.
+ request.setMethod("get");
+ request.clearParameters();
+ request.setParameter("action", "index");
+ request.setParameter("subaction", "projectmenu");
+
+ document = request.performRequest();
+
+ // Access the project names via XPath
+ NodeList nl = this.getNodeSet(document, "/server/projects/entry/name");
+ for (int i = 0; i < nl.getLength(); i++) {
+ System.out.println("Project name: " + nl.item(i).getTextContent());
+ }
+
+ // list all users
+ request.clearParameters();
+ request.setParameter("action", "user");
+ request.setParameter("subaction", "listing");
+
+ document = request.performRequest();
+
+ // lets see the rights of the folder with id 1
+ request.clearParameters();
+ request.setParameter("action", "folder");
+ request.setParameter("subaction", "rights");
+ request.setParameter("id", "1");
+
+ document = request.performRequest();
+
+ // lets see the contents of the folder with id 1
+ request.clearParameters();
+ request.setParameter("action", "folder");
+ request.setParameter("subaction", "show");
+ request.setParameter("id", "1");
+
+ document = request.performRequest();
+
+ // Access the object names via XPath
+ nl = this.getNodeSet(document, "/server/object/entry/name");
+ for (int i = 0; i < nl.getLength(); i++) {
+ System.out.println("Object name: " + nl.item(i).getTextContent());
+ }
+ }
+ catch (IOException e) {
+
+ e.printStackTrace();
+ fail();
+ }
+ }
+
+ private String getText(Document document, String xpath) throws IOException {
+
+ XPath xPath = XPathFactory.newInstance().newXPath();
+
+ try {
+ XPathExpression xPathExpression = xPath.compile(xpath);
+ return (String) xPathExpression.evaluate(document, XPathConstants.STRING);
+ }
+ catch (XPathExpressionException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ private NodeList getNodeSet(Document document, String xpath) throws IOException {
+
+ XPath xPath = XPathFactory.newInstance().newXPath();
+
+ try {
+ XPathExpression xPathExpression = xPath.compile(xpath);
+ return (NodeList) xPathExpression.evaluate(document, XPathConstants.NODESET);
+ }
+ catch (XPathExpressionException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+}
diff --git a/client/php/client.php b/client/php/client.php
@@ -0,0 +1,146 @@
+<html>
+<head>
+<title>OpenRat API-Client</title>
+</head>
+<body>
+<h1>OpenRat API-Client</h1>
+<h2>Request</h2>
+<form action="<?php echo $_SERVER['SCRIPT_NAME'] ?>">
+<table>
+<tr>
+<th>Parameter</th><th>Value</th>
+</tr>
+<?php for( $i=1; $i<=10; $i++ ) { ?>
+<tr>
+<td><input name="param<?php echo $i ?>" value="<?php echo $_REQUEST['param'.$i] ?>"></td>
+<td><input name="value<?php echo $i ?>" value="<?php echo htmlentities($_REQUEST['value'.$i]) ?>" size="50"></td>
+<!--
+<td><textarea rows="3" cols="50" name="value<?php echo $i ?>"><?php echo htmlentities($_REQUEST['value'.$i]) ?></textarea></td>
+ -->
+</tr>
+<?php } ?>
+</table><br>
+<select name="type">
+<?php foreach( array('text/html','application/json','application/xml') as $type ) { ?>
+<option value="<?php echo $type ?>" <?php echo ($_REQUEST['type']==$type)?'selected':'' ?>><?php echo $type ?></option>
+<?php } ?>
+</select><input type="submit">
+</form>
+<hr>
+<h2>Response</h2>
+<strong>
+<?php if ( !empty($_REQUEST['param1']) ) {
+
+ $error = '';
+ $status = '';
+
+ $errno = 0;
+ $errstr = '';
+
+ $host = $_SERVER['SERVER_ADDR'];
+ $port = $_SERVER['SERVER_PORT'];
+ $path = substr($_SERVER['SCRIPT_NAME'],0,-15).'/do.php';
+ $method = 'GET';
+
+ // Die Funktion fsockopen() erwartet eine Protokollangabe (bei TCP optional, bei SSL notwendig).
+ if ( $port == '443' )
+ $prx_proto = 'ssl://'; // SSL
+ else
+ $prx_proto = 'tcp://'; // Default
+
+ $fp = fsockopen ($prx_proto.$host,$port, $errno, $errstr, 30);
+
+ if ( !$fp || !is_resource($fp) )
+ {
+ echo "Connection refused: '".$prx_proto.$host.':'.$port." - $errstr ($errno)";
+ }
+ else
+ {
+ $lb = "\r\n";
+ $http_get = $path;
+
+ $parameterString = '';
+
+ for( $i = 1;$i<=10;$i++)
+ {
+ if (!empty($_REQUEST['param'.$i]))
+ {
+ if ( strlen($parameterString) > 0)
+ $parameterString .= '&';
+ elseif ( $withPraefixQuestionMark )
+ $parameterString .= '?';
+
+ $parameterString .= urlencode($_REQUEST['param'.$i]) . '=' .urlencode($_REQUEST['value'.$i]);
+ }
+ }
+
+ if ( $method == 'GET')
+ if ( !empty($parameterString) )
+ $http_get .= '?'.$parameterString;
+
+ if ( $method == 'POST' )
+ {
+ $header[] = 'Content-Type: application/x-www-form-urlencoded';
+ $header[] = 'Content-Length: '.strlen($parameterString);
+ }
+
+ $header[] = 'Host: '.$host;
+ $header[] = 'Accept: '.$_REQUEST['type'];
+ $request_header = array( $method.' '.$http_get.' HTTP/1.0') + $header;
+ $http_request = implode($lb,$request_header).$lb.$lb;
+
+ if ( $method == 'POST' )
+ $http_request .= $parameterString;
+
+ if (!is_resource($fp)) {
+ $error = 'Connection lost after connect: '.$prx_proto.$host.':'.$port;
+ return false;
+ }
+ fputs($fp, $http_request); // Die HTTP-Anfrage zum Server senden.
+
+ // Jetzt erfolgt das Auslesen der HTTP-Antwort.
+ $isHeader = true;
+
+ // RFC 1945 (Section 6.1) schreibt als Statuszeile folgendes Format vor
+ // "HTTP/" 1*DIGIT "." 1*DIGIT SP 3DIGIT SP
+ if (!is_resource($fp)) {
+ echo 'Connection lost during transfer: '.$host.':'.$port;
+ }
+ elseif (!feof($fp)) {
+ $line = fgets($fp,1028);
+ $status = substr($line,9,3);
+ }
+ else
+ {
+ echo 'Unexpected EOF while reading HTTP-Response';
+ }
+
+ while (!feof($fp)) {
+ $line = fgets($fp,1028);
+ if ( $isHeader && trim($line)=='' ) // Leerzeile nach Header.
+ {
+ $isHeader = false;
+ }
+ elseif( $isHeader )
+ {
+ list($headerName,$headerValue) = explode(': ',$line) + array(1=>'');
+ $responseHeader[$headerName] = trim($headerValue);
+ }
+ else
+ {
+ $body .= $line;
+ }
+ }
+ fclose($fp); // Verbindung brav schlie�en.
+ $response = $body;
+
+ // 301 Moved Permanently
+ // 302 Moved Temporarily
+ echo '<span style="background-color:'.($status=='200'?'green':'red').'">HTTP-Status '.$status.'</span>';
+ }
+ ?>
+ </strong>
+<pre><?php echo htmlentities($response) ?></pre>
+<?php } ?>
+</body>
+</html>+
\ No newline at end of file