mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-04-13 22:03:09 +00:00
Rhizome Java API: insert bundle
This commit is contained in:
parent
3715c5bf0b
commit
a81d05b4f6
@ -25,6 +25,7 @@ import org.servalproject.servaldna.meshms.MeshMSConversationList;
|
||||
import org.servalproject.servaldna.meshms.MeshMSException;
|
||||
import org.servalproject.servaldna.meshms.MeshMSMessageList;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URL;
|
||||
@ -36,11 +37,19 @@ 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.RhizomeIncompleteManifest;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeBundleList;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeManifestBundle;
|
||||
import org.servalproject.servaldna.rhizome.RhizomePayloadRawBundle;
|
||||
import org.servalproject.servaldna.rhizome.RhizomePayloadBundle;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeInsertBundle;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeException;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeInvalidManifestException;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeFakeManifestException;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeInconsistencyException;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeEncryptionException;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeReadOnlyException;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeDecryptionException;
|
||||
import org.servalproject.servaldna.meshms.MeshMSCommon;
|
||||
import org.servalproject.servaldna.meshms.MeshMSConversationList;
|
||||
import org.servalproject.servaldna.meshms.MeshMSMessageList;
|
||||
@ -87,6 +96,30 @@ public class ServalDClient implements ServalDHttpConnectionFactory
|
||||
return RhizomeCommon.rhizomePayload(this, bid);
|
||||
}
|
||||
|
||||
public RhizomeInsertBundle rhizomeInsert(SubscriberId author, RhizomeIncompleteManifest manifest)
|
||||
throws ServalDInterfaceException,
|
||||
IOException,
|
||||
RhizomeInvalidManifestException,
|
||||
RhizomeFakeManifestException,
|
||||
RhizomeInconsistencyException,
|
||||
RhizomeReadOnlyException,
|
||||
RhizomeEncryptionException
|
||||
{
|
||||
return RhizomeCommon.rhizomeInsert(this, author, manifest);
|
||||
}
|
||||
|
||||
public RhizomeInsertBundle rhizomeInsert(SubscriberId author, RhizomeIncompleteManifest manifest, InputStream payloadStream, String fileName)
|
||||
throws ServalDInterfaceException,
|
||||
IOException,
|
||||
RhizomeInvalidManifestException,
|
||||
RhizomeFakeManifestException,
|
||||
RhizomeInconsistencyException,
|
||||
RhizomeReadOnlyException,
|
||||
RhizomeEncryptionException
|
||||
{
|
||||
return RhizomeCommon.rhizomeInsert(this, author, manifest, payloadStream, fileName);
|
||||
}
|
||||
|
||||
public MeshMSConversationList meshmsListConversations(SubscriberId sid) throws ServalDInterfaceException, IOException, MeshMSException
|
||||
{
|
||||
MeshMSConversationList list = new MeshMSConversationList(this, sid);
|
||||
|
@ -33,7 +33,8 @@ public enum RhizomeBundleStatus {
|
||||
INVALID(4), // manifest is invalid
|
||||
FAKE(5), // manifest signature not valid
|
||||
INCONSISTENT(6), // manifest filesize/filehash does not match supplied payload
|
||||
NO_ROOM(7) // doesn't fit; store may contain more important bundles
|
||||
NO_ROOM(7), // doesn't fit; store may contain more important bundles
|
||||
READONLY(8) // cannot modify manifest; secret unknown
|
||||
;
|
||||
|
||||
final public int code;
|
||||
@ -55,6 +56,7 @@ public enum RhizomeBundleStatus {
|
||||
case 5: status = FAKE; break;
|
||||
case 6: status = INCONSISTENT; break;
|
||||
case 7: status = NO_ROOM; break;
|
||||
case 8: status = READONLY; break;
|
||||
default: throw new InvalidException(code);
|
||||
}
|
||||
assert status.code == code;
|
||||
|
@ -31,6 +31,7 @@ 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;
|
||||
@ -57,6 +58,17 @@ public class RhizomeCommon
|
||||
String payload_status_message;
|
||||
}
|
||||
|
||||
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);
|
||||
out.println("bundle_status_code=" + status.bundle_status_code);
|
||||
out.println("bundle_status_message=" + status.bundle_status_message);
|
||||
out.println("payload_status_code=" + status.payload_status_code);
|
||||
out.println("payload_status_message=" + status.payload_status_message);
|
||||
}
|
||||
|
||||
protected static Status receiveResponse(HttpURLConnection conn, int expected_response_code) throws IOException, ServalDInterfaceException
|
||||
{
|
||||
int[] expected_response_codes = { expected_response_code };
|
||||
@ -84,9 +96,9 @@ public class RhizomeCommon
|
||||
throw new ServalDInterfaceException("unexpected HTTP response code: " + conn.getResponseCode());
|
||||
}
|
||||
|
||||
protected static void unexpectedResponse(Status status) throws ServalDInterfaceException
|
||||
protected static ServalDInterfaceException unexpectedResponse(Status status)
|
||||
{
|
||||
throw new ServalDInterfaceException(
|
||||
return new ServalDInterfaceException(
|
||||
"unexpected Rhizome failure, \"" + status.http_status_message + "\""
|
||||
+ (status.bundle_status_code == null ? "" : ", " + status.bundle_status_code)
|
||||
+ (status.bundle_status_message == null ? "" : " \"" + status.bundle_status_message + "\"")
|
||||
@ -95,34 +107,6 @@ public class RhizomeCommon
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
protected static void throwRestfulResponseExceptions(Status status, URL url) throws RhizomeException, ServalDFailureException
|
||||
{
|
||||
if (status.bundle_status_code != null) {
|
||||
switch (status.bundle_status_code) {
|
||||
case ERROR:
|
||||
throw new ServalDFailureException("received rhizome_bundle_status_code=ERROR(-1) from " + url);
|
||||
case NEW:
|
||||
throw new RhizomeManifestNotFoundException(url);
|
||||
case SAME:
|
||||
throw new RhizomeManifestAlreadyStoredException(url);
|
||||
case DUPLICATE:
|
||||
throw new RhizomeDuplicateBundleException(url);
|
||||
case OLD:
|
||||
throw new RhizomeOutdatedBundleException(url);
|
||||
case NO_ROOM:
|
||||
throw new RhizomeStoreFullException(url);
|
||||
case INVALID:
|
||||
throw new RhizomeInvalidManifestException(url);
|
||||
case FAKE:
|
||||
throw new RhizomeFakeManifestException(url);
|
||||
case INCONSISTENT:
|
||||
throw new RhizomeInconsistencyException(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
protected static JSONTokeniser receiveRestfulResponse(HttpURLConnection conn, int expected_response_code) throws IOException, ServalDInterfaceException, RhizomeException
|
||||
{
|
||||
int[] expected_response_codes = { expected_response_code };
|
||||
@ -227,6 +211,7 @@ public class RhizomeCommon
|
||||
try {
|
||||
dumpHeaders(conn, System.err);
|
||||
decodeHeaderBundleStatus(status, conn);
|
||||
dumpStatus(status, System.err);
|
||||
switch (status.bundle_status_code) {
|
||||
case NEW:
|
||||
return null;
|
||||
@ -235,7 +220,7 @@ public class RhizomeCommon
|
||||
throw new ServalDInterfaceException("unexpected HTTP Content-Type: " + conn.getContentType());
|
||||
RhizomeManifest manifest = RhizomeManifest.fromTextFormat(status.input_stream);
|
||||
BundleExtra extra = bundleExtraFromHeaders(conn);
|
||||
return new RhizomeManifestBundle(manifest, extra.insertTime, extra.author, extra.secret);
|
||||
return new RhizomeManifestBundle(manifest, extra.rowId, extra.insertTime, extra.author, extra.secret);
|
||||
case ERROR:
|
||||
throw new ServalDFailureException("received rhizome_bundle_status_code=ERROR(-1) from " + conn.getURL());
|
||||
}
|
||||
@ -247,8 +232,7 @@ public class RhizomeCommon
|
||||
if (status.input_stream != null)
|
||||
status.input_stream.close();
|
||||
}
|
||||
unexpectedResponse(status);
|
||||
return null;
|
||||
throw unexpectedResponse(status);
|
||||
}
|
||||
|
||||
public static RhizomePayloadRawBundle rhizomePayloadRaw(ServalDHttpConnectionFactory connector, BundleId bid)
|
||||
@ -260,6 +244,7 @@ public class RhizomeCommon
|
||||
try {
|
||||
dumpHeaders(conn, System.err);
|
||||
decodeHeaderBundleStatus(status, conn);
|
||||
dumpStatus(status, System.err);
|
||||
switch (status.bundle_status_code) {
|
||||
case ERROR:
|
||||
throw new ServalDFailureException("received rhizome_bundle_status_code=ERROR(-1) from " + conn.getURL());
|
||||
@ -285,7 +270,7 @@ public class RhizomeCommon
|
||||
throw new ServalDInterfaceException("unexpected HTTP Content-Type: " + conn.getContentType());
|
||||
RhizomeManifest manifest = manifestFromHeaders(conn);
|
||||
BundleExtra extra = bundleExtraFromHeaders(conn);
|
||||
RhizomePayloadRawBundle ret = new RhizomePayloadRawBundle(manifest, status.input_stream, extra.insertTime, extra.author, extra.secret);
|
||||
RhizomePayloadRawBundle ret = new RhizomePayloadRawBundle(manifest, status.input_stream, extra.rowId, extra.insertTime, extra.author, extra.secret);
|
||||
status.input_stream = null; // don't close when we return
|
||||
return ret;
|
||||
}
|
||||
@ -296,8 +281,7 @@ public class RhizomeCommon
|
||||
if (status.input_stream != null)
|
||||
status.input_stream.close();
|
||||
}
|
||||
unexpectedResponse(status);
|
||||
return null;
|
||||
throw unexpectedResponse(status);
|
||||
}
|
||||
|
||||
public static RhizomePayloadBundle rhizomePayload(ServalDHttpConnectionFactory connector, BundleId bid)
|
||||
@ -309,6 +293,7 @@ public class RhizomeCommon
|
||||
try {
|
||||
dumpHeaders(conn, System.err);
|
||||
decodeHeaderBundleStatus(status, conn);
|
||||
dumpStatus(status, System.err);
|
||||
switch (status.bundle_status_code) {
|
||||
case ERROR:
|
||||
throw new ServalDFailureException("received rhizome_bundle_status_code=ERROR(-1) from " + conn.getURL());
|
||||
@ -336,7 +321,7 @@ public class RhizomeCommon
|
||||
throw new ServalDInterfaceException("unexpected HTTP Content-Type: " + conn.getContentType());
|
||||
RhizomeManifest manifest = manifestFromHeaders(conn);
|
||||
BundleExtra extra = bundleExtraFromHeaders(conn);
|
||||
RhizomePayloadBundle ret = new RhizomePayloadBundle(manifest, status.input_stream, extra.insertTime, extra.author, extra.secret);
|
||||
RhizomePayloadBundle ret = new RhizomePayloadBundle(manifest, status.input_stream, extra.rowId, extra.insertTime, extra.author, extra.secret);
|
||||
status.input_stream = null; // don't close when we return
|
||||
return ret;
|
||||
}
|
||||
@ -347,8 +332,134 @@ public class RhizomeCommon
|
||||
if (status.input_stream != null)
|
||||
status.input_stream.close();
|
||||
}
|
||||
unexpectedResponse(status);
|
||||
return null;
|
||||
throw unexpectedResponse(status);
|
||||
}
|
||||
|
||||
public static RhizomeInsertBundle rhizomeInsert(ServalDHttpConnectionFactory connector,
|
||||
SubscriberId author,
|
||||
RhizomeIncompleteManifest manifest)
|
||||
throws ServalDInterfaceException,
|
||||
IOException,
|
||||
RhizomeInvalidManifestException,
|
||||
RhizomeFakeManifestException,
|
||||
RhizomeInconsistencyException,
|
||||
RhizomeReadOnlyException,
|
||||
RhizomeEncryptionException
|
||||
{
|
||||
return rhizomeInsert(connector, author, manifest, null, null);
|
||||
}
|
||||
|
||||
public static RhizomeInsertBundle rhizomeInsert(ServalDHttpConnectionFactory connector,
|
||||
SubscriberId author,
|
||||
RhizomeIncompleteManifest manifest,
|
||||
InputStream payloadStream,
|
||||
String fileName)
|
||||
throws ServalDInterfaceException,
|
||||
IOException,
|
||||
RhizomeInvalidManifestException,
|
||||
RhizomeFakeManifestException,
|
||||
RhizomeInconsistencyException,
|
||||
RhizomeReadOnlyException,
|
||||
RhizomeEncryptionException
|
||||
{
|
||||
HttpURLConnection conn = connector.newServalDHttpConnection("/restful/rhizome/insert");
|
||||
String boundary = Long.toHexString(System.currentTimeMillis());
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setDoOutput(true);
|
||||
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
|
||||
conn.connect();
|
||||
OutputStream ost = conn.getOutputStream();
|
||||
PrintStream wr = new PrintStream(ost, false, "US-ASCII");
|
||||
if (author != null) {
|
||||
wr.print("\r\n--" + boundary + "\r\n");
|
||||
wr.print("Content-Disposition: form-data; name=\"bundle-author\"\r\n");
|
||||
wr.print("\r\n");
|
||||
wr.print(author.toHex());
|
||||
}
|
||||
wr.print("\r\n--" + boundary + "\r\n");
|
||||
wr.print("Content-Disposition: form-data; name=\"manifest\"\r\n");
|
||||
wr.print("Content-Type: rhizome-manifest/text\r\n");
|
||||
wr.print("\r\n");
|
||||
wr.flush();
|
||||
manifest.toTextFormat(ost);
|
||||
if (payloadStream != null) {
|
||||
wr.print("\r\n--" + boundary + "\r\n");
|
||||
wr.print("Content-Disposition: form-data; name=\"payload\"");
|
||||
if (fileName != null) {
|
||||
wr.print("; filename=");
|
||||
wr.print(quoteString(fileName));
|
||||
}
|
||||
wr.print("\r\n");
|
||||
wr.print("Content-Type: application/octet-stream\r\n");
|
||||
wr.print("\r\n");
|
||||
wr.flush();
|
||||
byte[] buffer = new byte[4096];
|
||||
int n;
|
||||
while ((n = payloadStream.read(buffer)) > 0)
|
||||
ost.write(buffer, 0, n);
|
||||
}
|
||||
wr.print("\r\n--" + boundary + "--\r\n");
|
||||
wr.close();
|
||||
int[] expected_response_codes = { HttpURLConnection.HTTP_OK, HttpURLConnection.HTTP_CREATED };
|
||||
Status status = RhizomeCommon.receiveResponse(conn, expected_response_codes);
|
||||
try {
|
||||
dumpHeaders(conn, System.err);
|
||||
decodeHeaderPayloadStatus(status, conn);
|
||||
switch (status.payload_status_code) {
|
||||
case ERROR:
|
||||
dumpStatus(status, System.err);
|
||||
throw new ServalDFailureException("received rhizome_payload_status_code=ERROR(-1) from " + conn.getURL());
|
||||
case EMPTY:
|
||||
case NEW:
|
||||
case STORED:
|
||||
decodeHeaderBundleStatus(status, conn);
|
||||
dumpStatus(status, System.err);
|
||||
switch (status.bundle_status_code) {
|
||||
case ERROR:
|
||||
throw new ServalDFailureException("received rhizome_bundle_status_code=ERROR(-1) from " + conn.getURL());
|
||||
case NEW:
|
||||
case SAME:
|
||||
case DUPLICATE:
|
||||
case OLD:
|
||||
case NO_ROOM: {
|
||||
if (!conn.getContentType().equals("rhizome-manifest/text"))
|
||||
throw new ServalDInterfaceException("unexpected HTTP Content-Type: " + conn.getContentType());
|
||||
RhizomeManifest returned_manifest = RhizomeManifest.fromTextFormat(status.input_stream);
|
||||
BundleExtra extra = bundleExtraFromHeaders(conn);
|
||||
return new RhizomeInsertBundle(status.bundle_status_code, returned_manifest, extra.rowId, extra.insertTime, extra.author, extra.secret);
|
||||
}
|
||||
case INVALID:
|
||||
throw new RhizomeInvalidManifestException(conn.getURL());
|
||||
case FAKE:
|
||||
throw new RhizomeFakeManifestException(conn.getURL());
|
||||
case INCONSISTENT:
|
||||
throw new RhizomeInconsistencyException(conn.getURL());
|
||||
case READONLY:
|
||||
throw new RhizomeReadOnlyException(conn.getURL());
|
||||
}
|
||||
break;
|
||||
case TOO_BIG:
|
||||
case EVICTED:
|
||||
dumpStatus(status, System.err);
|
||||
return null;
|
||||
case WRONG_SIZE:
|
||||
case WRONG_HASH:
|
||||
dumpStatus(status, System.err);
|
||||
throw new RhizomeInconsistencyException(conn.getURL());
|
||||
case CRYPTO_FAIL:
|
||||
dumpStatus(status, System.err);
|
||||
throw new RhizomeEncryptionException(conn.getURL());
|
||||
}
|
||||
}
|
||||
catch (RhizomeManifestParseException e) {
|
||||
throw new ServalDInterfaceException("malformed manifest from daemon", e);
|
||||
}
|
||||
finally {
|
||||
if (status.input_stream != null)
|
||||
status.input_stream.close();
|
||||
}
|
||||
dumpStatus(status, System.err);
|
||||
throw unexpectedResponse(status);
|
||||
}
|
||||
|
||||
private static void dumpHeaders(HttpURLConnection conn, PrintStream out)
|
||||
@ -376,7 +487,8 @@ public class RhizomeCommon
|
||||
}
|
||||
|
||||
private static class BundleExtra {
|
||||
public long insertTime;
|
||||
public Long rowId;
|
||||
public Long insertTime;
|
||||
public SubscriberId author;
|
||||
public BundleSecret secret;
|
||||
}
|
||||
@ -384,15 +496,35 @@ public class RhizomeCommon
|
||||
private static BundleExtra bundleExtraFromHeaders(HttpURLConnection conn) throws ServalDInterfaceException
|
||||
{
|
||||
BundleExtra extra = new BundleExtra();
|
||||
extra.insertTime = headerUnsignedLong(conn, "Serval-Rhizome-Bundle-Inserttime");
|
||||
extra.author = header(conn, "Serval-Rhizome-Bundle-Author", SubscriberId.class);
|
||||
extra.secret = header(conn, "Serval-Rhizome-Bundle-Secret", BundleSecret.class);
|
||||
extra.rowId = headerUnsignedLongOrNull(conn, "Serval-Rhizome-Bundle-Rowid");
|
||||
extra.insertTime = headerUnsignedLongOrNull(conn, "Serval-Rhizome-Bundle-Inserttime");
|
||||
extra.author = headerOrNull(conn, "Serval-Rhizome-Bundle-Author", SubscriberId.class);
|
||||
extra.secret = headerOrNull(conn, "Serval-Rhizome-Bundle-Secret", BundleSecret.class);
|
||||
return extra;
|
||||
}
|
||||
|
||||
private static String quoteString(String unquoted)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
private static String headerStringOrNull(HttpURLConnection conn, String header) throws ServalDInterfaceException
|
||||
{
|
||||
return conn.getHeaderField(header);
|
||||
}
|
||||
|
||||
private static String headerString(HttpURLConnection conn, String header) throws ServalDInterfaceException
|
||||
{
|
||||
String str = conn.getHeaderField(header);
|
||||
String str = headerStringOrNull(conn, header);
|
||||
if (str == null)
|
||||
throw new ServalDInterfaceException("missing header field: " + header);
|
||||
return str;
|
||||
|
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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.net.URL;
|
||||
|
||||
/**
|
||||
* Thrown when a Rhizome API method is asked to encrypt a payload without possessing the necessary
|
||||
* author or sender secret (not in keyring, or identity not unlocked) and without possessing the
|
||||
* bundle secret.
|
||||
*
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
public class RhizomeEncryptionException extends RhizomeException
|
||||
{
|
||||
public RhizomeEncryptionException(URL url) {
|
||||
super("cannot encrypt payload", url);
|
||||
}
|
||||
|
||||
}
|
@ -32,6 +32,11 @@ public abstract class RhizomeException extends Exception
|
||||
{
|
||||
public final URL url;
|
||||
|
||||
public RhizomeException(String message) {
|
||||
super(message);
|
||||
this.url = null;
|
||||
}
|
||||
|
||||
public RhizomeException(String message, URL url) {
|
||||
super(message + "; " + url);
|
||||
this.url = url;
|
||||
|
@ -0,0 +1,267 @@
|
||||
/**
|
||||
* 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.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
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 RhizomeIncompleteManifest {
|
||||
|
||||
public BundleId id;
|
||||
public Long version;
|
||||
public Long filesize;
|
||||
public FileHash filehash;
|
||||
public SubscriberId sender;
|
||||
public SubscriberId recipient;
|
||||
public BundleKey BK;
|
||||
public Long tail;
|
||||
public Integer crypt;
|
||||
public Long date;
|
||||
public String service;
|
||||
public String name;
|
||||
private HashMap<String,String> extraFields;
|
||||
|
||||
public RhizomeIncompleteManifest()
|
||||
{
|
||||
this.extraFields = new HashMap<String,String>();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public RhizomeIncompleteManifest(RhizomeManifest m)
|
||||
{
|
||||
this.id = m.id;
|
||||
this.version = m.version;
|
||||
this.filesize = m.filesize;
|
||||
this.filehash = m.filehash;
|
||||
this.sender = m.sender;
|
||||
this.recipient = m.recipient;
|
||||
this.BK = m.BK;
|
||||
this.crypt = m.crypt;
|
||||
this.tail = m.tail;
|
||||
this.date = m.date;
|
||||
this.service = m.service;
|
||||
this.name = m.name;
|
||||
this.extraFields = (HashMap<String,String>) m.extraFields.clone(); // unchecked cast
|
||||
}
|
||||
|
||||
/** Return the Rhizome manifest in its text format representation.
|
||||
*
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
public void toTextFormat(OutputStream os) throws IOException
|
||||
{
|
||||
OutputStreamWriter osw = new OutputStreamWriter(os, "US-ASCII");
|
||||
if (id != null)
|
||||
osw.write("id=" + id.toHex() + "\n");
|
||||
if (version != null)
|
||||
osw.write("version=" + version + "\n");
|
||||
if (filesize != null)
|
||||
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();
|
||||
}
|
||||
|
||||
/** Construct a Rhizome manifest from its text format representation.
|
||||
*
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
public static RhizomeIncompleteManifest fromTextFormat(byte[] bytes) throws RhizomeManifestParseException
|
||||
{
|
||||
RhizomeIncompleteManifest m = new RhizomeIncompleteManifest();
|
||||
try {
|
||||
m.parseTextFormat(new ByteArrayInputStream(bytes, 0, bytes.length));
|
||||
}
|
||||
catch (IOException e) {
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/** Construct a Rhizome manifest from its text format representation.
|
||||
*
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
public static RhizomeIncompleteManifest fromTextFormat(byte[] bytes, int off, int len) throws RhizomeManifestParseException
|
||||
{
|
||||
RhizomeIncompleteManifest m = new RhizomeIncompleteManifest();
|
||||
try {
|
||||
m.parseTextFormat(new ByteArrayInputStream(bytes, off, len));
|
||||
}
|
||||
catch (IOException e) {
|
||||
}
|
||||
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 RhizomeIncompleteManifest fromTextFormat(InputStream in) throws IOException, RhizomeManifestParseException
|
||||
{
|
||||
RhizomeIncompleteManifest m = new RhizomeIncompleteManifest();
|
||||
m.parseTextFormat(in);
|
||||
return m;
|
||||
}
|
||||
|
||||
/** Fill in manifest fields from a text format representation.
|
||||
*
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
public void parseTextFormat(InputStream in) throws IOException, RhizomeManifestParseException
|
||||
{
|
||||
try {
|
||||
InputStreamReader inr = new InputStreamReader(in, "US-ASCII");
|
||||
int pos = 0;
|
||||
int lnum = 1;
|
||||
int eq = -1;
|
||||
StringBuilder line = new StringBuilder();
|
||||
while (true) {
|
||||
int c = inr.read();
|
||||
if (c != -1 && c != '\n') {
|
||||
if (eq == -1 && c == '=')
|
||||
eq = line.length();
|
||||
line.append((char)c);
|
||||
}
|
||||
else if (line.length() == 0)
|
||||
break;
|
||||
else if (eq < 1)
|
||||
throw new RhizomeManifestParseException("malformed (missing '=') at line " + lnum + ": " + line);
|
||||
else {
|
||||
String fieldName = line.substring(0, eq);
|
||||
String fieldValue = line.substring(eq + 1);
|
||||
if (!isFieldNameFirstChar(fieldName.charAt(0)))
|
||||
throw new RhizomeManifestParseException("invalid field name at line " + lnum + ": " + line);
|
||||
for (int i = 1; i < fieldName.length(); ++i)
|
||||
if (!isFieldNameChar(fieldName.charAt(i)))
|
||||
throw new RhizomeManifestParseException("invalid field name at line " + lnum + ": " + line);
|
||||
try {
|
||||
if (fieldName.equals("id"))
|
||||
this.id = parseField(this.id, new BundleId(fieldValue));
|
||||
else if (fieldName.equals("version"))
|
||||
this.version = parseField(this.version, parseUnsignedLong(fieldValue));
|
||||
else if (fieldName.equals("filesize"))
|
||||
this.filesize = parseField(this.filesize, parseUnsignedLong(fieldValue));
|
||||
else if (fieldName.equals("filehash"))
|
||||
this.filehash = parseField(this.filehash, new FileHash(fieldValue));
|
||||
else if (fieldName.equals("sender"))
|
||||
this.sender = parseField(this.sender, new SubscriberId(fieldValue));
|
||||
else if (fieldName.equals("recipient"))
|
||||
this.recipient = parseField(this.recipient, new SubscriberId(fieldValue));
|
||||
else if (fieldName.equals("BK"))
|
||||
this.BK = parseField(this.BK, new BundleKey(fieldValue));
|
||||
else if (fieldName.equals("crypt"))
|
||||
this.crypt = parseField(this.crypt, Integer.parseInt(fieldValue));
|
||||
else if (fieldName.equals("tail"))
|
||||
this.tail = parseField(this.tail, parseUnsignedLong(fieldValue));
|
||||
else if (fieldName.equals("date"))
|
||||
this.date = parseField(this.date, parseUnsignedLong(fieldValue));
|
||||
else if (fieldName.equals("service"))
|
||||
this.service = parseField(this.service, fieldValue);
|
||||
else if (fieldName.equals("name"))
|
||||
this.name = parseField(this.name, fieldValue);
|
||||
else if (this.extraFields.containsKey(fieldName))
|
||||
throw new RhizomeManifestParseException("duplicate field");
|
||||
else
|
||||
this.extraFields.put(fieldName, fieldValue);
|
||||
}
|
||||
catch (RhizomeManifestParseException e) {
|
||||
throw new RhizomeManifestParseException(e.getMessage() + " at line " + lnum + ": " + line);
|
||||
}
|
||||
catch (AbstractId.InvalidHexException e) {
|
||||
throw new RhizomeManifestParseException("invalid value at line " + lnum + ": " + line, e);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
throw new RhizomeManifestParseException("invalid value at line " + lnum + ": " + line, e);
|
||||
}
|
||||
line.setLength(0);
|
||||
eq = -1;
|
||||
++lnum;
|
||||
}
|
||||
}
|
||||
if (line.length() > 0)
|
||||
throw new RhizomeManifestParseException("malformed (missing newline) at line " + lnum + ": " + line);
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
throw new RhizomeManifestParseException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static private <T> T parseField(T currentValue, T newValue) throws RhizomeManifestParseException
|
||||
{
|
||||
if (currentValue == null)
|
||||
return newValue;
|
||||
if (!currentValue.equals(newValue))
|
||||
throw new RhizomeManifestParseException("duplicate field");
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
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,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;
|
||||
|
||||
import org.servalproject.servaldna.SubscriberId;
|
||||
import org.servalproject.servaldna.BundleSecret;
|
||||
import org.servalproject.servaldna.ServalDInterfaceException;
|
||||
|
||||
public class RhizomeInsertBundle extends RhizomeManifestBundle {
|
||||
|
||||
public final RhizomeBundleStatus status;
|
||||
|
||||
protected RhizomeInsertBundle(RhizomeBundleStatus status,
|
||||
RhizomeManifest manifest,
|
||||
Long rowId,
|
||||
Long insertTime,
|
||||
SubscriberId author,
|
||||
BundleSecret secret)
|
||||
{
|
||||
super(manifest, rowId, insertTime, author, secret);
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
}
|
@ -23,9 +23,9 @@ package org.servalproject.servaldna.rhizome;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Thrown when a Rhizome API method is passed an invalid manifest. This is not an error within the
|
||||
* Serval DNA interface, so it is not a subclass of ServalDInterfaceException. The programmer must
|
||||
* explicitly deal with it instead of just absorbing it as an interface malfunction.
|
||||
* Thrown when the Rhizome API rejects a caller-supplied manifest as invalid. This error does not
|
||||
* originate from the Serval DNA interface, so it is not a subclass of ServalDInterfaceException.
|
||||
* The programmer must deal with it, and not treat it as an interface malfunction.
|
||||
*
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
@ -35,4 +35,8 @@ public class RhizomeInvalidManifestException extends RhizomeException
|
||||
super("invalid manifest", url);
|
||||
}
|
||||
|
||||
public RhizomeInvalidManifestException(RhizomeIncompleteManifest manifest) {
|
||||
super("invalid manifest");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -39,7 +39,6 @@ public class RhizomeListBundle {
|
||||
long insertTime,
|
||||
SubscriberId author,
|
||||
int fromHere)
|
||||
|
||||
{
|
||||
this.manifest = manifest;
|
||||
this.rowNumber = rowNumber;
|
||||
|
@ -26,8 +26,10 @@ import java.util.HashSet;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import org.servalproject.servaldna.AbstractId;
|
||||
import org.servalproject.servaldna.SubscriberId;
|
||||
import org.servalproject.servaldna.BundleId;
|
||||
@ -56,7 +58,7 @@ public class RhizomeManifest {
|
||||
public final String service;
|
||||
public final String name;
|
||||
|
||||
private HashMap<String,String> extraFields;
|
||||
protected HashMap<String,String> extraFields;
|
||||
private byte[] signatureBlock;
|
||||
private byte[] textFormat;
|
||||
|
||||
@ -96,59 +98,51 @@ public class RhizomeManifest {
|
||||
this.textFormat = null;
|
||||
}
|
||||
|
||||
/** Return the Rhizome manifest in its text format representation.
|
||||
protected RhizomeManifest(RhizomeIncompleteManifest m)
|
||||
{
|
||||
this(m.id, m.version, m.filesize, m.filehash, m.sender, m.recipient, m.BK, m.crypt, m.tail, m.date, m.service, m.name);
|
||||
}
|
||||
|
||||
/** Return the Rhizome manifest in its text format representation, with the signature block at
|
||||
* the end if present.
|
||||
*
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
public byte[] toTextFormat() throws RhizomeManifestSizeException
|
||||
{
|
||||
if (textFormat == null) {
|
||||
if (this.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();
|
||||
toTextFormat(os);
|
||||
os.close();
|
||||
if (os.size() > TEXT_FORMAT_MAX_SIZE)
|
||||
throw new RhizomeManifestSizeException("manifest text format overflow", os.size(), TEXT_FORMAT_MAX_SIZE);
|
||||
textFormat = os.toByteArray();
|
||||
this.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);
|
||||
byte[] ret = new byte[this.textFormat.length];
|
||||
System.arraycopy(this.textFormat, 0, ret, 0, ret.length);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Write the Rhizome manifest in its text format representation to the given output stream,
|
||||
* with the signature block at the end if present.
|
||||
*
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
public void toTextFormat(OutputStream os) throws IOException
|
||||
{
|
||||
new RhizomeIncompleteManifest(this).toTextFormat(os);
|
||||
if (this.signatureBlock != null) {
|
||||
os.write(0);
|
||||
os.write(this.signatureBlock);
|
||||
}
|
||||
}
|
||||
|
||||
/** Construct a Rhizome manifest from its text format representation.
|
||||
*
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
@ -158,7 +152,8 @@ public class RhizomeManifest {
|
||||
return fromTextFormat(bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
/** Construct a Rhizome manifest from its text format representation.
|
||||
/** Construct a complete Rhizome manifest from its text format representation, including a
|
||||
* trailing signature block.
|
||||
*
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
@ -175,95 +170,23 @@ public class RhizomeManifest {
|
||||
break;
|
||||
}
|
||||
}
|
||||
String text;
|
||||
RhizomeIncompleteManifest im = new RhizomeIncompleteManifest();
|
||||
try {
|
||||
text = new String(bytes, off, proplen, "US-ASCII");
|
||||
im.parseTextFormat(new ByteArrayInputStream(bytes, off, proplen));
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
throw new RhizomeManifestParseException(e);
|
||||
catch (IOException 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)
|
||||
if (im.id == null)
|
||||
throw new RhizomeManifestParseException("missing 'id' field");
|
||||
if (version == null)
|
||||
if (im.version == null)
|
||||
throw new RhizomeManifestParseException("missing 'version' field");
|
||||
if (filesize == null)
|
||||
if (im.filesize == null)
|
||||
throw new RhizomeManifestParseException("missing 'filesize' field");
|
||||
if (filesize != 0 && filehash == null)
|
||||
if (im.filesize != 0 && im.filehash == null)
|
||||
throw new RhizomeManifestParseException("missing 'filehash' field");
|
||||
else if (filesize == 0 && filehash != null)
|
||||
else if (im.filesize == 0 && im.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;
|
||||
RhizomeManifest m = new RhizomeManifest(im);
|
||||
m.signatureBlock = sigblock;
|
||||
m.textFormat = new byte[len];
|
||||
System.arraycopy(bytes, off, m.textFormat, 0, m.textFormat.length);
|
||||
|
@ -26,18 +26,20 @@ import org.servalproject.servaldna.ServalDInterfaceException;
|
||||
|
||||
public class RhizomeManifestBundle {
|
||||
|
||||
public final long insertTime;
|
||||
public final Long insertTime;
|
||||
public final Long rowId;
|
||||
public final SubscriberId author;
|
||||
public final BundleSecret secret;
|
||||
public final RhizomeManifest manifest;
|
||||
|
||||
protected RhizomeManifestBundle(RhizomeManifest manifest,
|
||||
long insertTime,
|
||||
Long rowId,
|
||||
Long insertTime,
|
||||
SubscriberId author,
|
||||
BundleSecret secret)
|
||||
|
||||
{
|
||||
this.manifest = manifest;
|
||||
this.rowId = rowId;
|
||||
this.insertTime = insertTime;
|
||||
this.author = author;
|
||||
this.secret = secret;
|
||||
|
@ -29,19 +29,21 @@ public class RhizomePayloadBundle {
|
||||
|
||||
public final RhizomeManifest manifest;
|
||||
public final InputStream payloadInputStream;
|
||||
public final long insertTime;
|
||||
public final Long rowId;
|
||||
public final Long insertTime;
|
||||
public final SubscriberId author;
|
||||
public final BundleSecret secret;
|
||||
|
||||
protected RhizomePayloadBundle(RhizomeManifest manifest,
|
||||
InputStream payloadInputStream,
|
||||
long insertTime,
|
||||
Long rowId,
|
||||
Long insertTime,
|
||||
SubscriberId author,
|
||||
BundleSecret secret)
|
||||
|
||||
{
|
||||
this.payloadInputStream = payloadInputStream;
|
||||
this.manifest = manifest;
|
||||
this.rowId = rowId;
|
||||
this.insertTime = insertTime;
|
||||
this.author = author;
|
||||
this.secret = secret;
|
||||
|
@ -29,19 +29,21 @@ public class RhizomePayloadRawBundle {
|
||||
|
||||
public final RhizomeManifest manifest;
|
||||
public final InputStream rawPayloadInputStream;
|
||||
public final long insertTime;
|
||||
public final Long rowId;
|
||||
public final Long insertTime;
|
||||
public final SubscriberId author;
|
||||
public final BundleSecret secret;
|
||||
|
||||
protected RhizomePayloadRawBundle(RhizomeManifest manifest,
|
||||
InputStream rawPayloadInputStream,
|
||||
long insertTime,
|
||||
Long rowId,
|
||||
Long insertTime,
|
||||
SubscriberId author,
|
||||
BundleSecret secret)
|
||||
|
||||
{
|
||||
this.rawPayloadInputStream = rawPayloadInputStream;
|
||||
this.manifest = manifest;
|
||||
this.rowId = rowId;
|
||||
this.insertTime = insertTime;
|
||||
this.author = author;
|
||||
this.secret = secret;
|
||||
|
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 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.net.URL;
|
||||
|
||||
/**
|
||||
* Thrown when a Rhizome API method is passed a manifest which is inconsistent with a supplied
|
||||
* payload. I.e., filesize or filehash does not match.
|
||||
*
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
public class RhizomeReadOnlyException extends RhizomeException
|
||||
{
|
||||
public RhizomeReadOnlyException(URL url) {
|
||||
super("bundle cannot be modified", url);
|
||||
}
|
||||
|
||||
}
|
@ -20,36 +20,42 @@
|
||||
|
||||
package org.servalproject.test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import org.servalproject.servaldna.ServalDClient;
|
||||
import org.servalproject.servaldna.ServalDInterfaceException;
|
||||
import org.servalproject.servaldna.ServerControl;
|
||||
import org.servalproject.servaldna.BundleId;
|
||||
import org.servalproject.servaldna.SubscriberId;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeManifest;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeIncompleteManifest;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeListBundle;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeBundleList;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeManifestBundle;
|
||||
import org.servalproject.servaldna.rhizome.RhizomePayloadRawBundle;
|
||||
import org.servalproject.servaldna.rhizome.RhizomePayloadBundle;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeInsertBundle;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeException;
|
||||
import org.servalproject.servaldna.rhizome.RhizomeManifestParseException;
|
||||
|
||||
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;
|
||||
return "id=" + manifest.id
|
||||
+ sep + "version=" + manifest.version
|
||||
+ sep + "filesize=" + manifest.filesize
|
||||
+ (manifest.filesize != 0 ? sep + "filehash=" + manifest.filehash : "")
|
||||
+ (manifest.sender != null ? sep + "sender=" + manifest.sender : "")
|
||||
+ (manifest.recipient != null ? sep + "recipient=" + manifest.recipient : "")
|
||||
+ (manifest.date != null ? sep + "date=" + manifest.date : "")
|
||||
+ (manifest.service != null ? sep + "service=" + manifest.service : "")
|
||||
+ (manifest.BK != null ? sep + "BK=" + manifest.BK : "")
|
||||
+ (manifest.name != null ? sep + "name=" + manifest.name : "");
|
||||
}
|
||||
|
||||
static void rhizome_list() throws ServalDInterfaceException, IOException, InterruptedException
|
||||
@ -61,8 +67,8 @@ public class Rhizome {
|
||||
RhizomeListBundle bundle;
|
||||
while ((bundle = list.nextBundle()) != null) {
|
||||
System.out.println(
|
||||
"_rowId=" + bundle.rowId +
|
||||
", _token=" + bundle.token +
|
||||
"_token=" + bundle.token +
|
||||
", _rowId=" + bundle.rowId +
|
||||
", _insertTime=" + bundle.insertTime +
|
||||
", _author=" + bundle.author +
|
||||
", _fromHere=" + bundle.fromHere +
|
||||
@ -89,9 +95,10 @@ public class Rhizome {
|
||||
System.out.println("not found");
|
||||
else {
|
||||
System.out.println(
|
||||
"_insertTime=" + bundle.insertTime + "\n" +
|
||||
"_author=" + bundle.author + "\n" +
|
||||
"_secret=" + bundle.secret + "\n" +
|
||||
(bundle.rowId == null ? "" : "_rowId=" + bundle.rowId + "\n") +
|
||||
(bundle.insertTime == null ? "" : "_insertTime=" + bundle.insertTime + "\n") +
|
||||
(bundle.author == null ? "" : "_author=" + bundle.author + "\n") +
|
||||
(bundle.secret == null ? "" : "_secret=" + bundle.secret + "\n") +
|
||||
manifestFields(bundle.manifest, "\n") + "\n"
|
||||
);
|
||||
FileOutputStream out = new FileOutputStream(dstpath);
|
||||
@ -128,9 +135,10 @@ public class Rhizome {
|
||||
out = null;
|
||||
}
|
||||
System.out.println(
|
||||
"_insertTime=" + bundle.insertTime + "\n" +
|
||||
"_author=" + bundle.author + "\n" +
|
||||
"_secret=" + bundle.secret + "\n" +
|
||||
(bundle.rowId == null ? "" : "_rowId=" + bundle.rowId + "\n") +
|
||||
(bundle.insertTime == null ? "" : "_insertTime=" + bundle.insertTime + "\n") +
|
||||
(bundle.author == null ? "" : "_author=" + bundle.author + "\n") +
|
||||
(bundle.secret == null ? "" : "_secret=" + bundle.secret + "\n") +
|
||||
manifestFields(bundle.manifest, "\n") + "\n"
|
||||
);
|
||||
}
|
||||
@ -168,9 +176,10 @@ public class Rhizome {
|
||||
out = null;
|
||||
}
|
||||
System.out.println(
|
||||
"_insertTime=" + bundle.insertTime + "\n" +
|
||||
"_author=" + bundle.author + "\n" +
|
||||
"_secret=" + bundle.secret + "\n" +
|
||||
(bundle.rowId == null ? "" : "_rowId=" + bundle.rowId + "\n") +
|
||||
(bundle.insertTime == null ? "" : "_insertTime=" + bundle.insertTime + "\n") +
|
||||
(bundle.author == null ? "" : "_author=" + bundle.author + "\n") +
|
||||
(bundle.secret == null ? "" : "_secret=" + bundle.secret + "\n") +
|
||||
manifestFields(bundle.manifest, "\n") + "\n"
|
||||
);
|
||||
}
|
||||
@ -185,6 +194,43 @@ public class Rhizome {
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
static void rhizome_insert(String author, String manifestpath, String payloadPath, String manifestoutpath, String payloadName)
|
||||
throws ServalDInterfaceException, IOException, InterruptedException, SubscriberId.InvalidHexException
|
||||
{
|
||||
ServalDClient client = new ServerControl().getRestfulClient();
|
||||
try {
|
||||
RhizomeIncompleteManifest manifest = RhizomeIncompleteManifest.fromTextFormat(new FileInputStream(manifestpath));
|
||||
RhizomeInsertBundle bundle;
|
||||
SubscriberId authorSid = author == null || author.length() == 0 ? null : new SubscriberId(author);
|
||||
if (payloadName == null || payloadName.length() == 0)
|
||||
payloadName = new File(payloadPath).getName();
|
||||
if (payloadPath == null || payloadPath.length() == 0)
|
||||
bundle = client.rhizomeInsert(authorSid, manifest);
|
||||
else
|
||||
bundle = client.rhizomeInsert(authorSid, manifest, new FileInputStream(payloadPath), payloadName);
|
||||
System.out.println(
|
||||
"_status=" + bundle.status + "\n" +
|
||||
(bundle.rowId == null ? "" : "_rowId=" + bundle.rowId + "\n") +
|
||||
(bundle.insertTime == null ? "" : "_insertTime=" + bundle.insertTime + "\n") +
|
||||
(bundle.author == null ? "" : "_author=" + bundle.author + "\n") +
|
||||
(bundle.secret == null ? "" : "_secret=" + bundle.secret + "\n") +
|
||||
manifestFields(bundle.manifest, "\n") + "\n"
|
||||
);
|
||||
if (manifestoutpath != null && manifestoutpath.length() != 0) {
|
||||
FileOutputStream out = new FileOutputStream(manifestoutpath);
|
||||
out.write(bundle.manifestText());
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
catch (RhizomeManifestParseException e) {
|
||||
System.out.println(e.toString());
|
||||
}
|
||||
catch (RhizomeException e) {
|
||||
System.out.println(e.toString());
|
||||
}
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
public static void main(String... args)
|
||||
{
|
||||
if (args.length < 1)
|
||||
@ -199,6 +245,13 @@ public class Rhizome {
|
||||
rhizome_payload_raw(new BundleId(args[1]), args[2]);
|
||||
else if (methodName.equals("rhizome-payload-decrypted"))
|
||||
rhizome_payload_decrypted(new BundleId(args[1]), args[2]);
|
||||
else if (methodName.equals("rhizome-insert"))
|
||||
rhizome_insert( args[1], // author SID
|
||||
args[2], // manifest path
|
||||
args.length > 3 ? args[3] : null, // payload path
|
||||
args.length > 4 ? args[4] : null, // manifest out path
|
||||
args.length > 5 ? args[5] : null // payload name
|
||||
);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
|
@ -376,16 +376,20 @@ static int insert_make_manifest(httpd_request *r)
|
||||
r->manifest->manifest_all_bytes = r->u.insert.manifest.length;
|
||||
int n = rhizome_manifest_parse(r->manifest);
|
||||
switch (n) {
|
||||
case -1:
|
||||
break;
|
||||
case 0:
|
||||
if (!r->manifest->malformed)
|
||||
return 0;
|
||||
// fall through
|
||||
case 1:
|
||||
rhizome_manifest_free(r->manifest);
|
||||
r->manifest = NULL;
|
||||
r->bundle_status = RHIZOME_BUNDLE_STATUS_INVALID;
|
||||
return http_request_rhizome_response(r, 403, "Malformed manifest", NULL);
|
||||
default:
|
||||
WHYF("rhizome_manifest_parse() returned %d", n);
|
||||
// fall through
|
||||
case -1:
|
||||
r->bundle_status = RHIZOME_BUNDLE_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -516,6 +520,8 @@ static int insert_mime_part_end(struct http_request *hr)
|
||||
}
|
||||
else if (r->u.insert.current_part == PART_MANIFEST) {
|
||||
r->u.insert.received_manifest = 1;
|
||||
if (config.debug.rhizome)
|
||||
DEBUGF("received %s = %s", PART_MANIFEST, alloca_toprint(-1, r->u.insert.manifest.buffer, r->u.insert.manifest.length));
|
||||
int result = insert_make_manifest(r);
|
||||
if (result)
|
||||
return result;
|
||||
@ -564,14 +570,14 @@ static int restful_rhizome_insert_end(struct http_request *hr)
|
||||
assert(r->manifest != NULL);
|
||||
assert(r->u.insert.write.file_length != RHIZOME_SIZE_UNSET);
|
||||
int status_valid = 0;
|
||||
if (config.debug.rhizome)
|
||||
DEBUGF("r->payload_status=%d", r->payload_status);
|
||||
switch (r->payload_status) {
|
||||
case RHIZOME_PAYLOAD_STATUS_NEW:
|
||||
status_valid = 1;
|
||||
if (r->manifest->filesize == RHIZOME_SIZE_UNSET)
|
||||
rhizome_manifest_set_filesize(r->manifest, r->u.insert.write.file_length);
|
||||
// fall through
|
||||
case RHIZOME_PAYLOAD_STATUS_STORED:
|
||||
status_valid = 1;
|
||||
// TODO: check that stored hash matches received payload's hash
|
||||
// fall through
|
||||
case RHIZOME_PAYLOAD_STATUS_EMPTY:
|
||||
@ -579,18 +585,25 @@ static int restful_rhizome_insert_end(struct http_request *hr)
|
||||
assert(r->manifest->filesize != RHIZOME_SIZE_UNSET);
|
||||
if (r->u.insert.payload_size == r->manifest->filesize)
|
||||
break;
|
||||
// fall through
|
||||
case RHIZOME_PAYLOAD_STATUS_WRONG_SIZE:
|
||||
r->payload_status = RHIZOME_PAYLOAD_STATUS_WRONG_SIZE;
|
||||
status_valid = 1;
|
||||
r->bundle_status = RHIZOME_BUNDLE_STATUS_INCONSISTENT;
|
||||
{
|
||||
strbuf msg = strbuf_alloca(200);
|
||||
strbuf_sprintf(msg, "Payload size (%"PRIu64") contradicts manifest (filesize=%"PRIu64")", r->u.insert.payload_size, r->manifest->filesize);
|
||||
return http_request_rhizome_response(r, 403, NULL, strbuf_str(msg));
|
||||
}
|
||||
case RHIZOME_PAYLOAD_STATUS_WRONG_HASH:
|
||||
r->bundle_status = RHIZOME_BUNDLE_STATUS_INCONSISTENT;
|
||||
return http_request_rhizome_response(r, 403, NULL, NULL);
|
||||
case RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL:
|
||||
r->bundle_status = RHIZOME_BUNDLE_STATUS_READONLY;
|
||||
return http_request_rhizome_response(r, 403, "Missing bundle secret", NULL);
|
||||
case RHIZOME_PAYLOAD_STATUS_TOO_BIG:
|
||||
case RHIZOME_PAYLOAD_STATUS_EVICTED:
|
||||
case RHIZOME_PAYLOAD_STATUS_WRONG_HASH:
|
||||
case RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL:
|
||||
r->bundle_status = RHIZOME_BUNDLE_STATUS_NO_ROOM;
|
||||
// fall through
|
||||
case RHIZOME_PAYLOAD_STATUS_ERROR:
|
||||
return http_request_rhizome_response(r, 403, NULL, NULL);
|
||||
}
|
||||
@ -605,17 +618,24 @@ static int restful_rhizome_insert_end(struct http_request *hr)
|
||||
else
|
||||
assert(cmp_rhizome_filehash_t(&r->u.insert.write.id, &r->manifest->filehash) == 0);
|
||||
}
|
||||
if (!rhizome_manifest_validate(r->manifest) || r->manifest->malformed) {
|
||||
http_request_simple_response(&r->http, 403, "Manifest is malformed");
|
||||
return 403;
|
||||
const char *invalid_reason = rhizome_manifest_validate_reason(r->manifest);
|
||||
if (invalid_reason) {
|
||||
r->bundle_status = RHIZOME_BUNDLE_STATUS_INVALID;
|
||||
return http_request_rhizome_response(r, 403, invalid_reason, NULL);
|
||||
}
|
||||
if (r->manifest->malformed) {
|
||||
r->bundle_status = RHIZOME_BUNDLE_STATUS_INVALID;
|
||||
return http_request_rhizome_response(r, 403, r->manifest->malformed, NULL);
|
||||
}
|
||||
if (!r->manifest->haveSecret) {
|
||||
http_request_simple_response(&r->http, 403, "Missing bundle secret");
|
||||
return 403;
|
||||
r->bundle_status = RHIZOME_BUNDLE_STATUS_READONLY;
|
||||
return http_request_rhizome_response(r, 403, "Missing bundle secret", NULL);
|
||||
}
|
||||
rhizome_manifest *mout = NULL;
|
||||
r->bundle_status = rhizome_manifest_finalise(r->manifest, &mout, !r->u.insert.force_new);
|
||||
int result = 500;
|
||||
if (config.debug.rhizome)
|
||||
DEBUGF("r->bundle_status=%d", r->bundle_status);
|
||||
switch (r->bundle_status) {
|
||||
case RHIZOME_BUNDLE_STATUS_NEW:
|
||||
if (mout && mout != r->manifest)
|
||||
@ -639,6 +659,8 @@ static int restful_rhizome_insert_end(struct http_request *hr)
|
||||
case RHIZOME_BUNDLE_STATUS_ERROR:
|
||||
if (mout && mout != r->manifest)
|
||||
rhizome_manifest_free(mout);
|
||||
rhizome_manifest_free(r->manifest);
|
||||
r->manifest = NULL;
|
||||
return http_request_rhizome_response(r, 0, NULL, NULL);
|
||||
}
|
||||
if (result == 500)
|
||||
|
@ -196,7 +196,7 @@ unpack_manifest_for_grep() {
|
||||
re_name=$(escape_grep_basic "${filename##*/}")
|
||||
if [ -e "$manifestname" ]; then
|
||||
re_filesize=$($SED -n -e '/^filesize=/s///p' "$manifestname")
|
||||
if [ "$filesize" = 0 ]; then
|
||||
if [ "$re_filesize" = 0 ]; then
|
||||
re_filehash=
|
||||
else
|
||||
re_filehash=$($SED -n -e '/^filehash=/s///p' "$manifestname")
|
||||
@ -243,10 +243,18 @@ extract_stdout_version() {
|
||||
extract_stdout_keyvalue "$1" version "$rexp_version"
|
||||
}
|
||||
|
||||
extract_stdout_author_optional() {
|
||||
extract_stdout_keyvalue_optional "$1" .author "$rexp_author"
|
||||
}
|
||||
|
||||
extract_stdout_author() {
|
||||
extract_stdout_keyvalue "$1" .author "$rexp_author"
|
||||
}
|
||||
|
||||
extract_stdout_secret_optional() {
|
||||
extract_stdout_keyvalue_optional "$1" .secret "$rexp_bundlesecret"
|
||||
}
|
||||
|
||||
extract_stdout_secret() {
|
||||
extract_stdout_keyvalue "$1" .secret "$rexp_bundlesecret"
|
||||
}
|
||||
|
@ -30,12 +30,18 @@ setup() {
|
||||
set_instance +A
|
||||
executeOk_servald config \
|
||||
set log.console.level debug \
|
||||
set debug.httpd on
|
||||
set debug.httpd on \
|
||||
set debug.rhizome on \
|
||||
set debug.rhizome_manifest on
|
||||
set_extra_config
|
||||
create_identities 4
|
||||
start_servald_server
|
||||
}
|
||||
|
||||
set_extra_config() {
|
||||
:
|
||||
}
|
||||
|
||||
teardown() {
|
||||
stop_all_servald_servers
|
||||
kill_all_servald_processes
|
||||
@ -165,6 +171,7 @@ test_RhizomeManifest() {
|
||||
executeJavaOk org.servalproject.test.Rhizome rhizome-manifest "${BID[$n]}" bundle$n.rhm
|
||||
tfw_cat --stdout --stderr
|
||||
assert_metadata $n
|
||||
ls -l file$n.manifest bundle$n.rhm
|
||||
tfw_cat -v file$n.manifest -v bundle$n.rhm
|
||||
assert diff file$n.manifest bundle$n.rhm
|
||||
done
|
||||
@ -275,4 +282,91 @@ test_RhizomePayloadDecryptedForeign() {
|
||||
assertStdoutGrep RhizomeDecryptionException
|
||||
}
|
||||
|
||||
doc_RhizomeInsert="Java API insert new Rhizome bundles"
|
||||
setup_RhizomeInsert() {
|
||||
setup
|
||||
for n in 1 2 3 4; do
|
||||
create_file file$n $((1000 + $n))
|
||||
create_file nfile$n $((1100 + $n))
|
||||
payload_filename[$n]=
|
||||
eval author[$n]=\$SIDA$n
|
||||
service[$n]=file
|
||||
done
|
||||
name[1]=elvis
|
||||
echo "name=elvis" >manifest1
|
||||
name[2]=file2
|
||||
echo "crypt=1" >manifest2
|
||||
name[3]=fintlewoodlewix
|
||||
payload_filename[3]=fintlewoodlewix
|
||||
>manifest3
|
||||
name[4]=
|
||||
author[4]=
|
||||
service[4]=wah
|
||||
echo -e "service=wah\ncrypt=0" >manifest4
|
||||
}
|
||||
test_RhizomeInsert() {
|
||||
for n in 1 2 3 4; do
|
||||
executeJavaOk org.servalproject.test.Rhizome rhizome-insert "${author[$n]}" manifest$n file$n file$n.manifest "${payload_filename[$n]}"
|
||||
tfw_cat --stdout --stderr -v file$n.manifest
|
||||
assertStdoutGrep '^_status=NEW$'
|
||||
replayStdout >stdout-insert
|
||||
extract_manifest_id BID[$n] stdout-insert
|
||||
extract_manifest SECRET[$n] stdout-insert _secret "$rexp_bundlesecret"
|
||||
executeOk_servald rhizome extract bundle "${BID[$n]}" xfile$n.manifest xfile$n
|
||||
tfw_cat --stdout -v xfile$n.manifest
|
||||
extract_stdout_rowid ROWID[$n]
|
||||
extract_stdout_inserttime INSERTTIME[$n]
|
||||
assertGrep stdout-insert "^_rowId=${ROWID[$n]}\$"
|
||||
assertGrep stdout-insert "^_insertTime=${INSERTTIME[$n]}\$"
|
||||
if extract_stdout_author_optional AUTHOR[$n]; then
|
||||
assertGrep stdout-insert "^_author=${AUTHOR[$n]}\$"
|
||||
else
|
||||
assertGrep --matches=0 stdout-insert "^_author="
|
||||
fi
|
||||
assert diff xfile$n.manifest file$n.manifest
|
||||
assert diff file$n xfile$n
|
||||
unpack_manifest_for_grep xfile$n
|
||||
assertGrep stdout-insert "^id=$re_manifestid\$"
|
||||
assertGrep stdout-insert "^version=$re_version\$"
|
||||
assertGrep stdout-insert "^filesize=$re_filesize\$"
|
||||
if [ -n "$re_filehash" ]; then
|
||||
assertGrep stdout-insert "^filehash=$re_filehash\$"
|
||||
else
|
||||
assertGrep --matches=0 stdout-insert "^filehash="
|
||||
fi
|
||||
assertGrep stdout-insert "^date=$re_date\$"
|
||||
assertGrep stdout-insert "^service=$re_service\$"
|
||||
if [ -n "${name[$n]}" ]; then
|
||||
assertGrep stdout-insert "^name=$re_name\$"
|
||||
assert [ "$re_name" = "${name[$n]}" ]
|
||||
fi
|
||||
done
|
||||
executeOk_servald rhizome list
|
||||
assert_rhizome_list \
|
||||
--fromhere=1 \
|
||||
--author=${author[1]} file1 \
|
||||
--author=${author[2]} file2 \
|
||||
--author=${author[3]} file3 \
|
||||
--fromhere=0 \
|
||||
--author=${author[4]} file4
|
||||
for n in 1 2 3 4; do
|
||||
$SED -e '/^version=/d;/^date=/d;/^filehash=/d;/^filesize=/d;/^[^a-zA-Z]/,$d' xfile$n.manifest >nmanifest$n
|
||||
assertGrep nmanifest$n '^id='
|
||||
tfw_cat -v nmanifest$n
|
||||
executeJavaOk org.servalproject.test.Rhizome rhizome-insert '' nmanifest$n nfile$n nfile$n.manifest "nfile$n"
|
||||
tfw_cat --stdout --stderr -v nfile$n.manifest
|
||||
if [ -n "${author[$n]}" ]; then
|
||||
assertStdoutGrep '^_status=NEW$'
|
||||
assertStdoutGrep "^id=${BID[$n]}\$"
|
||||
assertStderrGrep --matches=1 "^bundle_status_code=NEW$CR\$"
|
||||
assertStderrGrep --matches=1 --ignore-case "^bundle_status_message=.*bundle new to store.*$CR\$"
|
||||
assertStderrGrep --matches=1 "^payload_status_code=NEW$CR\$"
|
||||
assertStderrGrep --matches=1 --ignore-case "^payload_status_message=.*payload new to store.*$CR\$"
|
||||
else
|
||||
assertStdoutGrep RhizomeReadOnlyException
|
||||
assertStderrGrep --ignore-case "missing bundle secret"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
runTests "$@"
|
||||
|
Loading…
x
Reference in New Issue
Block a user