mirror of
https://github.com/servalproject/serval-dna.git
synced 2024-12-27 08:32:33 +00:00
259 lines
9.7 KiB
Java
259 lines
9.7 KiB
Java
/**
|
|
* Copyright (C) 2015 Serval Project Inc.
|
|
*
|
|
* This file is part of Serval Software (http://www.servalproject.org)
|
|
*
|
|
* Serval Software is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This source code 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this source code; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
package org.servalproject.servaldna.keyring;
|
|
|
|
import java.lang.StringBuilder;
|
|
import java.lang.reflect.Constructor;
|
|
import java.lang.reflect.Method;
|
|
import java.lang.reflect.Modifier;
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import java.util.Map;
|
|
import java.util.List;
|
|
import java.util.Vector;
|
|
import java.io.IOException;
|
|
import java.io.PrintStream;
|
|
import java.io.InputStream;
|
|
import java.io.InputStreamReader;
|
|
import java.io.OutputStream;
|
|
import java.net.URL;
|
|
import java.net.HttpURLConnection;
|
|
import org.servalproject.json.JSONTokeniser;
|
|
import org.servalproject.json.JSONInputException;
|
|
import org.servalproject.servaldna.SubscriberId;
|
|
import org.servalproject.servaldna.ServalDHttpConnectionFactory;
|
|
import org.servalproject.servaldna.ServalDInterfaceException;
|
|
import org.servalproject.servaldna.ServalDFailureException;
|
|
import org.servalproject.servaldna.ServalDNotImplementedException;
|
|
|
|
public class KeyringCommon
|
|
{
|
|
|
|
public static class Status {
|
|
InputStream input_stream;
|
|
JSONTokeniser json;
|
|
public int http_status_code;
|
|
public String http_status_message;
|
|
public KeyringIdentity identity;
|
|
}
|
|
|
|
private static void dumpStatus(Status status, PrintStream out)
|
|
{
|
|
out.println("input_stream=" + status.input_stream);
|
|
out.println("http_status_code=" + status.http_status_code);
|
|
out.println("http_status_message=" + status.http_status_message);
|
|
if (status.identity == null) {
|
|
out.println("identity=null");
|
|
} else {
|
|
out.println("identity.sid=" + status.identity.sid);
|
|
out.println("identity.did=" + status.identity.did);
|
|
out.println("identity.name=" + status.identity.name);
|
|
}
|
|
}
|
|
|
|
protected static Status receiveResponse(HttpURLConnection conn, int expected_response_code) throws IOException, ServalDInterfaceException
|
|
{
|
|
int[] expected_response_codes = { expected_response_code };
|
|
return receiveResponse(conn, expected_response_codes);
|
|
}
|
|
|
|
protected static Status receiveResponse(HttpURLConnection conn, int[] expected_response_codes) throws IOException, ServalDInterfaceException
|
|
{
|
|
Status status = new Status();
|
|
status.http_status_code = conn.getResponseCode();
|
|
status.http_status_message = conn.getResponseMessage();
|
|
for (int code: expected_response_codes) {
|
|
if (status.http_status_code == code) {
|
|
status.input_stream = conn.getInputStream();
|
|
return status;
|
|
}
|
|
}
|
|
if (!conn.getContentType().equals("application/json"))
|
|
throw new ServalDInterfaceException("unexpected HTTP Content-Type: " + conn.getContentType());
|
|
if (status.http_status_code >= 300) {
|
|
status.json = new JSONTokeniser(new InputStreamReader(conn.getErrorStream(), "UTF-8"));
|
|
decodeRestfulStatus(status);
|
|
}
|
|
if (status.http_status_code == HttpURLConnection.HTTP_FORBIDDEN)
|
|
return status;
|
|
if (status.http_status_code == HttpURLConnection.HTTP_NOT_IMPLEMENTED)
|
|
throw new ServalDNotImplementedException(status.http_status_message);
|
|
throw new ServalDInterfaceException("unexpected HTTP response: " + status.http_status_code + " " + status.http_status_message);
|
|
}
|
|
|
|
protected static ServalDInterfaceException unexpectedResponse(HttpURLConnection conn, Status status)
|
|
{
|
|
return new ServalDInterfaceException(
|
|
"unexpected Keyring failure, " + quoteString(status.http_status_message)
|
|
+ " from " + conn.getURL()
|
|
);
|
|
}
|
|
|
|
protected static Status receiveRestfulResponse(HttpURLConnection conn, int expected_response_code) throws IOException, ServalDInterfaceException
|
|
{
|
|
int[] expected_response_codes = { expected_response_code };
|
|
return receiveRestfulResponse(conn, expected_response_codes);
|
|
}
|
|
|
|
protected static Status receiveRestfulResponse(HttpURLConnection conn, int[] expected_response_codes) throws IOException, ServalDInterfaceException
|
|
{
|
|
Status status = receiveResponse(conn, expected_response_codes);
|
|
if (!conn.getContentType().equals("application/json"))
|
|
throw new ServalDInterfaceException("unexpected HTTP Content-Type: " + conn.getContentType());
|
|
status.json = new JSONTokeniser(new InputStreamReader(status.input_stream, "UTF-8"));
|
|
return status;
|
|
}
|
|
|
|
protected static void decodeRestfulStatus(Status status) throws IOException, ServalDInterfaceException
|
|
{
|
|
JSONTokeniser json = status.json;
|
|
try {
|
|
json.consume(JSONTokeniser.Token.START_OBJECT);
|
|
json.consume("http_status_code");
|
|
json.consume(JSONTokeniser.Token.COLON);
|
|
int hs = json.consume(Integer.class);
|
|
json.consume(JSONTokeniser.Token.COMMA);
|
|
if (status.http_status_code == 0)
|
|
status.http_status_code = json.consume(Integer.class);
|
|
else if (hs != status.http_status_code)
|
|
throw new ServalDInterfaceException("JSON/header conflict"
|
|
+ ", http_status_code=" + hs
|
|
+ " but HTTP response code is " + status.http_status_code);
|
|
json.consume("http_status_message");
|
|
json.consume(JSONTokeniser.Token.COLON);
|
|
status.http_status_message = json.consume(String.class);
|
|
Object tok = json.nextToken();
|
|
if (tok == JSONTokeniser.Token.COMMA) {
|
|
json.consume("identity");
|
|
json.consume(JSONTokeniser.Token.COLON);
|
|
json.consume(JSONTokeniser.Token.START_OBJECT);
|
|
json.consume("sid");
|
|
json.consume(JSONTokeniser.Token.COLON);
|
|
String sid_hex = json.consume(String.class);
|
|
SubscriberId sid = new SubscriberId(sid_hex);
|
|
String did = null;
|
|
String name = null;
|
|
tok = json.nextToken();
|
|
if (tok == JSONTokeniser.Token.COMMA) {
|
|
json.consume("did");
|
|
json.consume(JSONTokeniser.Token.COLON);
|
|
did = json.consume(String.class);
|
|
tok = json.nextToken();
|
|
}
|
|
if (tok == JSONTokeniser.Token.COMMA) {
|
|
json.consume("name");
|
|
json.consume(JSONTokeniser.Token.COLON);
|
|
name = json.consume(String.class);
|
|
tok = json.nextToken();
|
|
}
|
|
json.match(tok, JSONTokeniser.Token.END_OBJECT);
|
|
tok = json.nextToken();
|
|
status.identity = new KeyringIdentity(0, sid, did, name);
|
|
}
|
|
json.match(tok, JSONTokeniser.Token.END_OBJECT);
|
|
json.consume(JSONTokeniser.Token.EOF);
|
|
}
|
|
catch (SubscriberId.InvalidHexException e) {
|
|
throw new ServalDInterfaceException("malformed JSON status response", e);
|
|
}
|
|
catch (JSONInputException e) {
|
|
throw new ServalDInterfaceException("malformed JSON status response", e);
|
|
}
|
|
}
|
|
|
|
private static void dumpHeaders(HttpURLConnection conn, PrintStream out)
|
|
{
|
|
for (Map.Entry<String,List<String>> e: conn.getHeaderFields().entrySet())
|
|
for (String v: e.getValue())
|
|
out.println("received header " + e.getKey() + ": " + v);
|
|
}
|
|
|
|
private static String quoteString(String unquoted)
|
|
{
|
|
if (unquoted == null)
|
|
return "null";
|
|
StringBuilder b = new StringBuilder(unquoted.length() + 2);
|
|
b.append('"');
|
|
for (int i = 0; i < unquoted.length(); ++i) {
|
|
char c = unquoted.charAt(i);
|
|
if (c == '"' || c == '\\')
|
|
b.append('\\');
|
|
b.append(c);
|
|
}
|
|
b.append('"');
|
|
return b.toString();
|
|
}
|
|
|
|
public static KeyringIdentity setDidName(ServalDHttpConnectionFactory connector, SubscriberId sid, String did, String name, String pin)
|
|
throws IOException, ServalDInterfaceException
|
|
{
|
|
Vector<ServalDHttpConnectionFactory.QueryParam> query_params = new Vector<ServalDHttpConnectionFactory.QueryParam>();
|
|
if (did != null)
|
|
query_params.add(new ServalDHttpConnectionFactory.QueryParam("did", did));
|
|
if (name != null)
|
|
query_params.add(new ServalDHttpConnectionFactory.QueryParam("name", name));
|
|
if (pin != null)
|
|
query_params.add(new ServalDHttpConnectionFactory.QueryParam("pin", pin));
|
|
HttpURLConnection conn = connector.newServalDHttpConnection("/restful/keyring/" + sid.toHex() + "/set", query_params);
|
|
conn.connect();
|
|
Status status = receiveRestfulResponse(conn, HttpURLConnection.HTTP_OK);
|
|
try {
|
|
decodeRestfulStatus(status);
|
|
dumpStatus(status, System.err);
|
|
if (status.identity == null)
|
|
throw new ServalDInterfaceException("invalid JSON response; missing identity");
|
|
|
|
return status.identity;
|
|
}
|
|
finally {
|
|
if (status.input_stream != null)
|
|
status.input_stream.close();
|
|
}
|
|
}
|
|
|
|
public static KeyringIdentity addIdentity(ServalDHttpConnectionFactory connector, String did, String name, String pin)
|
|
throws IOException, ServalDInterfaceException
|
|
{
|
|
Vector<ServalDHttpConnectionFactory.QueryParam> query_params = new Vector<ServalDHttpConnectionFactory.QueryParam>();
|
|
if (did != null)
|
|
query_params.add(new ServalDHttpConnectionFactory.QueryParam("did", did));
|
|
if (name != null)
|
|
query_params.add(new ServalDHttpConnectionFactory.QueryParam("name", name));
|
|
if (pin != null)
|
|
query_params.add(new ServalDHttpConnectionFactory.QueryParam("pin", pin));
|
|
HttpURLConnection conn = connector.newServalDHttpConnection("/restful/keyring/add", query_params);
|
|
conn.connect();
|
|
Status status = receiveRestfulResponse(conn, HttpURLConnection.HTTP_OK);
|
|
try {
|
|
decodeRestfulStatus(status);
|
|
dumpStatus(status, System.err);
|
|
if (status.identity == null)
|
|
throw new ServalDInterfaceException("invalid JSON response; missing identity");
|
|
|
|
return status.identity;
|
|
}
|
|
finally {
|
|
if (status.input_stream != null)
|
|
status.input_stream.close();
|
|
}
|
|
}
|
|
|
|
}
|