2016-10-12 03:04:03 +00:00
|
|
|
#include "serval.h"
|
|
|
|
#include "serval_types.h"
|
|
|
|
#include "dataformats.h"
|
|
|
|
#include "cli.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "debug.h"
|
|
|
|
#include "instance.h"
|
|
|
|
#include "commandline.h"
|
|
|
|
#include "keyring.h"
|
|
|
|
#include "rhizome.h"
|
|
|
|
#include "message_ply.h"
|
|
|
|
#include "meshmb.h"
|
|
|
|
#include "feature.h"
|
|
|
|
|
|
|
|
DEFINE_FEATURE(cli_meshmb);
|
|
|
|
|
2017-05-10 01:31:02 +00:00
|
|
|
static struct meshmb_feeds * cli_feeds_open(const struct cli_parsed *parsed){
|
|
|
|
const char *idhex;
|
|
|
|
if (cli_arg(parsed, "id", &idhex, str_is_identity, "") == -1)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
identity_t identity;
|
|
|
|
if (str_to_identity_t(&identity, idhex) == -1){
|
|
|
|
WHY("Invalid identity");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (create_serval_instance_dir() == -1
|
|
|
|
|| rhizome_opendb() == -1
|
|
|
|
|| !(keyring = keyring_open_instance_cli(parsed)))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
keyring_identity *id = keyring_find_identity(keyring, &identity);
|
|
|
|
if (!id){
|
|
|
|
WHY("Invalid identity");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct meshmb_feeds *feeds = NULL;
|
|
|
|
if (meshmb_open(id, &feeds)==-1)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return feeds;
|
|
|
|
}
|
|
|
|
|
2016-10-12 03:04:03 +00:00
|
|
|
DEFINE_CMD(app_meshmb_send, 0,
|
|
|
|
"Append a public broadcast message to your feed",
|
|
|
|
"meshmb", "send" KEYRING_PIN_OPTIONS, "<id>", "<message>", "...");
|
|
|
|
static int app_meshmb_send(const struct cli_parsed *parsed, struct cli_context *UNUSED(context))
|
|
|
|
{
|
2017-05-10 01:31:02 +00:00
|
|
|
const char *message;
|
|
|
|
if (cli_arg(parsed, "message", &message, NULL, "") == -1)
|
2016-10-12 03:04:03 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
unsigned nfields = (parsed->varargi == -1) ? 0 : parsed->argc - (unsigned)parsed->varargi;
|
|
|
|
struct rhizome_manifest_field_assignment fields[nfields];
|
|
|
|
|
|
|
|
if (nfields){
|
|
|
|
if (rhizome_parse_field_assignments(fields, nfields, parsed->args + parsed->varargi)==-1)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-05-10 01:31:02 +00:00
|
|
|
struct meshmb_feeds *feeds = cli_feeds_open(parsed);
|
2016-10-12 03:04:03 +00:00
|
|
|
|
2017-05-10 01:31:02 +00:00
|
|
|
int ret = -1;
|
|
|
|
if (feeds){
|
|
|
|
ret = meshmb_send(feeds, message, strlen(message)+1, nfields, fields);
|
2016-10-12 03:04:03 +00:00
|
|
|
|
2017-05-10 01:31:02 +00:00
|
|
|
if (ret!=-1){
|
|
|
|
ret = meshmb_flush(feeds);
|
|
|
|
if (ret!=-1)
|
|
|
|
ret=0;
|
|
|
|
}
|
|
|
|
|
|
|
|
meshmb_close(feeds);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (keyring)
|
|
|
|
keyring_free(keyring);
|
|
|
|
keyring = NULL;
|
2016-10-12 03:04:03 +00:00
|
|
|
|
2017-05-10 01:31:02 +00:00
|
|
|
return ret;
|
2016-10-12 03:04:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO from offset....?
|
|
|
|
DEFINE_CMD(app_meshmb_read, 0,
|
|
|
|
"Read all messages in a broadcast message feed.",
|
|
|
|
"meshmb", "read", "<id>");
|
|
|
|
static int app_meshmb_read(const struct cli_parsed *parsed, struct cli_context *context)
|
|
|
|
{
|
|
|
|
const char *hex_id;
|
|
|
|
if (cli_arg(parsed, "id", &hex_id, str_is_identity, "") == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
rhizome_bid_t bid;
|
|
|
|
if (str_to_rhizome_bid_t(&bid, hex_id) == -1)
|
|
|
|
return WHY("Invalid Identity");
|
|
|
|
|
|
|
|
/* Ensure the Rhizome database exists and is open */
|
|
|
|
if (create_serval_instance_dir() == -1
|
|
|
|
|| rhizome_opendb() == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
struct message_ply_read read;
|
|
|
|
bzero(&read, sizeof read);
|
|
|
|
|
2017-02-21 03:06:38 +00:00
|
|
|
if (message_ply_read_open(&read, &bid, NULL)==-1)
|
2016-10-12 03:04:03 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
int ret=0;
|
|
|
|
size_t row_id = 0;
|
|
|
|
const char *names[]={
|
|
|
|
"_id","offset","age","message"
|
|
|
|
};
|
|
|
|
cli_start_table(context, NELS(names), names);
|
|
|
|
time_s_t timestamp = 0;
|
|
|
|
time_s_t now = gettime();
|
|
|
|
|
|
|
|
while(message_ply_read_prev(&read)==0){
|
|
|
|
switch(read.type){
|
|
|
|
case MESSAGE_BLOCK_TYPE_TIME:
|
2017-02-15 05:05:20 +00:00
|
|
|
if (message_ply_parse_timestamp(&read, ×tamp)!=0){
|
|
|
|
WARN("Malformed ply, expected timestamp");
|
2016-10-12 03:04:03 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MESSAGE_BLOCK_TYPE_MESSAGE:
|
|
|
|
cli_put_long(context, row_id++, ":");
|
|
|
|
cli_put_long(context, read.record_end_offset, ":");
|
|
|
|
cli_put_long(context, timestamp ? (long)(now - timestamp) : (long)-1, ":");
|
|
|
|
cli_put_string(context, (const char *)read.record, "\n");
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MESSAGE_BLOCK_TYPE_ACK:
|
|
|
|
// TODO, link to some other ply?
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
//ignore unknown types
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cli_end_table(context, row_id);
|
|
|
|
|
|
|
|
message_ply_read_close(&read);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFINE_CMD(app_meshmb_find, 0,
|
|
|
|
"Browse available broadcast message feeds",
|
|
|
|
"meshmb", "find", "[<search>]");
|
|
|
|
static int app_meshmb_find(const struct cli_parsed *parsed, struct cli_context *context)
|
|
|
|
{
|
|
|
|
const char *search=NULL;
|
|
|
|
cli_arg(parsed, "search", &search, NULL, "");
|
|
|
|
// Ensure the Rhizome database exists and is open
|
|
|
|
if (create_serval_instance_dir() == -1
|
|
|
|
|| rhizome_opendb() == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
struct rhizome_list_cursor cursor;
|
|
|
|
bzero(&cursor, sizeof cursor);
|
|
|
|
cursor.service = RHIZOME_SERVICE_MESHMB;
|
|
|
|
cursor.name = search && search[0] ? search : NULL;
|
|
|
|
|
|
|
|
//TODO hide feeds that have been blocked
|
|
|
|
|
|
|
|
if (rhizome_list_open(&cursor) == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
const char *names[]={
|
|
|
|
"_id",
|
|
|
|
"id",
|
2017-02-06 02:23:12 +00:00
|
|
|
"author",
|
2016-10-12 03:04:03 +00:00
|
|
|
"version",
|
|
|
|
"date",
|
|
|
|
"name"
|
|
|
|
};
|
|
|
|
cli_start_table(context, NELS(names), names);
|
|
|
|
|
|
|
|
unsigned rowcount=0;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
while ((n = rhizome_list_next(&cursor)) == 1) {
|
|
|
|
rowcount++;
|
|
|
|
rhizome_manifest *m = cursor.manifest;
|
|
|
|
cli_put_long(context, m->rowid, ":");
|
|
|
|
cli_put_hexvalue(context, m->keypair.public_key.binary, sizeof m->keypair.public_key.binary, ":");
|
2017-02-06 02:23:12 +00:00
|
|
|
|
|
|
|
switch (m->authorship) {
|
|
|
|
case AUTHOR_NOT_CHECKED:
|
|
|
|
case AUTHOR_AUTHENTIC:
|
|
|
|
case AUTHOR_LOCAL:
|
|
|
|
case AUTHOR_REMOTE:
|
|
|
|
cli_put_hexvalue(context, m->author.binary, sizeof m->author.binary, ":");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
cli_put_string(context, NULL, ":");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-10-12 03:04:03 +00:00
|
|
|
cli_put_long(context, m->version, ":");
|
|
|
|
cli_put_long(context, m->has_date ? m->date : 0, ":");
|
|
|
|
cli_put_string(context, m->name, "\n");
|
|
|
|
}
|
|
|
|
rhizome_list_release(&cursor);
|
|
|
|
cli_end_table(context, rowcount);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFINE_CMD(app_meshmb_follow, 0,
|
2017-06-05 03:09:07 +00:00
|
|
|
"Follow, block or ignore a broadcast feed",
|
2017-06-14 05:21:06 +00:00
|
|
|
"meshmb", "follow|ignore|block" KEYRING_PIN_OPTIONS, "<id>", "<peer>", "[<sender>]", "[<name>]");
|
2016-10-12 03:04:03 +00:00
|
|
|
static int app_meshmb_follow(const struct cli_parsed *parsed, struct cli_context *UNUSED(context))
|
|
|
|
{
|
2017-02-21 03:06:38 +00:00
|
|
|
const char *peerhex;
|
2017-06-14 05:21:06 +00:00
|
|
|
const char *sidhex;
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
if (cli_arg(parsed, "peer", &peerhex, str_is_identity, "") == -1
|
|
|
|
|| cli_arg(parsed, "sender", &sidhex, str_is_subscriber_id, NULL) == -1)
|
2016-10-12 03:04:03 +00:00
|
|
|
return -1;
|
|
|
|
|
2017-06-14 05:21:06 +00:00
|
|
|
cli_arg(parsed, "name", &name, NULL, NULL);
|
2016-10-12 03:04:03 +00:00
|
|
|
int follow = cli_arg(parsed, "follow", NULL, NULL, NULL) == 0;
|
2017-06-05 03:09:07 +00:00
|
|
|
int block = cli_arg(parsed, "block", NULL, NULL, NULL) == 0;
|
2016-10-12 03:04:03 +00:00
|
|
|
|
|
|
|
identity_t peer;
|
2017-02-21 03:06:38 +00:00
|
|
|
if (str_to_identity_t(&peer, peerhex) == -1)
|
2016-10-12 03:04:03 +00:00
|
|
|
return WHY("Invalid identity");
|
|
|
|
|
2017-06-14 05:21:06 +00:00
|
|
|
sid_t sender;
|
|
|
|
bzero(&sender, sizeof sender);
|
|
|
|
if (sidhex && *sidhex){
|
|
|
|
if (str_to_sid_t(&sender, sidhex) == -1)
|
|
|
|
return WHY("Invalid sender");
|
|
|
|
}
|
|
|
|
|
2017-02-21 03:06:38 +00:00
|
|
|
struct meshmb_feeds *feeds = cli_feeds_open(parsed);
|
2016-10-12 03:04:03 +00:00
|
|
|
|
|
|
|
int ret = -1;
|
2017-02-21 03:06:38 +00:00
|
|
|
if (feeds){
|
2017-06-05 03:09:07 +00:00
|
|
|
if (block){
|
2017-06-14 05:21:06 +00:00
|
|
|
ret = meshmb_block(feeds, &peer, (sidhex && *sidhex ? &sender : NULL));
|
2017-06-05 03:09:07 +00:00
|
|
|
}else if (follow){
|
2017-06-14 05:21:06 +00:00
|
|
|
ret = meshmb_follow(feeds, &peer, (sidhex && *sidhex ? &sender : NULL), name);
|
2017-02-21 03:06:38 +00:00
|
|
|
}else{
|
|
|
|
ret = meshmb_ignore(feeds, &peer);
|
|
|
|
}
|
2016-10-12 03:04:03 +00:00
|
|
|
|
2017-02-21 03:06:38 +00:00
|
|
|
if (ret!=-1){
|
|
|
|
ret = meshmb_flush(feeds);
|
|
|
|
if (ret!=-1)
|
|
|
|
ret=0;
|
|
|
|
}
|
2016-10-12 03:04:03 +00:00
|
|
|
|
2017-02-21 03:06:38 +00:00
|
|
|
meshmb_close(feeds);
|
2016-10-12 03:04:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (keyring)
|
|
|
|
keyring_free(keyring);
|
|
|
|
keyring = NULL;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct cli_enum_context{
|
|
|
|
unsigned rowcount;
|
|
|
|
struct cli_context *context;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int list_callback(struct meshmb_feed_details *details, void *context)
|
|
|
|
{
|
|
|
|
struct cli_enum_context *enum_context = context;
|
|
|
|
enum_context->rowcount++;
|
|
|
|
cli_put_long(enum_context->context, enum_context->rowcount, ":");
|
2017-03-22 02:22:18 +00:00
|
|
|
cli_put_string(enum_context->context, alloca_tohex_rhizome_bid_t(details->ply.bundle_id), ":");
|
|
|
|
cli_put_string(enum_context->context, alloca_tohex_sid_t(details->ply.author), ":");
|
2017-06-05 03:09:07 +00:00
|
|
|
cli_put_string(enum_context->context, details->blocked ? "true" : "false", ":");
|
2016-10-12 03:04:03 +00:00
|
|
|
cli_put_string(enum_context->context, details->name, ":");
|
|
|
|
cli_put_long(enum_context->context, details->timestamp ? (long)(gettime() - details->timestamp) : (long)-1, ":");
|
|
|
|
cli_put_string(enum_context->context, details->last_message, "\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFINE_CMD(app_meshmb_list, 0,
|
2017-06-05 03:09:07 +00:00
|
|
|
"List the feeds that you are currently following or blocking",
|
2016-10-12 03:04:03 +00:00
|
|
|
"meshmb", "list", "following" KEYRING_PIN_OPTIONS, "<id>");
|
|
|
|
static int app_meshmb_list(const struct cli_parsed *parsed, struct cli_context *context)
|
|
|
|
{
|
2017-02-21 03:06:38 +00:00
|
|
|
struct meshmb_feeds *feeds = cli_feeds_open(parsed);
|
2016-10-12 03:04:03 +00:00
|
|
|
int ret = -1;
|
|
|
|
|
2017-02-21 03:06:38 +00:00
|
|
|
if (feeds){
|
|
|
|
const char *names[]={
|
|
|
|
"_id",
|
|
|
|
"id",
|
|
|
|
"author",
|
2017-06-05 03:09:07 +00:00
|
|
|
"blocked",
|
2017-02-21 03:06:38 +00:00
|
|
|
"name",
|
|
|
|
"age",
|
|
|
|
"last_message"
|
|
|
|
};
|
|
|
|
cli_start_table(context, NELS(names), names);
|
|
|
|
struct cli_enum_context enum_context = {
|
|
|
|
.rowcount = 0,
|
|
|
|
.context = context,
|
|
|
|
};
|
|
|
|
|
|
|
|
meshmb_enum(feeds, NULL, list_callback, &enum_context);
|
|
|
|
meshmb_close(feeds);
|
|
|
|
|
|
|
|
cli_end_table(context, enum_context.rowcount);
|
|
|
|
ret = 0;
|
2016-10-12 03:04:03 +00:00
|
|
|
}
|
|
|
|
|
2017-02-21 03:06:38 +00:00
|
|
|
if (keyring)
|
|
|
|
keyring_free(keyring);
|
|
|
|
keyring = NULL;
|
|
|
|
return ret;
|
|
|
|
}
|
2016-10-12 03:04:03 +00:00
|
|
|
|
2017-02-21 03:06:38 +00:00
|
|
|
DEFINE_CMD(app_meshmb_activity, 0,
|
|
|
|
"List messages from feeds that you are currently following",
|
|
|
|
"meshmb", "activity" KEYRING_PIN_OPTIONS, "<id>");
|
|
|
|
static int app_meshmb_activity(const struct cli_parsed *parsed, struct cli_context *context)
|
|
|
|
{
|
|
|
|
struct meshmb_feeds *feeds = cli_feeds_open(parsed);
|
|
|
|
int ret = -1;
|
2016-10-12 03:04:03 +00:00
|
|
|
|
2017-02-21 03:06:38 +00:00
|
|
|
if (feeds){
|
|
|
|
meshmb_update(feeds);
|
|
|
|
meshmb_flush(feeds);
|
|
|
|
|
|
|
|
struct meshmb_activity_iterator *iterator = meshmb_activity_open(feeds);
|
|
|
|
|
|
|
|
if (iterator){
|
|
|
|
const char *names[]={
|
|
|
|
"_id",
|
|
|
|
"id",
|
|
|
|
"author",
|
|
|
|
"name",
|
|
|
|
"age",
|
|
|
|
"offset",
|
|
|
|
"message"
|
|
|
|
};
|
|
|
|
cli_start_table(context, NELS(names), names);
|
|
|
|
|
|
|
|
unsigned rowcount=0;
|
|
|
|
time_s_t now = gettime();
|
|
|
|
|
|
|
|
while(meshmb_activity_next(iterator)==1){
|
|
|
|
switch(iterator->msg_reader.type){
|
|
|
|
case MESSAGE_BLOCK_TYPE_MESSAGE:
|
|
|
|
cli_put_long(context, rowcount++, ":");
|
2017-02-27 01:28:28 +00:00
|
|
|
cli_put_string(context, alloca_tohex_rhizome_bid_t(iterator->msg_reader.bundle_id), ":");
|
2017-02-21 03:06:38 +00:00
|
|
|
cli_put_string(context, alloca_tohex_identity_t(&iterator->msg_reader.author), ":");
|
|
|
|
cli_put_string(context, iterator->msg_reader.name, ":");
|
|
|
|
cli_put_long(context, iterator->ack_timestamp ? (long)(now - iterator->ack_timestamp) : (long)-1, ":");
|
|
|
|
cli_put_long(context, iterator->msg_reader.record_end_offset, ":");
|
|
|
|
cli_put_string(context, (const char *)iterator->msg_reader.record, "\n");
|
|
|
|
}
|
|
|
|
}
|
2016-10-12 03:04:03 +00:00
|
|
|
|
2017-02-21 03:06:38 +00:00
|
|
|
cli_end_table(context, rowcount);
|
|
|
|
meshmb_activity_close(iterator);
|
|
|
|
ret = 0;
|
|
|
|
}
|
2016-10-12 03:04:03 +00:00
|
|
|
|
|
|
|
meshmb_close(feeds);
|
2017-02-21 03:06:38 +00:00
|
|
|
}
|
2016-10-12 03:04:03 +00:00
|
|
|
|
2017-02-21 03:06:38 +00:00
|
|
|
if (keyring)
|
|
|
|
keyring_free(keyring);
|
|
|
|
keyring = NULL;
|
|
|
|
return ret;
|
2016-10-12 03:04:03 +00:00
|
|
|
}
|