serval-dna/java/org/servalproject/servaldna/ServalDCommand.java
Andrew Bettison 5191d424cb Fix broken Android build
Renamed libservald.so to libservaldaemon.so everywhere to avoid conflicts
with the "servald" module name in Android.mk, and to avoid the overly
generic name "libserval".

Include the JNI entry points in the Android build.
2016-10-20 13:28:12 +10:30

761 lines
22 KiB
Java

/**
* Copyright (C) 2014-2015 Serval Project Inc.
* Copyright (C) 2016 Flinders University
*
* 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.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class ServalDCommand
{
private ServalDCommand(){
}
static
{
System.loadLibrary("servaldaemon");
}
public static final int STATUS_ERROR = 255;
public static String toString(String[] values) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < values.length; i++) {
if (i > 0)
sb.append(' ');
sb.append(values[i]);
}
return sb.toString();
}
// copies the semantics of serval-dna's confParseBoolean
private static boolean parseBoolean(String value, boolean defaultValue) {
if (value == null || "".equals(value))
return defaultValue;
return "off".compareToIgnoreCase(value) != 0
&& "no".compareToIgnoreCase(value) != 0
&& "false".compareToIgnoreCase(value) != 0
&& "0".compareToIgnoreCase(value) != 0;
}
/**
* Low-level JNI entry point into servald command line.
*
* @param results Interface that will receive each value from the command
* @param args The words to pass on the command line (ie, argv[1]...argv[n])
* @return The servald exit status code (normally 0 indicates success)
*/
private static native int rawCommand(IJniResults results, String[] args);
public static native int server(IJniServer callback, String keyringPin, String[] entryPins);
public static native int setInstancePath(String path);
/**
* Common entry point into servald command line.
*
* @param callback
* Each result will be passed to callback.result(String)
* immediately.
* @param args
* The parameters as passed on the command line, eg: res =
* servald.command("config", "set", "debug", "peers");
* @return The servald exit status code (normally0 indicates success)
*/
public static synchronized int command(final IJniResults callback, String... args)
throws ServalDFailureException {
int ret = ServalDCommand.rawCommand(callback, args);
if (ret == STATUS_ERROR)
throw new ServalDFailureException("Command \"" + toString(args)+"\" returned an error");
return ret;
}
public static synchronized JniResult command(String... args)
throws ServalDFailureException {
JniResult result = new JniResult();
result.setCommand(args);
result.setResult(ServalDCommand.rawCommand(result, args));
return result;
}
public static class Status extends JniResult{
public int pid;
public int tries;
public String instancePath;
public String status;
public int mdpInetPort;
public int httpPort;
@Override
public void putString(String value) {
if (columnName.equals("instancepath"))
instancePath=value;
if (columnName.equals("status"))
status=value;
if (columnName.equals("mdp_inet_port"))
mdpInetPort=Integer.parseInt(value);
if (columnName.equals("http_port"))
httpPort=Integer.parseInt(value);
}
@Override
public void putLong(long value) {
if (columnName.equals("pid"))
pid = (int)value;
if (columnName.equals("tries"))
tries = (int)value;
}
@Override
public String toString() {
return "Status{" +
"pid=" + pid +
", tries=" + tries +
", instancePath='" + instancePath + '\'' +
", status='" + status + '\'' +
", mdpInetPort='" + mdpInetPort + '\'' +
", httpPort='" + httpPort + '\'' +
'}';
}
}
/** Start the servald server process if it is not already running.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
public static Status serverStart()
throws ServalDFailureException {
Status result = new Status();
result.setResult(command(result, "start"));
return result;
}
public static Status serverStart(String execPath)
throws ServalDFailureException {
Status result = new Status();
result.setResult(command(result, "start", "exec", execPath));
return result;
}
public static Status serverStop()
throws ServalDFailureException {
Status result = new Status();
result.setResult(command(result, "stop"));
return result;
}
public static Status serverStatus()
throws ServalDFailureException {
Status result = new Status();
result.setResult(command(result, "status"));
return result;
}
@Deprecated
public static class IdentityResult extends JniResult {
public String did;
public String name;
public SubscriberId subscriberId;
public Map<String, String> tags = new HashMap<String, String>();
@Override
public void putString(String value) {
if (this.columnName.equals("did"))
this.did = value;
else if (this.columnName.equals("name"))
this.name = value;
else if (this.columnName.equals("sid"))
try {
this.subscriberId = new SubscriberId(value);
} catch (AbstractId.InvalidHexException e) {
e.printStackTrace();
}
else
tags.put(columnName, value);
}
@Override
public void putBlob(byte[] value) {
if (this.columnName.equals("sid"))
try {
this.subscriberId = new SubscriberId(value);
} catch (AbstractId.InvalidBinaryException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return "IdentityResult{" +
"did='" + did + '\'' +
", name='" + name + '\'' +
", subscriberId=" + subscriberId +
'}';
}
}
@Deprecated
public static IdentityResult keyringAdd()
throws ServalDFailureException {
IdentityResult result = new IdentityResult();
command(result, "keyring", "add");
return result;
}
@Deprecated
public static IdentityResult keyringSetDidName(SubscriberId sid, String did, String name) throws ServalDFailureException
{
IdentityResult result = new IdentityResult();
command(result, "keyring","set","did", sid.toHex(), did, name);
return result;
}
@Deprecated
public static int keyringList(final AsyncResult<IdentityResult> results) throws ServalDFailureException
{
// FIXME, this is a little hacky as the number of tags is unknown so we don't have a fixed number of columns
return command(new AbstractJniResults() {
IdentityResult id = null;
long fields=0;
String columnName;
@Override
public void putBlob(byte[] value) {
}
@Override
public void setColumnName(int i, String name) {
columnName = name;
}
@Override
public void putLong(long value) {
if (columnName.equals("fields")){
fields=value;
id = new IdentityResult();
}
}
@Override
public void putString(String value) {
id.setColumnName(0, columnName);
id.putString(value);
if (--fields==0){
results.result(id);
id=null;
}
}
}, "keyring", "list", "--full");
}
@Deprecated
public static int keyringList(IJniResults results) throws ServalDFailureException{
return command(results, "keyring", "list");
}
public static IdentityResult keyringSetTag(SubscriberId sid, String tag, String value) throws ServalDFailureException {
IdentityResult result = new IdentityResult();
command(result, "keyring", "set", "tag", sid.toHex(), tag, value);
return result;
}
@Deprecated
public static IdentityResult reverseLookup(final SubscriberId sid) throws ServalDFailureException {
IdentityResult result = new IdentityResult();
command(result, "reverse", "lookup", sid.toHex());
return result;
}
public static class LookupResult extends JniResult {
public SubscriberId subscriberId;
public String did;
public String name;
public String uri;
@Override
public void putString(String value) {
if (this.columnName.equals("did"))
this.did = value;
else if (this.columnName.equals("name"))
this.name = value;
else if (this.columnName.equals("uri"))
this.uri = value;
}
@Override
public String toString() {
return "LookupResult{" +
"subscriberId=" + subscriberId +
", did='" + did + '\'' +
", name='" + name + '\'' +
", uri='" + uri + '\'' +
'}';
}
}
@Deprecated
public static int dnaLookup(AsyncResult<LookupResult> results, String did, int timeout) throws ServalDFailureException {
return dnaLookup(new JniResultList<LookupResult>(results) {
@Override
public LookupResult create() {
return new LookupResult();
}
}, did, timeout);
}
@Deprecated
public static int dnaLookup(IJniResults results, String did, int timeout) throws ServalDFailureException {
return command(results, "dna", "lookup", did, Integer.toString(timeout));
}
public static class ManifestResult extends JniResult{
public BundleId manifestId;
public long version;
public long fileSize;
public FileHash fileHash;
public BundleKey bundleKey;
public long date;
public int crypt;
public String service;
public String name;
public boolean readonly=true;
public byte[] manifest;
public String secret;
public SubscriberId author;
public long rowId;
public long insertTime;
@Override
public void putString(String value) {
try {
if (value!="" && (columnName.equals("manifestid")||columnName.equals("id")))
manifestId = new BundleId(value);
if (value!="" && columnName.equals("filehash"))
fileHash = new FileHash(value);
if (value!="" && columnName.equals("BK"))
bundleKey = new BundleKey(value);
if (value!="" && columnName.equals(".author"))
author = new SubscriberId(value);
} catch (AbstractId.InvalidHexException e) {
e.printStackTrace();
}
if (columnName.equals("service"))
service = value;
if (columnName.equals("name"))
name = value;
if (columnName.equals("secret"))
secret = value;
}
@Override
public void putBlob(byte[] value) {
if (columnName.equals("manifest"))
this.manifest = value;
}
@Override
public void putLong(long value) {
if (columnName.equals("version"))
version = value;
if (columnName.equals("filesize"))
fileSize = value;
if (columnName.equals("date"))
date = value;
if (columnName.equals("crypt"))
crypt = (int)value;
if (columnName.equals(".readonly"))
readonly = value>0;
if (columnName.equals(".fromhere"))
readonly = value==0;
if (columnName.equals(".rowid") || columnName.equals("_id"))
rowId = value;
if (columnName.equals(".inserttime"))
insertTime = value;
}
}
public static ManifestResult rhizomeAddFile(File payloadPath, File manifestPath, BundleId bid, SubscriberId author, String pin, String... fieldValues)
throws ServalDFailureException
{
List<String> args = new LinkedList<String>();
args.add("rhizome");
args.add("add");
args.add("file");
if (pin != null) {
args.add("--entry-pin");
args.add(pin);
}
if (bid != null)
args.add("--bundle="+bid.toHex());
args.add(author == null ? null : author.toHex());
args.add(payloadPath == null ? null : payloadPath.getAbsolutePath());
args.add(manifestPath == null ? null : manifestPath.getAbsolutePath());
args.add(null);
for(String f : fieldValues)
args.add(f);
ManifestResult result = new ManifestResult();
result.setResult(command(result, args.toArray(new String[args.size()])));
return result;
}
public static int rhizomeList(AsyncResult<ManifestResult> result, String service, String name, SubscriberId sender, SubscriberId recipient, int offset, int numRows) throws ServalDFailureException {
return rhizomeList(new JniResultList<ManifestResult>(result) {
@Override
public ManifestResult create() {
return new ManifestResult();
}
}, service, name, sender, recipient, offset, numRows);
}
public static int rhizomeList(IJniResults result, String service, String name, SubscriberId sender, SubscriberId recipient, int offset, int numRows) throws ServalDFailureException {
List<String> args = new LinkedList<String>();
args.add("rhizome");
args.add("list");
args.add(service == null ? "" : service);
args.add(name == null ? "" : name);
args.add(sender == null ? "" : sender.toHex());
args.add(recipient == null ? "" : recipient.toHex());
if (offset > 0)
args.add("" + offset);
else if (numRows > 0)
args.add("0");
if (numRows > 0)
args.add("" + numRows);
return command(result, args.toArray(new String[args.size()]));
}
public static ManifestResult rhizomeImportBundle(File payloadFile,
File manifestFile) throws ServalDFailureException {
ManifestResult result = new ManifestResult();
result.setResult(command(result, "rhizome", "import", "bundle",
payloadFile.getAbsolutePath(), manifestFile.getAbsolutePath()));
return result;
}
public static ManifestResult rhizomeImportZipBundle(File payloadFile) throws ServalDFailureException {
ManifestResult result = new ManifestResult();
result.setResult(command(result, "rhizome", "import", "bundle", "--zip-comment",
payloadFile.getAbsolutePath(), payloadFile.getAbsolutePath()));
return result;
}
public static ManifestResult rhizomeExtractBundle(BundleId manifestId, File manifestFile, File payloadFile) throws ServalDFailureException{
ManifestResult result = new ManifestResult();
result.setResult(command(result, "rhizome", "extract", "bundle",
manifestId.toHex(),
manifestFile == null ? "-" : manifestFile.getAbsolutePath(),
payloadFile.getAbsolutePath()));
return result;
}
public static ManifestResult rhizomeExportZipBundle(BundleId manifestId, File payloadFile) throws ServalDFailureException{
ManifestResult result = new ManifestResult();
result.setResult(command(result, "rhizome", "export", "bundle", "--zip-comment",
manifestId.toHex(),
payloadFile.getAbsolutePath(),
payloadFile.getAbsolutePath()));
return result;
}
public static ManifestResult rhizomeExportManifest(BundleId manifestId, File manifestFile) throws ServalDFailureException{
ManifestResult result = new ManifestResult();
result.setResult(command(result, "rhizome", "export", "manifest",
manifestId.toHex(),
manifestFile == null ? "-" : manifestFile.getAbsolutePath()));
return result;
}
public static ManifestResult rhizomeExtractFile(BundleId manifestId, File payloadFile) throws ServalDFailureException{
ManifestResult result = new ManifestResult();
result.setResult(command(result, "rhizome", "extract", "file",
manifestId.toHex(),
payloadFile.getAbsolutePath()));
return result;
}
/**
* Push Rhizome bundles to all configured direct hosts.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
public static void rhizomeDirectPush() throws ServalDFailureException
{
command("rhizome", "direct", "push");
}
/**
* Pull Rhizome bundles from all configured direct hosts.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
public static void rhizomeDirectPull() throws ServalDFailureException
{
command("rhizome", "direct", "pull");
}
/**
* Sync (push and pull) Rhizome bundles from all configured direct hosts.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
public static void rhizomeDirectSync() throws ServalDFailureException
{
command("rhizome", "direct", "sync");
}
public static class ConfigItems extends JniResult{
public Map<String, String> values = new HashMap<String, String>();
@Override
public void putString(String value) {
values.put(this.columnName, value);
}
}
public static ConfigItems getConfig(String pattern) throws ServalDFailureException {
ConfigItems results = new ConfigItems();
results.setResult(command(results, "config", "get", pattern));
return results;
}
public static ConfigItems getConfig() throws ServalDFailureException {
ConfigItems results = new ConfigItems();
results.setResult(command(results, "config", "get"));
return results;
}
public static String getConfigItem(String name) throws ServalDFailureException{
Object result = getConfig(name).values.get(name);
if (result == null)
return null;
if (result instanceof byte[]){
return new String((byte[])result);
}
return result.toString();
}
public enum ConfigAction{
set,
del,
sync
};
public static void configActions(Object... arguments) throws ServalDFailureException {
// TODO we could verify the types and number of arguments here, though servald is about to do that anyway.
String args[] = new String[arguments.length+1];
args[0]="config";
for (int i=0;i<arguments.length;i++)
args[i+1]=arguments[i].toString();
ServalDCommand.command(args);
}
public static void deleteConfig(String name) throws ServalDFailureException {
ServalDCommand.command("config", "del", name);
}
public static void setConfigItem(String name, String value) throws ServalDFailureException {
ServalDCommand.command("config", "set", name, value);
}
public static void configSync() throws ServalDFailureException{
ServalDCommand.command("config", "sync");
}
public static boolean getConfigItemBoolean(String name, boolean defaultValue) {
try {
String value = getConfigItem(name);
return parseBoolean(value, defaultValue);
} catch (ServalDFailureException e) {
e.printStackTrace();
return defaultValue;
}
}
public static int getConfigItemInt(String name, int defaultValue) throws ServalDFailureException{
try {
return Integer.parseInt(getConfig(name).values.get(name));
} catch (ServalDFailureException e) {
e.printStackTrace();
return defaultValue;
}
}
private static class PeerCount extends JniResult{
long count;
@Override
public void putLong(long value) {
count = value;
}
}
public static int peerCount() throws ServalDFailureException {
PeerCount result = new PeerCount();
result.setResult(ServalDCommand.command(result, "peer", "count"));
return (int)result.count;
}
// Note that the result values will only have a subscriber id
@Deprecated
public static int idPeers(AsyncResult<IdentityResult> results) throws ServalDFailureException {
return idPeers(new JniResultList<IdentityResult>(results) {
@Override
public IdentityResult create() {
return new IdentityResult();
}
});
}
@Deprecated
public static int idPeers(IJniResults results) throws ServalDFailureException {
return command(results, "id", "peers");
}
@Deprecated
public static class Conversation extends JniResult{
public long id;
public SubscriberId recipient;
public String read;
public long last_message;
public long read_offset;
@Override
public void putString(String value) {
if (columnName.equals("read"))
this.read = value;
}
@Override
public void putBlob(byte[] value) {
if (columnName.equals("recipient"))
try {
this.recipient = new SubscriberId(value);
} catch (AbstractId.InvalidBinaryException e) {
e.printStackTrace();
}
}
@Override
public void putLong(long value) {
if (columnName.equals("_id"))
this.id = value;
if (columnName.equals("last_message"))
this.last_message = value;
if (columnName.equals("read_offset"))
this.read_offset = value;
}
}
@Deprecated
public static int listConversations(AsyncResult<Conversation> result, final SubscriberId sender, int offset, int numRows) throws ServalDFailureException {
return listConversations(new JniResultList<Conversation>(result) {
@Override
public Conversation create() {
return new Conversation();
}
}, sender, offset, numRows);
}
@Deprecated
public static int listConversations(IJniResults callback, final SubscriberId sender, int offset, int numRows) throws ServalDFailureException {
return command(callback, "meshms", "list", "conversations",
sender.toHex(), ""+offset, ""+numRows);
}
@Deprecated
public static class Message extends JniResult{
public long id;
public long offset;
public String type;
public String message;
@Override
public void putString(String value) {
if (columnName.equals("type"))
this.type = value;
if (columnName.equals("message"))
this.message = value;
}
@Override
public void putLong(long value) {
if (columnName.equals("_id"))
this.id = value;
if (columnName.equals("offset"))
this.offset = value;
}
}
@Deprecated
public static int listMessages(AsyncResult<Message> result, final SubscriberId sender, final SubscriberId recipient) throws ServalDFailureException {
return listMessages(new JniResultList<Message>(result) {
@Override
public Message create() {
return new Message();
}
}, sender, recipient);
}
@Deprecated
public static int listMessages(IJniResults callback, final SubscriberId sender, final SubscriberId recipient) throws ServalDFailureException {
return ServalDCommand.command(callback, "meshms", "list", "messages",
sender.toHex(), recipient.toHex());
}
@Deprecated
public static void sendMessage(final SubscriberId sender, final SubscriberId recipient, String message) throws ServalDFailureException {
command("meshms", "send", "message",
sender.toHex(), recipient.toHex(),
message);
}
@Deprecated
public static void readMessage(final SubscriberId sender, final SubscriberId recipient) throws ServalDFailureException {
command("meshms", "read", "messages",
sender.toHex(), recipient.toHex());
}
@Deprecated
public static void readMessage(final SubscriberId sender, final SubscriberId recipient, long offset) throws ServalDFailureException {
command("meshms", "read", "messages",
sender.toHex(), recipient.toHex(),
"" + offset);
}
public static Process mspTunnnelCreate(String exec, int ip_port, int msp_port) throws IOException {
return new ProcessBuilder(exec, "msp", "listen", "--forward="+ip_port, Integer.toString(msp_port)).start();
}
public static Process mspTunnnelCreate(String exec, int ip_port, String serviceName, int msp_port) throws IOException {
return new ProcessBuilder(exec, "msp", "listen", "--forward="+ip_port, "--service="+serviceName, Integer.toString(msp_port)).start();
}
public static Process mspTunnelConnect(String exec, int ip_port, SubscriberId msp_sid, int msp_port) throws IOException {
return new ProcessBuilder(exec, "msp", "connect", "--forward="+ip_port, msp_sid.toHex(), Integer.toString(msp_port)).start();
}
}