mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-04-14 22:26:44 +00:00
Rhizome Java API: get manifest
Fixes assertion violation in GET /restful/rhizome/<BID>.rhm when <BID> not found
This commit is contained in:
parent
db8ee79a4e
commit
93e67ede63
44
java/org/servalproject/servaldna/BundleSecret.java
Normal file
44
java/org/servalproject/servaldna/BundleSecret.java
Normal file
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (C) 2014 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;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class BundleSecret extends AbstractId {
|
||||
|
||||
@Override
|
||||
public int getBinarySize() {
|
||||
return 32;
|
||||
}
|
||||
|
||||
public BundleSecret(String hex) throws InvalidHexException {
|
||||
super(hex);
|
||||
}
|
||||
|
||||
public BundleSecret(ByteBuffer b) throws InvalidBinaryException {
|
||||
super(b);
|
||||
}
|
||||
|
||||
public BundleSecret(byte[] binary) throws InvalidBinaryException {
|
||||
super(binary);
|
||||
}
|
||||
|
||||
}
|
@ -32,10 +32,12 @@ import java.net.URLConnection;
|
||||
import java.net.HttpURLConnection;
|
||||
import org.servalproject.codec.Base64;
|
||||
import org.servalproject.servaldna.SubscriberId;
|
||||
import org.servalproject.servaldna.BundleId;
|
||||
import org.servalproject.servaldna.ServalDCommand;
|
||||
import org.servalproject.servaldna.ServalDInterfaceException;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeCommon;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeBundleList;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeManifestBundle;
|
||||
import org.servalproject.servaldna.meshms.MeshMSCommon;
|
||||
import org.servalproject.servaldna.meshms.MeshMSConversationList;
|
||||
import org.servalproject.servaldna.meshms.MeshMSMessageList;
|
||||
@ -67,6 +69,11 @@ public class ServalDClient implements ServalDHttpConnectionFactory
|
||||
return list;
|
||||
}
|
||||
|
||||
public RhizomeManifestBundle rhizomeManifest(BundleId bid) throws ServalDInterfaceException, IOException
|
||||
{
|
||||
return RhizomeCommon.rhizomeManifest(this, bid);
|
||||
}
|
||||
|
||||
public MeshMSConversationList meshmsListConversations(SubscriberId sid) throws ServalDInterfaceException, IOException, MeshMSException
|
||||
{
|
||||
MeshMSConversationList list = new MeshMSConversationList(this, sid);
|
||||
|
@ -1,78 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2014 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.rhizome;
|
||||
|
||||
import org.servalproject.servaldna.BundleId;
|
||||
import org.servalproject.servaldna.SubscriberId;
|
||||
import org.servalproject.servaldna.FileHash;
|
||||
|
||||
public class RhizomeBundle {
|
||||
|
||||
public final int _rowNumber;
|
||||
public final int _id;
|
||||
public final String _token;
|
||||
public final String service;
|
||||
public final BundleId id;
|
||||
public final long version;
|
||||
public final long date;
|
||||
public final long _inserttime;
|
||||
public final SubscriberId _author;
|
||||
public final int _fromhere;
|
||||
public final long filesize;
|
||||
public final FileHash filehash;
|
||||
public final SubscriberId sender;
|
||||
public final SubscriberId recipient;
|
||||
public final String name;
|
||||
|
||||
protected RhizomeBundle(int rowNumber,
|
||||
int _id,
|
||||
String _token,
|
||||
String service,
|
||||
BundleId id,
|
||||
long version,
|
||||
long date,
|
||||
long _inserttime,
|
||||
SubscriberId _author,
|
||||
int _fromhere,
|
||||
long filesize,
|
||||
FileHash filehash,
|
||||
SubscriberId sender,
|
||||
SubscriberId recipient,
|
||||
String name)
|
||||
{
|
||||
this._rowNumber = rowNumber;
|
||||
this._id = _id;
|
||||
this._token = _token;
|
||||
this.service = service;
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
this.date = date;
|
||||
this._inserttime = _inserttime;
|
||||
this._author = _author;
|
||||
this._fromhere = _fromhere;
|
||||
this.filesize = filesize;
|
||||
this.filehash = filehash;
|
||||
this.sender = sender;
|
||||
this.recipient = recipient;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
@ -89,7 +89,7 @@ public class RhizomeBundleList {
|
||||
}
|
||||
}
|
||||
|
||||
public RhizomeBundle nextBundle() throws ServalDInterfaceException, IOException
|
||||
public RhizomeListBundle nextBundle() throws ServalDInterfaceException, IOException
|
||||
{
|
||||
try {
|
||||
Object tok = json.nextToken();
|
||||
@ -103,22 +103,26 @@ public class RhizomeBundleList {
|
||||
else
|
||||
json.pushToken(tok);
|
||||
Map<String,Object> row = table.consumeRowArray(json);
|
||||
return new RhizomeBundle(
|
||||
rowCount++,
|
||||
(int)row.get("_id"),
|
||||
(String)row.get(".token"),
|
||||
(String)row.get("service"),
|
||||
(BundleId)row.get("id"),
|
||||
(long)row.get("version"),
|
||||
(long)row.get("date"),
|
||||
(long)row.get(".inserttime"),
|
||||
(SubscriberId)row.get(".author"),
|
||||
(int)row.get(".fromhere"),
|
||||
(long)row.get("filesize"),
|
||||
(FileHash)row.get("filehash"),
|
||||
(SubscriberId)row.get("sender"),
|
||||
(SubscriberId)row.get("recipient"),
|
||||
(String)row.get("name"));
|
||||
return new RhizomeListBundle(
|
||||
new RhizomeManifest((BundleId)row.get("id"),
|
||||
(long)row.get("version"),
|
||||
(long)row.get("filesize"),
|
||||
(FileHash)row.get("filehash"),
|
||||
(SubscriberId)row.get("sender"),
|
||||
(SubscriberId)row.get("recipient"),
|
||||
null, // BK
|
||||
null, // crypt
|
||||
null, // tail
|
||||
(long)row.get("date"),
|
||||
(String)row.get("service"),
|
||||
(String)row.get("name")),
|
||||
rowCount++,
|
||||
(int)row.get("_id"),
|
||||
(String)row.get(".token"),
|
||||
(long)row.get(".inserttime"),
|
||||
(SubscriberId)row.get(".author"),
|
||||
(int)row.get(".fromhere")
|
||||
);
|
||||
}
|
||||
catch (JSONInputException e) {
|
||||
throw new ServalDInterfaceException(e);
|
||||
|
@ -20,15 +20,47 @@
|
||||
|
||||
package org.servalproject.servaldna.rhizome;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import org.servalproject.servaldna.ServalDInterfaceException;
|
||||
import org.servalproject.json.JSONTokeniser;
|
||||
import org.servalproject.json.JSONInputException;
|
||||
import org.servalproject.servaldna.BundleId;
|
||||
import org.servalproject.servaldna.SubscriberId;
|
||||
import org.servalproject.servaldna.BundleSecret;
|
||||
import org.servalproject.servaldna.ServalDHttpConnectionFactory;
|
||||
import org.servalproject.servaldna.ServalDInterfaceException;
|
||||
import org.servalproject.servaldna.ServalDFailureException;
|
||||
|
||||
public class RhizomeCommon
|
||||
{
|
||||
|
||||
protected static InputStream 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 InputStream receiveResponse(HttpURLConnection conn, int[] expected_response_codes) throws IOException, ServalDInterfaceException
|
||||
{
|
||||
for (int code: expected_response_codes) {
|
||||
if (conn.getResponseCode() == code)
|
||||
return conn.getInputStream();
|
||||
}
|
||||
if (!conn.getContentType().equals("application/json"))
|
||||
throw new ServalDInterfaceException("unexpected HTTP Content-Type: " + conn.getContentType());
|
||||
if (conn.getResponseCode() == HttpURLConnection.HTTP_FORBIDDEN) {
|
||||
JSONTokeniser json = new JSONTokeniser(new InputStreamReader(conn.getErrorStream(), "US-ASCII"));
|
||||
Status status = decodeRestfulStatus(json);
|
||||
throw new ServalDInterfaceException("unexpected Rhizome failure, \"" + status.message + "\"");
|
||||
}
|
||||
throw new ServalDInterfaceException("unexpected HTTP response code: " + conn.getResponseCode());
|
||||
}
|
||||
|
||||
protected static JSONTokeniser receiveRestfulResponse(HttpURLConnection conn, int expected_response_code) throws IOException, ServalDInterfaceException
|
||||
{
|
||||
int[] expected_response_codes = { expected_response_code };
|
||||
@ -37,20 +69,10 @@ public class RhizomeCommon
|
||||
|
||||
protected static JSONTokeniser receiveRestfulResponse(HttpURLConnection conn, int[] expected_response_codes) throws IOException, ServalDInterfaceException
|
||||
{
|
||||
InputStream in = receiveResponse(conn, expected_response_codes);
|
||||
if (!conn.getContentType().equals("application/json"))
|
||||
throw new ServalDInterfaceException("unexpected HTTP Content-Type: " + conn.getContentType());
|
||||
if (conn.getResponseCode() == HttpURLConnection.HTTP_FORBIDDEN) {
|
||||
JSONTokeniser json = new JSONTokeniser(new InputStreamReader(conn.getErrorStream(), "US-ASCII"));
|
||||
Status status = decodeRestfulStatus(json);
|
||||
throw new ServalDInterfaceException("unexpected Rhizome failure, \"" + status.message + "\"");
|
||||
}
|
||||
for (int code: expected_response_codes) {
|
||||
if (conn.getResponseCode() == code) {
|
||||
JSONTokeniser json = new JSONTokeniser(new InputStreamReader(conn.getInputStream(), "US-ASCII"));
|
||||
return json;
|
||||
}
|
||||
}
|
||||
throw new ServalDInterfaceException("unexpected HTTP response code: " + conn.getResponseCode());
|
||||
return new JSONTokeniser(new InputStreamReader(in, "US-ASCII"));
|
||||
}
|
||||
|
||||
private static class Status {
|
||||
@ -78,4 +100,79 @@ public class RhizomeCommon
|
||||
}
|
||||
}
|
||||
|
||||
public static RhizomeManifestBundle rhizomeManifest(ServalDHttpConnectionFactory connector, BundleId bid) throws IOException, ServalDInterfaceException
|
||||
{
|
||||
HttpURLConnection conn = connector.newServalDHttpConnection("/restful/rhizome/" + bid.toHex() + ".rhm");
|
||||
conn.connect();
|
||||
InputStream in = RhizomeCommon.receiveResponse(conn, HttpURLConnection.HTTP_OK);
|
||||
if (!conn.getContentType().equals("rhizome-manifest/text"))
|
||||
throw new ServalDInterfaceException("unexpected HTTP Content-Type: " + conn.getContentType());
|
||||
RhizomeManifest manifest;
|
||||
try {
|
||||
manifest = RhizomeManifest.fromTextFormat(in);
|
||||
}
|
||||
catch (RhizomeManifestParseException e) {
|
||||
throw new ServalDInterfaceException("malformed manifest from daemon", e);
|
||||
}
|
||||
finally {
|
||||
in.close();
|
||||
}
|
||||
Map<String,List<String>> headers = conn.getHeaderFields();
|
||||
for (Map.Entry<String,List<String>> e: headers.entrySet()) {
|
||||
for (String v: e.getValue()) {
|
||||
System.err.println("received header " + e.getKey() + ": " + v);
|
||||
}
|
||||
}
|
||||
long insertTime = headerUnsignedLong(conn, "Serval-Rhizome-Bundle-Inserttime");
|
||||
SubscriberId author = header(conn, "Serval-Rhizome-Bundle-Author", SubscriberId.class);
|
||||
BundleSecret secret = header(conn, "Serval-Rhizome-Bundle-Secret", BundleSecret.class);
|
||||
return new RhizomeManifestBundle(manifest, insertTime, author, secret);
|
||||
}
|
||||
|
||||
private static String headerString(HttpURLConnection conn, String header) throws ServalDInterfaceException
|
||||
{
|
||||
String str = conn.getHeaderField(header);
|
||||
if (str == null)
|
||||
throw new ServalDInterfaceException("missing header field: " + header);
|
||||
return str;
|
||||
}
|
||||
|
||||
private static int headerInteger(HttpURLConnection conn, String header) throws ServalDInterfaceException
|
||||
{
|
||||
String str = headerString(conn, header);
|
||||
try {
|
||||
return Integer.parseInt(str);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
}
|
||||
throw new ServalDInterfaceException("invalid header field: " + header + ": " + str);
|
||||
}
|
||||
|
||||
private static long headerUnsignedLong(HttpURLConnection conn, String header) throws ServalDInterfaceException
|
||||
{
|
||||
String str = headerString(conn, header);
|
||||
try {
|
||||
long value = Long.parseLong(str);
|
||||
if (value >= 0)
|
||||
return value;
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
}
|
||||
throw new ServalDInterfaceException("invalid header field: " + header + ": " + str);
|
||||
}
|
||||
|
||||
private static <T> T header(HttpURLConnection conn, String header, Class<T> cls) throws ServalDInterfaceException
|
||||
{
|
||||
String str = headerString(conn, header);
|
||||
try {
|
||||
return (T) cls.getConstructor(String.class).newInstance(str);
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
throw new ServalDInterfaceException("invalid header field: " + header + ": " + str, e.getTargetException());
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new ServalDInterfaceException("invalid header field: " + header + ": " + str, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (C) 2014 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.rhizome;
|
||||
|
||||
import org.servalproject.servaldna.SubscriberId;
|
||||
|
||||
public class RhizomeListBundle {
|
||||
|
||||
public final int rowNumber;
|
||||
public final int rowId;
|
||||
public final String token;
|
||||
public final long insertTime;
|
||||
public final SubscriberId author;
|
||||
public final int fromHere;
|
||||
public final RhizomeManifest manifest;
|
||||
|
||||
protected RhizomeListBundle(RhizomeManifest manifest,
|
||||
int rowNumber,
|
||||
int rowId,
|
||||
String token,
|
||||
long insertTime,
|
||||
SubscriberId author,
|
||||
int fromHere)
|
||||
|
||||
{
|
||||
this.manifest = manifest;
|
||||
this.rowNumber = rowNumber;
|
||||
this.rowId = rowId;
|
||||
this.token = token;
|
||||
this.insertTime = insertTime;
|
||||
this.author = author;
|
||||
this.fromHere = fromHere;
|
||||
}
|
||||
|
||||
}
|
311
java/org/servalproject/servaldna/rhizome/RhizomeManifest.java
Normal file
311
java/org/servalproject/servaldna/rhizome/RhizomeManifest.java
Normal file
@ -0,0 +1,311 @@
|
||||
/**
|
||||
* Copyright (C) 2014 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.rhizome;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import org.servalproject.servaldna.AbstractId;
|
||||
import org.servalproject.servaldna.SubscriberId;
|
||||
import org.servalproject.servaldna.BundleId;
|
||||
import org.servalproject.servaldna.FileHash;
|
||||
import org.servalproject.servaldna.BundleKey;
|
||||
|
||||
public class RhizomeManifest {
|
||||
|
||||
public final static int TEXT_FORMAT_MAX_SIZE = 8192;
|
||||
|
||||
// Core fields used for routing and expiry (cannot be null)
|
||||
public final BundleId id;
|
||||
public final long version;
|
||||
public final long filesize;
|
||||
|
||||
// Principal fields (can be null)
|
||||
public final FileHash filehash; // null iff filesize == 0
|
||||
public final SubscriberId sender;
|
||||
public final SubscriberId recipient;
|
||||
public final BundleKey BK;
|
||||
public final Long tail; // null iff not a journal
|
||||
public final Integer crypt;
|
||||
|
||||
// Optional fields (all can be null)
|
||||
public final Long date; // can be null
|
||||
public final String service;
|
||||
public final String name;
|
||||
|
||||
private HashMap<String,String> extraFields;
|
||||
private byte[] signatureBlock;
|
||||
private byte[] textFormat;
|
||||
|
||||
protected RhizomeManifest( BundleId id,
|
||||
long version,
|
||||
long filesize,
|
||||
FileHash filehash,
|
||||
SubscriberId sender,
|
||||
SubscriberId recipient,
|
||||
BundleKey BK,
|
||||
Integer crypt,
|
||||
Long tail,
|
||||
Long date,
|
||||
String service,
|
||||
String name
|
||||
)
|
||||
{
|
||||
assert id != null;
|
||||
if (filesize == 0)
|
||||
assert filehash == null;
|
||||
else
|
||||
assert filehash != null;
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
this.filesize = filesize;
|
||||
this.filehash = filehash;
|
||||
this.sender = sender;
|
||||
this.recipient = recipient;
|
||||
this.BK = BK;
|
||||
this.crypt = crypt;
|
||||
this.tail = tail;
|
||||
this.date = date;
|
||||
this.service = service;
|
||||
this.name = name;
|
||||
this.extraFields = null;
|
||||
this.signatureBlock = null;
|
||||
this.textFormat = null;
|
||||
}
|
||||
|
||||
/** Return the Rhizome manifest in its text format representation.
|
||||
*
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
public byte[] toTextFormat() throws RhizomeManifestSizeException
|
||||
{
|
||||
if (textFormat == null) {
|
||||
try {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
OutputStreamWriter osw = new OutputStreamWriter(os, "US-ASCII");
|
||||
osw.write("id=" + id.toHex() + "\n");
|
||||
osw.write("version=" + version + "\n");
|
||||
osw.write("filesize=" + filesize + "\n");
|
||||
if (filehash != null)
|
||||
osw.write("filehash=" + filehash.toHex() + "\n");
|
||||
if (sender != null)
|
||||
osw.write("sender=" + sender.toHex() + "\n");
|
||||
if (recipient != null)
|
||||
osw.write("recipient=" + recipient.toHex() + "\n");
|
||||
if (BK != null)
|
||||
osw.write("BK=" + BK.toHex() + "\n");
|
||||
if (crypt != null)
|
||||
osw.write("crypt=" + crypt + "\n");
|
||||
if (tail != null)
|
||||
osw.write("tail=" + tail + "\n");
|
||||
if (date != null)
|
||||
osw.write("date=" + date + "\n");
|
||||
if (service != null)
|
||||
osw.write("service=" + service + "\n");
|
||||
if (name != null)
|
||||
osw.write("name=" + name + "\n");
|
||||
for (Map.Entry<String,String> e: extraFields.entrySet())
|
||||
osw.write(e.getKey() + "=" + e.getValue() + "\n");
|
||||
osw.flush();
|
||||
if (signatureBlock != null) {
|
||||
os.write(0);
|
||||
os.write(signatureBlock);
|
||||
}
|
||||
osw.close();
|
||||
if (os.size() > TEXT_FORMAT_MAX_SIZE)
|
||||
throw new RhizomeManifestSizeException("manifest text format overflow", os.size(), TEXT_FORMAT_MAX_SIZE);
|
||||
textFormat = os.toByteArray();
|
||||
}
|
||||
catch (IOException e) {
|
||||
// should not happen with ByteArrayOutputStream
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
byte[] ret = new byte[textFormat.length];
|
||||
System.arraycopy(textFormat, 0, ret, 0, ret.length);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Construct a Rhizome manifest from its text format representation.
|
||||
*
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
static public RhizomeManifest fromTextFormat(byte[] bytes) throws RhizomeManifestParseException
|
||||
{
|
||||
return fromTextFormat(bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
/** Construct a Rhizome manifest from its text format representation.
|
||||
*
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
static public RhizomeManifest fromTextFormat(byte[] bytes, int off, int len) throws RhizomeManifestParseException
|
||||
{
|
||||
// The signature block follows the first nul character at the start of a line.
|
||||
byte[] sigblock = null;
|
||||
int proplen = len;
|
||||
for (int i = 0; i < len; ++i) {
|
||||
if (bytes[off + i] == 0 && (i == 0 || bytes[off + i - 1] == '\n')) {
|
||||
sigblock = new byte[len - i - 1];
|
||||
System.arraycopy(bytes, off + i + 1, sigblock, 0, sigblock.length);
|
||||
proplen = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
String text;
|
||||
try {
|
||||
text = new String(bytes, off, proplen, "US-ASCII");
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
throw new RhizomeManifestParseException(e);
|
||||
}
|
||||
BundleId id = null;
|
||||
Long version = null;
|
||||
Long filesize = null;
|
||||
FileHash filehash = null;
|
||||
SubscriberId sender = null;
|
||||
SubscriberId recipient = null;
|
||||
BundleKey BK = null;
|
||||
Integer crypt = null;
|
||||
Long tail = null;
|
||||
Long date = null;
|
||||
String service = null;
|
||||
String name = null;
|
||||
HashMap<String,String> extras = new HashMap<String,String>();
|
||||
int pos = 0;
|
||||
int lnum = 1;
|
||||
while (pos < text.length()) {
|
||||
int nl = text.indexOf('\n', pos);
|
||||
if (nl == -1)
|
||||
nl = text.length();
|
||||
int field = pos;
|
||||
if (!isFieldNameFirstChar(text.charAt(field)))
|
||||
throw new RhizomeManifestParseException("invalid field name at line " + lnum + ": " + text.substring(pos, nl - pos));
|
||||
++field;
|
||||
while (isFieldNameChar(text.charAt(field)))
|
||||
++field;
|
||||
assert field < nl;
|
||||
if (text.charAt(field) != '=')
|
||||
throw new RhizomeManifestParseException("invalid field name at line " + lnum + ": " + text.substring(pos, nl - pos));
|
||||
String fieldName = text.substring(pos, field);
|
||||
String fieldValue = text.substring(field + 1, nl);
|
||||
HashSet<String> fieldNames = new HashSet<String>(50);
|
||||
try {
|
||||
if (fieldNames.contains(fieldName))
|
||||
throw new RhizomeManifestParseException("duplicate field at line " + lnum + ": " + text.substring(pos, nl - pos));
|
||||
fieldNames.add(fieldName);
|
||||
if (fieldName.equals("id"))
|
||||
id = new BundleId(fieldValue);
|
||||
else if (fieldName.equals("version"))
|
||||
version = parseUnsignedLong(fieldValue);
|
||||
else if (fieldName.equals("filesize"))
|
||||
filesize = parseUnsignedLong(fieldValue);
|
||||
else if (fieldName.equals("filehash"))
|
||||
filehash = new FileHash(fieldValue);
|
||||
else if (fieldName.equals("sender"))
|
||||
sender = new SubscriberId(fieldValue);
|
||||
else if (fieldName.equals("recipient"))
|
||||
recipient = new SubscriberId(fieldValue);
|
||||
else if (fieldName.equals("BK"))
|
||||
BK = new BundleKey(fieldValue);
|
||||
else if (fieldName.equals("crypt"))
|
||||
crypt = Integer.parseInt(fieldValue);
|
||||
else if (fieldName.equals("tail"))
|
||||
tail = parseUnsignedLong(fieldValue);
|
||||
else if (fieldName.equals("date"))
|
||||
date = parseUnsignedLong(fieldValue);
|
||||
else if (fieldName.equals("service"))
|
||||
service = fieldValue;
|
||||
else if (fieldName.equals("name"))
|
||||
name = fieldValue;
|
||||
else
|
||||
extras.put(fieldName, fieldValue);
|
||||
}
|
||||
catch (AbstractId.InvalidHexException e) {
|
||||
throw new RhizomeManifestParseException("invalid value at line " + lnum + ": " + text.substring(pos, nl - pos), e);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
throw new RhizomeManifestParseException("invalid value at line " + lnum + ": " + text.substring(pos, nl - pos), e);
|
||||
}
|
||||
pos = nl + 1;
|
||||
}
|
||||
if (id == null)
|
||||
throw new RhizomeManifestParseException("missing 'id' field");
|
||||
if (version == null)
|
||||
throw new RhizomeManifestParseException("missing 'version' field");
|
||||
if (filesize == null)
|
||||
throw new RhizomeManifestParseException("missing 'filesize' field");
|
||||
if (filesize != 0 && filehash == null)
|
||||
throw new RhizomeManifestParseException("missing 'filehash' field");
|
||||
else if (filesize == 0 && filehash != null)
|
||||
throw new RhizomeManifestParseException("spurious 'filehash' field");
|
||||
RhizomeManifest m = new RhizomeManifest(id, version, filesize, filehash, sender, recipient, BK, crypt, tail, date, service, name);
|
||||
m.extraFields = extras;
|
||||
m.signatureBlock = sigblock;
|
||||
m.textFormat = new byte[len];
|
||||
System.arraycopy(bytes, off, m.textFormat, 0, m.textFormat.length);
|
||||
return m;
|
||||
}
|
||||
|
||||
/** Convenience method: construct a Rhizome manifest from all the bytes read from a given
|
||||
* InputStream.
|
||||
*
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
static public RhizomeManifest fromTextFormat(InputStream in) throws IOException, RhizomeManifestParseException
|
||||
{
|
||||
byte[] bytes = new byte[TEXT_FORMAT_MAX_SIZE];
|
||||
int n = 0;
|
||||
int offset = 0;
|
||||
while (offset < bytes.length && (n = in.read(bytes, offset, bytes.length - offset)) != -1)
|
||||
offset += n;
|
||||
assert offset <= bytes.length;
|
||||
if (offset == bytes.length)
|
||||
n = in.read();
|
||||
if (n != -1)
|
||||
throw new RhizomeManifestParseException("input stream too long");
|
||||
return fromTextFormat(bytes, 0, offset);
|
||||
}
|
||||
|
||||
private static boolean isFieldNameFirstChar(char c)
|
||||
{
|
||||
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
|
||||
}
|
||||
|
||||
private static boolean isFieldNameChar(char c)
|
||||
{
|
||||
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
|
||||
}
|
||||
|
||||
private static Long parseUnsignedLong(String text) throws NumberFormatException
|
||||
{
|
||||
Long value = Long.valueOf(text);
|
||||
if (value < 0)
|
||||
throw new NumberFormatException("negative value not allowed");
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright (C) 2014 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.rhizome;
|
||||
|
||||
import org.servalproject.servaldna.SubscriberId;
|
||||
import org.servalproject.servaldna.BundleSecret;
|
||||
import org.servalproject.servaldna.ServalDInterfaceException;
|
||||
|
||||
public class RhizomeManifestBundle {
|
||||
|
||||
public final long insertTime;
|
||||
public final SubscriberId author;
|
||||
public final BundleSecret secret;
|
||||
public final RhizomeManifest manifest;
|
||||
|
||||
protected RhizomeManifestBundle(RhizomeManifest manifest,
|
||||
long insertTime,
|
||||
SubscriberId author,
|
||||
BundleSecret secret)
|
||||
|
||||
{
|
||||
this.manifest = manifest;
|
||||
this.insertTime = insertTime;
|
||||
this.author = author;
|
||||
this.secret = secret;
|
||||
}
|
||||
|
||||
public byte[] manifestText() throws ServalDInterfaceException
|
||||
{
|
||||
try {
|
||||
return manifest.toTextFormat();
|
||||
}
|
||||
catch (RhizomeManifestSizeException e) {
|
||||
throw new ServalDInterfaceException("manifest text overflow", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (C) 2014 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.rhizome;
|
||||
|
||||
/**
|
||||
* Thrown when a Rhizome manifest cannot be parsed from its text format.
|
||||
*
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
public class RhizomeManifestParseException extends Exception
|
||||
{
|
||||
public RhizomeManifestParseException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public RhizomeManifestParseException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public RhizomeManifestParseException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (C) 2014 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.rhizome;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Thrown when the text or binary format of a Rhizome manifest is too long to fit in a limited-size
|
||||
* byte stream.
|
||||
*
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
public class RhizomeManifestSizeException extends Exception
|
||||
{
|
||||
private long mSize;
|
||||
private long mMaxSize;
|
||||
|
||||
public RhizomeManifestSizeException(String message, long size, long maxSize)
|
||||
{
|
||||
super(message + " (" + size + " bytes exceeds " + maxSize + ")");
|
||||
mSize = size;
|
||||
mMaxSize = maxSize;
|
||||
}
|
||||
|
||||
public RhizomeManifestSizeException(File manifestFile, long maxSize)
|
||||
{
|
||||
this(manifestFile.toString(), manifestFile.length(), maxSize);
|
||||
}
|
||||
|
||||
}
|
@ -21,39 +21,50 @@
|
||||
package org.servalproject.test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import org.servalproject.servaldna.ServalDClient;
|
||||
import org.servalproject.servaldna.ServalDInterfaceException;
|
||||
import org.servalproject.servaldna.ServerControl;
|
||||
import org.servalproject.servaldna.SubscriberId;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeBundle;
|
||||
import org.servalproject.servaldna.BundleId;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeManifest;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeListBundle;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeBundleList;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeManifestBundle;
|
||||
|
||||
public class Rhizome {
|
||||
|
||||
static String manifestFields(RhizomeManifest manifest, String sep)
|
||||
{
|
||||
return "id=" + manifest.id + sep +
|
||||
"version=" + manifest.version + sep +
|
||||
"filesize=" + manifest.filesize + sep +
|
||||
"filehash=" + manifest.filehash + sep +
|
||||
"sender=" + manifest.sender + sep +
|
||||
"recipient=" + manifest.recipient + sep +
|
||||
"date=" + manifest.date + sep +
|
||||
"service=" + manifest.service + sep +
|
||||
"name=" + manifest.name + sep +
|
||||
"BK=" + manifest.BK;
|
||||
}
|
||||
|
||||
static void rhizome_list() throws ServalDInterfaceException, IOException, InterruptedException
|
||||
{
|
||||
ServalDClient client = new ServerControl().getRestfulClient();
|
||||
RhizomeBundleList list = null;
|
||||
try {
|
||||
list = client.rhizomeListBundles();
|
||||
RhizomeBundle bundle;
|
||||
RhizomeListBundle bundle;
|
||||
while ((bundle = list.nextBundle()) != null) {
|
||||
System.out.println(
|
||||
"_id=" + bundle._id +
|
||||
", .token=" + bundle._token +
|
||||
", service=" + bundle.service +
|
||||
", id=" + bundle.id +
|
||||
", version=" + bundle.version +
|
||||
", date=" + bundle.date +
|
||||
", .inserttime=" + bundle._inserttime +
|
||||
", .author=" + bundle._author +
|
||||
", .fromhere=" + bundle._fromhere +
|
||||
", filesize=" + bundle.filesize +
|
||||
", filehash=" + bundle.filehash +
|
||||
", sender=" + bundle.sender +
|
||||
", recipient=" + bundle.recipient +
|
||||
", name=" + bundle.name
|
||||
);
|
||||
"_rowId=" + bundle.rowId +
|
||||
", _token=" + bundle.token +
|
||||
", _insertTime=" + bundle.insertTime +
|
||||
", _author=" + bundle.author +
|
||||
", _fromHere=" + bundle.fromHere +
|
||||
", " + manifestFields(bundle.manifest, ", ")
|
||||
);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
@ -63,6 +74,22 @@ public class Rhizome {
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
static void rhizome_manifest(BundleId bid, String dstpath) throws ServalDInterfaceException, IOException, InterruptedException
|
||||
{
|
||||
ServalDClient client = new ServerControl().getRestfulClient();
|
||||
RhizomeManifestBundle bundle = client.rhizomeManifest(bid);
|
||||
System.out.println(
|
||||
"_insertTime=" + bundle.insertTime + "\n" +
|
||||
"_author=" + bundle.author + "\n" +
|
||||
"_secret=" + bundle.secret + "\n" +
|
||||
manifestFields(bundle.manifest, "\n") + "\n"
|
||||
);
|
||||
FileOutputStream out = new FileOutputStream(dstpath);
|
||||
out.write(bundle.manifestText());
|
||||
out.close();
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
public static void main(String... args)
|
||||
{
|
||||
if (args.length < 1)
|
||||
@ -71,6 +98,8 @@ public class Rhizome {
|
||||
try {
|
||||
if (methodName.equals("rhizome-list"))
|
||||
rhizome_list();
|
||||
else if (methodName.equals("rhizome-manifest"))
|
||||
rhizome_manifest(new BundleId(args[1]), args[2]);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
|
@ -595,13 +595,17 @@ int restful_rhizome_(httpd_request *r, const char *remainder)
|
||||
if ((r->manifest = rhizome_new_manifest()) == NULL)
|
||||
return 500;
|
||||
ret = rhizome_retrieve_manifest(&bid, r->manifest);
|
||||
if (ret == -1)
|
||||
if (ret == -1) {
|
||||
rhizome_manifest_free(r->manifest);
|
||||
r->manifest = NULL;
|
||||
return 500;
|
||||
}
|
||||
if (ret == 0) {
|
||||
rhizome_authenticate_author(r->manifest);
|
||||
r->http.render_extra_headers = render_manifest_headers;
|
||||
} else {
|
||||
assert(r->manifest == NULL);
|
||||
rhizome_manifest_free(r->manifest);
|
||||
r->manifest = NULL;
|
||||
assert(r->http.render_extra_headers == NULL);
|
||||
}
|
||||
ret = handler(r, remainder);
|
||||
|
@ -117,25 +117,56 @@ test_RhizomeList() {
|
||||
let lnum=NBUNDLES
|
||||
for ((n = 0; n != NBUNDLES; ++n)); do
|
||||
line="$(sed -n -e ${lnum}p "$TFWSTDOUT")"
|
||||
unset_vars_with_prefix X__
|
||||
unpack_vars X__ "$line"
|
||||
unset_vars_with_prefix XX_
|
||||
unpack_vars XX_ "$line"
|
||||
if [ "${ROWID[$n]}" -eq "$ROWID_MAX" ]; then
|
||||
# The first row must contain a non-null token string.
|
||||
assert [ -n "$X____token" ]
|
||||
assert [ -n "$XX__token" ]
|
||||
assert [ "$lnum" -eq 1 ]
|
||||
fi
|
||||
assert [ "$X__name" = "file$n" ]
|
||||
assert [ "$X__service" = "file" ]
|
||||
assert [ "$X__id" = "${BID[$n]}" ]
|
||||
assert [ "$X__version" = "${VERSION[$n]}" ]
|
||||
assert [ "$X__filesize" = "${SIZE[$n]}" ]
|
||||
assert [ "$X__filehash" = "${HASH[$n]}" ]
|
||||
assert [ "$X__date" = "${DATE[$n]}" ]
|
||||
assert [ "$X___id" = "${ROWID[$n]}" ]
|
||||
assert [ "$X____fromhere" = "1" ]
|
||||
assert [ "$X____author" = "$SIDA1" ]
|
||||
assert [ "$XX_id" = "${BID[$n]}" ]
|
||||
assert [ "$XX_version" = "${VERSION[$n]}" ]
|
||||
assert [ "$XX_filesize" = "${SIZE[$n]}" ]
|
||||
assert [ "$XX_filehash" = "${HASH[$n]}" ]
|
||||
assert [ "$XX_date" = "${DATE[$n]}" ]
|
||||
assert [ "$XX_service" = "file" ]
|
||||
assert [ "$XX_name" = "file$n" ]
|
||||
assert [ "$XX__rowId" = "${ROWID[$n]}" ]
|
||||
assert [ "$XX__fromHere" = "1" ]
|
||||
assert [ "$XX__author" = "$SIDA1" ]
|
||||
assert [ "$XX__insertTime" = "${INSERTTIME[$n]}" ]
|
||||
let --lnum
|
||||
done
|
||||
}
|
||||
|
||||
assert_metadata() {
|
||||
local n=$1
|
||||
assertStdoutGrep --matches=1 "^id=${BID[$n]}$CR\$"
|
||||
assertStdoutGrep --matches=1 "^version=${VERSION[$n]}$CR\$"
|
||||
assertStdoutGrep --matches=1 "^filesize=${SIZE[$n]}$CR\$"
|
||||
assertStdoutGrep --matches=1 "^filehash=${HASH[$n]}$CR\$"
|
||||
assertStdoutGrep --matches=1 "^BK=${BK[$n]}$CR\$"
|
||||
assertStdoutGrep --matches=1 "^date=${DATE[$n]}$CR\$"
|
||||
assertStdoutGrep --matches=1 "^name=${NAME[$n]}$CR\$"
|
||||
assertStdoutGrep --matches=1 "^service=file$CR\$"
|
||||
assertStdoutGrep --matches=1 "^_insertTime=${INSERTTIME[$n]}$CR\$"
|
||||
assertStdoutGrep --matches=1 "^_author=${AUTHOR[$n]}$CR\$"
|
||||
assertStdoutGrep --matches=1 "^_secret=${SECRET[$n]}$CR\$"
|
||||
}
|
||||
|
||||
doc_RhizomeManifest="Java API fetch Rhizome manifest"
|
||||
setup_RhizomeManifest() {
|
||||
setup
|
||||
rhizome_add_bundles $SIDA1 0 2
|
||||
}
|
||||
test_RhizomeManifest() {
|
||||
for n in 0 1 2; do
|
||||
executeJavaOk org.servalproject.test.Rhizome rhizome-manifest ${BID[$n]} bundle$n.rhm
|
||||
tfw_cat --stdout --stderr
|
||||
assert_metadata $n
|
||||
tfw_cat -v file$n.manifest -v bundle$n.rhm
|
||||
assert diff file$n.manifest bundle$n.rhm
|
||||
done
|
||||
}
|
||||
|
||||
runTests "$@"
|
||||
|
Loading…
x
Reference in New Issue
Block a user