2013-05-09 03:47:24 +00:00
|
|
|
/*
|
|
|
|
Serval Distributed Numbering Architecture (DNA)
|
|
|
|
Copyright (C) 2013 Paul Gardner-Stephen
|
|
|
|
Copyright (C) 2013 Alexandra Sclapari
|
|
|
|
|
|
|
|
This program 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 2
|
|
|
|
of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This program 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 program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "serval.h"
|
|
|
|
#include "rhizome.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "conf.h"
|
|
|
|
|
2013-05-14 02:27:55 +00:00
|
|
|
int meshms_generate_outgoing_bid(rhizome_manifest *m,
|
2013-05-14 04:52:44 +00:00
|
|
|
const unsigned char *sender_sid,
|
2013-05-14 02:27:55 +00:00
|
|
|
const char *recipient_sid_hex)
|
|
|
|
{
|
|
|
|
// BIDprivate =SHA512(”moose”+recipientSID+RS+”anconal”+recipientSID+ ”capital gains tax”)
|
|
|
|
|
|
|
|
const unsigned char *rs;
|
|
|
|
int rs_len;
|
2013-05-14 04:52:44 +00:00
|
|
|
if (rhizome_find_secret(sender_sid,&rs_len,&rs))
|
|
|
|
return WHYF("Could not find rhizome secret for: '%s'",
|
|
|
|
alloca_tohex(sender_sid,SID_SIZE));
|
2013-05-14 02:27:55 +00:00
|
|
|
return -1;
|
|
|
|
if (rs_len>256) rs_len=256; // limit to first 2048 bits of rhizome secret
|
|
|
|
if (rs_len<128) return WHYF("Rhizome secret too short");
|
|
|
|
char *rs_hex=alloca_tohex(rs,rs_len);
|
|
|
|
|
|
|
|
char secret[1024];
|
|
|
|
unsigned char hash[crypto_hash_sha512_BYTES];
|
|
|
|
snprintf(secret,1024,"moose%s%sanconal%scapital gains tax",
|
|
|
|
recipient_sid_hex,rs_hex,recipient_sid_hex);
|
|
|
|
crypto_hash_sha512(hash, (unsigned char *)secret, strlen(secret));
|
|
|
|
|
|
|
|
// The first 256 bits of the hash will be used as the private key of the BID.
|
2013-05-14 04:52:44 +00:00
|
|
|
bcopy(hash,m->cryptoSignSecret,
|
|
|
|
crypto_sign_edwards25519sha512batch_SECRETKEYBYTES);
|
2013-05-14 02:27:55 +00:00
|
|
|
if (crypto_sign_compute_public_key(m->cryptoSignSecret,m->cryptoSignPublic))
|
|
|
|
return WHY("Could not compute BID");
|
|
|
|
|
|
|
|
// Clear out sensitive data
|
|
|
|
bzero(secret,1024);
|
|
|
|
bzero(rs_hex,strlen(rs_hex));
|
|
|
|
bzero(hash,crypto_hash_sha512_BYTES);
|
|
|
|
|
2013-05-14 03:06:36 +00:00
|
|
|
return 0;
|
2013-05-14 02:27:55 +00:00
|
|
|
}
|
|
|
|
|
2013-05-14 03:03:51 +00:00
|
|
|
|
|
|
|
int meshms_xor_obfuscated_sid(unsigned char *xor_sid,
|
|
|
|
const unsigned char *known_sid_secret,
|
|
|
|
const char *other_sid_hex)
|
|
|
|
{
|
|
|
|
sid_t otherSid;
|
|
|
|
if (str_to_sid_t(&otherSid, other_sid_hex)==-1)
|
|
|
|
return WHY("Could not parse foreign SID");
|
|
|
|
|
|
|
|
unsigned char nm_bytes[crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES];
|
|
|
|
if (crypto_box_curve25519xsalsa20poly1305_beforenm(nm_bytes,
|
|
|
|
otherSid.binary,
|
|
|
|
known_sid_secret))
|
|
|
|
return WHY("crypto_box_beforenm() failed");
|
|
|
|
|
|
|
|
char secret[strlen("Salt String 1")+crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES+strlen("Salt String 1")];
|
|
|
|
unsigned char hash[crypto_hash_sha512_BYTES];
|
|
|
|
int o=0,l;
|
|
|
|
l=strlen("Salt String 1");
|
|
|
|
bcopy("Salt String 1",&secret[o],l); o+=l;
|
|
|
|
l=crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES;
|
|
|
|
bcopy(nm_bytes,&secret[o],l); o+=l;
|
|
|
|
l=strlen("Salt String 2");
|
|
|
|
bcopy("Salt String 2",&secret[o],l); o+=l;
|
|
|
|
|
|
|
|
// Hash secret to get sender obfuscation XOR string
|
|
|
|
crypto_hash_sha512(hash, (unsigned char *)secret, strlen(secret));
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for(i=0;i<SID_SIZE;i++) xor_sid[i]^=hash[i];
|
|
|
|
|
|
|
|
// Clear out sensitive data
|
|
|
|
bzero(hash,crypto_hash_sha512_BYTES);
|
|
|
|
bzero(secret,sizeof(secret));
|
|
|
|
bzero(nm_bytes,sizeof(nm_bytes));
|
|
|
|
|
|
|
|
return 0;
|
2013-05-14 02:27:55 +00:00
|
|
|
}
|
|
|
|
|
2013-05-14 03:03:51 +00:00
|
|
|
int meshms_set_obfuscated_sender(rhizome_manifest *m,
|
|
|
|
const char *sender_sid_to_obfuscate_hex,
|
|
|
|
const char *recipient_sid_hex) {
|
|
|
|
|
|
|
|
// Generate shared secret.
|
|
|
|
// This function assumes it is being called from the sending side, and so
|
|
|
|
// the combination is private key of disposable SID (which we will generate)
|
|
|
|
// and public key of the recipient, as already available from the manifest.
|
|
|
|
|
|
|
|
// sender=Disposable\, SID
|
|
|
|
// SS=SharedSecret(Disposable\, SID\, private\, key,Recipient\, SID\, public\, key)
|
|
|
|
// b=SHA512("Salt\, String\,1"+SS+"Salt\, String\,2")
|
|
|
|
// ssender=b\oplus Sender\, SID
|
|
|
|
|
|
|
|
unsigned char disposable_sid[crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES];
|
|
|
|
unsigned char disposable_sid_secret[crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES];
|
|
|
|
if (crypto_box_curve25519xsalsa20poly1305_keypair(disposable_sid,
|
|
|
|
disposable_sid_secret))
|
|
|
|
return WHY("Failed to generate disposable SID");
|
|
|
|
|
|
|
|
sid_t obSid;
|
|
|
|
if (str_to_sid_t(&obSid, sender_sid_to_obfuscate_hex)==-1)
|
|
|
|
|
|
|
|
if (meshms_xor_obfuscated_sid(obSid.binary,disposable_sid_secret,
|
|
|
|
recipient_sid_hex))
|
|
|
|
return WHY("Failed to XOR sender to produce obfuscated SID");
|
|
|
|
|
2013-05-14 03:06:36 +00:00
|
|
|
char *sender_hex=alloca_tohex(disposable_sid,SID_SIZE);
|
|
|
|
char *ssender_hex=alloca_tohex(obSid.binary,SID_SIZE);
|
|
|
|
|
|
|
|
rhizome_manifest_set(m, "sender", sender_hex);
|
|
|
|
rhizome_manifest_set(m, "ssender", ssender_hex);
|
2013-05-14 04:52:44 +00:00
|
|
|
|
2013-05-14 03:06:36 +00:00
|
|
|
return 0;
|
2013-05-14 03:03:51 +00:00
|
|
|
}
|
2013-05-14 02:27:55 +00:00
|
|
|
|
2013-05-14 04:52:44 +00:00
|
|
|
int manifest_recover_obfuscated_sender(rhizome_manifest *m)
|
|
|
|
{
|
|
|
|
// There are two possiblities here:
|
|
|
|
// 1. We made the manifest, and are the real sender.
|
|
|
|
// 2. We are the named recipient, and can attempt to recover the
|
|
|
|
// real sender.
|
|
|
|
// The process of attempting to recover the real sender is different for each.
|
|
|
|
|
|
|
|
{
|
|
|
|
// Get recipient
|
|
|
|
char *recipient_hex=rhizome_manifest_get(m, "recipient", NULL, 0);
|
|
|
|
// For each of our SIDs, see if we can reproduce the manifest ID
|
|
|
|
sid_t recipient_sid;
|
|
|
|
if (cf_opt_sid(&recipient_sid,recipient_hex)!=CFOK)
|
|
|
|
return WHYF("Unable to parse recipient sid from manifest");
|
|
|
|
|
|
|
|
sid_t ssender;
|
|
|
|
char *ssender_hex=rhizome_manifest_get(m, "ssender", NULL, 0);
|
|
|
|
if (!ssender_hex||(!ssender_hex[0])||cf_opt_sid(&ssender,ssender_hex)!=CFOK)
|
|
|
|
// missing or mal-formed ssender field, so cannot extract real sender.
|
|
|
|
// this is normal for non obfuscated sender bundles, so don't report an
|
|
|
|
// error.
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
int cn=0,in=0,kp=0;
|
|
|
|
rhizome_manifest *m2=rhizome_new_manifest();
|
|
|
|
while (keyring_find_sid(keyring,&cn,&in,&kp,recipient_sid.binary))
|
|
|
|
{
|
|
|
|
// SID is in keyring->contexts[cn]->identities[in]
|
|
|
|
// ->keypairs[kp]->public_key
|
|
|
|
|
|
|
|
// 2. See if we are the recipient
|
|
|
|
if (!memcmp(recipient_sid.binary,
|
|
|
|
keyring->contexts[cn]->identities[in]
|
|
|
|
->keypairs[kp]->public_key,
|
|
|
|
crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES))
|
|
|
|
{
|
|
|
|
// We are the recipient -- so we can extract the sender
|
|
|
|
if (!meshms_xor_obfuscated_sid(ssender.binary,
|
|
|
|
keyring->contexts[cn]->identities[in]
|
|
|
|
->keypairs[kp]->private_key,
|
|
|
|
recipient_hex))
|
|
|
|
{
|
|
|
|
return rhizome_manifest_set_real_sender(m,ssender.binary);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// 1. See if we made the manifest
|
|
|
|
if (meshms_generate_outgoing_bid(m2,
|
|
|
|
keyring->contexts[cn]->identities[in]
|
|
|
|
->keypairs[kp]->public_key,
|
|
|
|
recipient_hex))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!memcmp(m->cryptoSignPublic,m2->cryptoSignPublic,
|
|
|
|
crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES))
|
|
|
|
{
|
|
|
|
// Bingo! We created this.
|
|
|
|
// Set the real sender in the manifest, and return
|
|
|
|
rhizome_manifest_free(m2);
|
|
|
|
return rhizome_manifest_set_real_sender(m,
|
|
|
|
keyring->contexts[cn]
|
|
|
|
->identities[in]
|
|
|
|
->keypairs[kp]->public_key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rhizome_manifest_free(m2);
|
|
|
|
}
|
|
|
|
// Not found
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-05-09 03:47:24 +00:00
|
|
|
rhizome_manifest *meshms_find_or_create_manifestid
|
2013-05-09 09:35:50 +00:00
|
|
|
(const char *sender_sid_hex,const char *recipient_sid_hex, int createP)
|
2013-05-09 03:47:24 +00:00
|
|
|
{
|
|
|
|
sid_t authorSid;
|
|
|
|
if (str_to_sid_t(&authorSid, sender_sid_hex)==-1)
|
2013-05-09 10:51:21 +00:00
|
|
|
{ WHYF("invalid sender_sid: '%s'", sender_sid_hex); return NULL; }
|
2013-05-09 03:47:24 +00:00
|
|
|
|
|
|
|
// Get manifest structure to hold the manifest we find or create
|
|
|
|
rhizome_manifest *m = rhizome_new_manifest();
|
|
|
|
if (!m) {
|
|
|
|
WHY("Manifest struct could not be allocated -- not added to rhizome");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if there is an existing one, if so, read it and return it.
|
|
|
|
char manifestid_hex[RHIZOME_MANIFEST_ID_STRLEN+1];
|
|
|
|
if (!rhizome_meshms_find_conversation(sender_sid_hex, recipient_sid_hex,
|
|
|
|
manifestid_hex,
|
|
|
|
0 /* get first matching manifestid */)) {
|
|
|
|
// Found manifest, so nothing more to do right now.
|
|
|
|
int ret = rhizome_retrieve_manifest(manifestid_hex, m);
|
2013-05-09 03:56:12 +00:00
|
|
|
if (!ret) {
|
|
|
|
rhizome_find_bundle_author(m);
|
|
|
|
return m;
|
|
|
|
}
|
2013-05-09 03:47:24 +00:00
|
|
|
else {
|
|
|
|
WHYF("rhizome_retreive_manifest(%s) failed",manifestid_hex);
|
|
|
|
rhizome_manifest_free(m);
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-05-09 09:35:50 +00:00
|
|
|
} else {
|
|
|
|
if (!createP) {
|
|
|
|
rhizome_manifest_free(m);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2013-05-09 03:47:24 +00:00
|
|
|
|
|
|
|
// No existing manifest, so create one:
|
|
|
|
|
2013-05-14 02:27:55 +00:00
|
|
|
// Generate the deterministic BID for this sender recipient pair
|
2013-05-14 04:52:44 +00:00
|
|
|
sid_t sender_sid;
|
|
|
|
if (cf_opt_sid(&sender_sid,sender_sid_hex)) {
|
|
|
|
WHY("Could not parse sender SID");
|
|
|
|
rhizome_manifest_free(m);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (meshms_generate_outgoing_bid(m,sender_sid.binary,recipient_sid_hex)) {
|
2013-05-14 02:27:55 +00:00
|
|
|
WHY("meshms_generate_outgoing_bid() failed");
|
|
|
|
rhizome_manifest_free(m);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-05-09 03:47:24 +00:00
|
|
|
// Populate with the fields we know
|
|
|
|
rhizome_manifest_set(m, "service", RHIZOME_SERVICE_MESHMS);
|
|
|
|
rhizome_manifest_set(m,"recipient",recipient_sid_hex);
|
2013-05-14 02:27:55 +00:00
|
|
|
// DO NOT put the real sender in, because that would reveal people's social
|
|
|
|
// graph to everyone trivially.
|
|
|
|
// See github.com/servalproject/serval-docs/securing-meshms/ for more info.
|
|
|
|
// Instead, according to the above scheme, we:
|
|
|
|
// 1. Set sender=<a disposable sid> and
|
|
|
|
// 2. ssender=<mechanism to retrieve real sender if you are the recipient>
|
|
|
|
// This is done by the following function
|
2013-05-14 03:03:51 +00:00
|
|
|
if (meshms_set_obfuscated_sender(m,sender_sid_hex,recipient_sid_hex)) {
|
2013-05-14 02:27:55 +00:00
|
|
|
WHY("meshms_set_obfuscated_sender() failed");
|
|
|
|
rhizome_manifest_free(m);
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-05-09 03:47:24 +00:00
|
|
|
|
|
|
|
// Ask rhizome to prepare the missing parts (this will automatically determine
|
|
|
|
// whether to encrypt based on whether receipient was set to broadcast or not)
|
|
|
|
if (rhizome_fill_manifest(m,NULL,&authorSid,NULL)) {
|
|
|
|
WHY("rhizome_fill_manifest() failed");
|
2013-05-14 02:27:55 +00:00
|
|
|
rhizome_manifest_free(m);
|
|
|
|
return NULL;
|
2013-05-09 03:47:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
|
|
|
// meshms add message <sender SID> <recipient SID> <sender DID> <recipient DID> <message text>
|
|
|
|
int app_meshms_add_message(const struct cli_parsed *parsed, void *context)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
2013-05-09 03:51:24 +00:00
|
|
|
if (create_serval_instance_dir() == -1)
|
|
|
|
return -1;
|
|
|
|
if (!(keyring = keyring_open_instance_cli(parsed)))
|
|
|
|
return -1;
|
|
|
|
if (rhizome_opendb() == -1)
|
|
|
|
return -1;
|
|
|
|
|
2013-05-09 03:47:24 +00:00
|
|
|
if (config.debug.verbose)
|
|
|
|
DEBUG_cli_parsed(parsed);
|
|
|
|
//sender_sid = author_sid
|
|
|
|
const char *sender_did, *recipient_did, *payload, *sender_sid, *recipient_sid;
|
2013-05-09 03:51:24 +00:00
|
|
|
|
2013-05-09 03:47:24 +00:00
|
|
|
// Parse mandatory arguments
|
|
|
|
cli_arg(parsed, "sender_sid", &sender_sid, cli_optional_sid, "");
|
|
|
|
cli_arg(parsed, "recipient_sid", &recipient_sid, cli_optional_sid, "");
|
|
|
|
cli_arg(parsed, "sender_did", &sender_did, cli_optional_did, "");
|
|
|
|
cli_arg(parsed, "recipient_did", &recipient_did, cli_optional_did, "");
|
|
|
|
cli_arg(parsed, "payload", &payload, NULL, "");
|
|
|
|
// Sanity check passed arguments
|
|
|
|
if ( (strcmp(sender_did,"") == 0) || (strcmp(recipient_did,"") == 0) || (strcmp(sender_sid,"") == 0) || (strcmp(recipient_sid,"" ) == 0) )
|
|
|
|
{
|
|
|
|
cli_puts("One or more missing arguments"); cli_delim("\n");
|
|
|
|
}
|
|
|
|
sid_t aSid;
|
|
|
|
if (sender_sid[0] && str_to_sid_t(&aSid, sender_sid) == -1)
|
|
|
|
return WHYF("invalid sender_sid: %s", sender_sid);
|
|
|
|
if (recipient_sid[0] && str_to_sid_t(&aSid, recipient_sid) == -1)
|
|
|
|
return WHYF("invalid recipient_sid: %s", recipient_sid);
|
|
|
|
|
|
|
|
// Create serialised meshms message for appending to the conversation ply
|
|
|
|
unsigned int length_int = 1;
|
|
|
|
int offset_buf=0;
|
|
|
|
unsigned long long send_date_ll=gettime_ms();
|
|
|
|
unsigned char *buffer_serialize;
|
|
|
|
buffer_serialize=malloc(strlen(payload)+100); // make sure we have plenty of space
|
|
|
|
|
|
|
|
// encode twice: first to work out the final length, then once more to write it correctly
|
|
|
|
ret = serialize_meshms(buffer_serialize,&offset_buf,length_int,sender_did, recipient_did, send_date_ll, payload, strlen(payload)+1);
|
|
|
|
while(length_int!=offset_buf) {
|
|
|
|
length_int=offset_buf;
|
|
|
|
offset_buf=0;
|
|
|
|
ret = serialize_meshms(buffer_serialize,&offset_buf,length_int,sender_did, recipient_did, send_date_ll, payload, strlen(payload)+1);
|
|
|
|
}
|
2013-05-09 04:36:42 +00:00
|
|
|
|
|
|
|
ret=meshms_append_messageblock(sender_sid,recipient_sid,
|
|
|
|
buffer_serialize,length_int);
|
|
|
|
free(buffer_serialize);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int meshms_append_messageblock(const char *sender_sid,
|
|
|
|
const char *recipient_sid,
|
|
|
|
const unsigned char *buffer_serialize,
|
|
|
|
int length_int)
|
|
|
|
{
|
2013-05-09 03:47:24 +00:00
|
|
|
// Find the manifest (or create it if it doesn't yet exist)
|
2013-05-09 09:35:50 +00:00
|
|
|
rhizome_manifest *m=meshms_find_or_create_manifestid(sender_sid,recipient_sid,1);
|
2013-05-09 03:47:24 +00:00
|
|
|
if (!m) return -1;
|
|
|
|
|
|
|
|
// Read the bundle file containing the meshms messages
|
|
|
|
// (and keep enough space to append the new message
|
|
|
|
unsigned char *buffer_file=malloc(m->fileLength+length_int);
|
|
|
|
if (!buffer_file) {
|
|
|
|
WHYF("malloc(%d) failed when reading existing MeshMS log.",m->fileLength);
|
|
|
|
rhizome_manifest_free(m);
|
|
|
|
return -1;
|
|
|
|
}
|
2013-05-09 04:36:42 +00:00
|
|
|
int ret = meshms_read_message(m,buffer_file);
|
2013-05-09 03:47:24 +00:00
|
|
|
if (ret) {
|
|
|
|
WHYF("meshms_read_message() failed.");
|
|
|
|
rhizome_manifest_free(m);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
// Append the serialised message, and update file length
|
|
|
|
bcopy(buffer_serialize,&buffer_file[m->fileLength],length_int);
|
|
|
|
m->fileLength += length_int;
|
|
|
|
// MeshMS bundles are journalled, so filesize and version are synonymous
|
|
|
|
rhizome_manifest_set_ll(m, "filesize", m->fileLength);
|
|
|
|
rhizome_manifest_set_ll(m,"version",m->fileLength);
|
|
|
|
// Write enlarged message log to bundle
|
|
|
|
rhizome_add_file(m,(char *)buffer_file,1,m->fileLength);
|
|
|
|
|
|
|
|
free(buffer_file);
|
|
|
|
|
|
|
|
rhizome_manifest *mout = NULL;
|
|
|
|
ret=rhizome_manifest_finalise(m,&mout);
|
|
|
|
if (ret<0){
|
|
|
|
cli_printf("Error in manifest finalise");
|
|
|
|
rhizome_manifest_free(m);
|
|
|
|
if (mout&&mout!=m) rhizome_manifest_free(mout);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
char bid[RHIZOME_MANIFEST_ID_STRLEN + 1];
|
|
|
|
rhizome_bytes_to_hex_upper(mout->cryptoSignPublic, bid, RHIZOME_MANIFEST_ID_BYTES);
|
|
|
|
cli_puts("manifestid");
|
|
|
|
cli_delim(":");
|
|
|
|
cli_puts(bid);
|
|
|
|
cli_delim("\n");
|
|
|
|
}
|
|
|
|
{
|
|
|
|
char secret[RHIZOME_BUNDLE_KEY_STRLEN + 1];
|
|
|
|
rhizome_bytes_to_hex_upper(mout->cryptoSignSecret, secret, RHIZOME_BUNDLE_KEY_BYTES);
|
|
|
|
cli_puts("secret");
|
|
|
|
cli_delim(":");
|
|
|
|
cli_puts(secret);
|
|
|
|
cli_delim("\n");
|
|
|
|
}
|
|
|
|
cli_puts("version"); cli_delim(":"); cli_printf("%lld", m->version); cli_delim("\n");
|
|
|
|
cli_puts("filesize");
|
|
|
|
cli_delim(":");
|
|
|
|
cli_printf("%lld", mout->fileLength);
|
|
|
|
cli_delim("\n");
|
|
|
|
if (mout->fileLength != 0) {
|
|
|
|
cli_puts("filehash");
|
|
|
|
cli_delim(":");
|
|
|
|
cli_puts(mout->fileHexHash);
|
|
|
|
cli_delim("\n");
|
|
|
|
}
|
|
|
|
const char *name_manifest = rhizome_manifest_get(mout, "name", NULL, 0);
|
|
|
|
if (name_manifest) {
|
|
|
|
cli_puts("name");
|
|
|
|
cli_delim(":");
|
|
|
|
cli_puts(name_manifest);
|
|
|
|
cli_delim("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mout != m)
|
|
|
|
rhizome_manifest_free(mout);
|
|
|
|
rhizome_manifest_free(m);
|
|
|
|
|
|
|
|
return ret ;
|
|
|
|
}
|
|
|
|
|
2013-05-09 10:51:21 +00:00
|
|
|
int app_meshms_list_messages(const struct cli_parsed *parsed, void *context)
|
2013-05-09 09:35:50 +00:00
|
|
|
{
|
|
|
|
if (create_serval_instance_dir() == -1)
|
|
|
|
return -1;
|
|
|
|
if (!(keyring = keyring_open_instance_cli(parsed)))
|
|
|
|
return -1;
|
|
|
|
if (rhizome_opendb() == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (config.debug.verbose)
|
|
|
|
DEBUG_cli_parsed(parsed);
|
2013-05-09 10:51:21 +00:00
|
|
|
//left_sid = author_sid
|
|
|
|
const char *left_sid, *right_sid;
|
2013-05-09 09:35:50 +00:00
|
|
|
|
|
|
|
// Parse mandatory arguments
|
2013-05-09 10:51:21 +00:00
|
|
|
cli_arg(parsed, "sender_sid", &left_sid, cli_optional_sid, "");
|
|
|
|
cli_arg(parsed, "recipient_sid", &right_sid, cli_optional_sid, "");
|
2013-05-09 09:35:50 +00:00
|
|
|
// Sanity check passed arguments
|
2013-05-09 10:51:21 +00:00
|
|
|
if ( (strcmp(left_sid,"") == 0) || (strcmp(right_sid,"" ) == 0) )
|
2013-05-09 09:35:50 +00:00
|
|
|
{
|
|
|
|
cli_puts("One or more missing arguments"); cli_delim("\n");
|
|
|
|
}
|
|
|
|
sid_t aSid;
|
2013-05-09 10:51:21 +00:00
|
|
|
if (left_sid[0] && str_to_sid_t(&aSid, left_sid) == -1)
|
|
|
|
return WHYF("invalid left_sid: %s", left_sid);
|
|
|
|
if (right_sid[0] && str_to_sid_t(&aSid, right_sid) == -1)
|
|
|
|
return WHYF("invalid right_sid: %s", right_sid);
|
2013-05-09 09:35:50 +00:00
|
|
|
|
|
|
|
// Obtain message logs for both sides of the conversation, if available
|
2013-05-09 10:51:21 +00:00
|
|
|
rhizome_manifest *m_left=NULL,*m_right=NULL;
|
|
|
|
m_left=meshms_find_or_create_manifestid(left_sid,right_sid,0);
|
2013-05-09 12:24:20 +00:00
|
|
|
m_right=meshms_find_or_create_manifestid(right_sid,left_sid,0);
|
2013-05-09 10:51:21 +00:00
|
|
|
int left_len=0, right_len=0;
|
|
|
|
unsigned char *left_messages=NULL, *right_messages=NULL;
|
|
|
|
if (m_left) {
|
|
|
|
left_messages=malloc(m_left->fileLength);
|
|
|
|
if (!left_messages) {
|
|
|
|
WHYF("malloc(%d) failed while reading meshms logs",m_left->fileLength);
|
2013-05-09 09:35:50 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2013-05-09 10:51:21 +00:00
|
|
|
if (!meshms_read_message(m_left,left_messages))
|
|
|
|
left_len=m_left->fileLength;
|
2013-05-09 09:35:50 +00:00
|
|
|
}
|
2013-05-09 10:51:21 +00:00
|
|
|
if (m_right) {
|
|
|
|
right_messages=malloc(m_right->fileLength);
|
|
|
|
if (!right_messages) {
|
|
|
|
WHYF("malloc(%d) failed while reading meshms logs",m_right->fileLength);
|
2013-05-09 09:35:50 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2013-05-09 10:51:21 +00:00
|
|
|
if (!meshms_read_message(m_right,right_messages))
|
|
|
|
right_len=m_right->fileLength;
|
2013-05-09 09:35:50 +00:00
|
|
|
}
|
2013-05-09 10:51:21 +00:00
|
|
|
rhizome_manifest_free(m_left); m_left=NULL;
|
|
|
|
rhizome_manifest_free(m_right); m_right=NULL;
|
|
|
|
|
|
|
|
#define MAX_MESSAGES_IN_THREAD 16384
|
|
|
|
int offsets[MAX_MESSAGES_IN_THREAD];
|
|
|
|
int sides[MAX_MESSAGES_IN_THREAD];
|
|
|
|
int message_count=0;
|
|
|
|
|
|
|
|
// Scan through messages and acks to generate forward-ordered list, and determine
|
|
|
|
// last message from left that has been ack'd by right. We will then traverse the
|
|
|
|
// list in reverse order to display the messages.
|
|
|
|
int right_ack_limit=0;
|
|
|
|
int left_ack=0, left_offset=0, right_offset=0;
|
|
|
|
for(left_offset=0;left_offset<left_len;)
|
|
|
|
{
|
2013-05-09 11:26:43 +00:00
|
|
|
for(;right_offset<left_ack;)
|
2013-05-09 10:51:21 +00:00
|
|
|
{
|
|
|
|
unsigned int right_block_len;
|
|
|
|
int o=right_offset;
|
|
|
|
if (decode_length_forwards(right_messages,&o,right_len,
|
|
|
|
&right_block_len)) break;
|
|
|
|
int block_type=meshms_block_type(right_messages,right_offset,right_len);
|
|
|
|
switch(block_type) {
|
|
|
|
case RHIZOME_MESHMS_BLOCK_TYPE_BID_REFERENCE:
|
|
|
|
case RHIZOME_MESHMS_BLOCK_TYPE_MESSAGE:
|
|
|
|
offsets[message_count]=right_offset;
|
|
|
|
sides[message_count++]=1;
|
|
|
|
break;
|
|
|
|
case RHIZOME_MESHMS_BLOCK_TYPE_ACK:
|
|
|
|
{
|
|
|
|
int o=right_offset;
|
|
|
|
deserialize_ack(right_messages,&o,right_len,&right_ack_limit);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
right_offset+=right_block_len;
|
|
|
|
if (message_count>=MAX_MESSAGES_IN_THREAD) break;
|
|
|
|
}
|
|
|
|
if (message_count>=MAX_MESSAGES_IN_THREAD) break;
|
|
|
|
int block_type=meshms_block_type(left_messages,left_offset,left_len);
|
|
|
|
switch(block_type) {
|
|
|
|
case RHIZOME_MESHMS_BLOCK_TYPE_BID_REFERENCE:
|
|
|
|
case RHIZOME_MESHMS_BLOCK_TYPE_MESSAGE:
|
2013-05-09 11:26:43 +00:00
|
|
|
offsets[message_count]=left_offset;
|
2013-05-09 10:51:21 +00:00
|
|
|
sides[message_count++]=0;
|
|
|
|
break;
|
|
|
|
case RHIZOME_MESHMS_BLOCK_TYPE_ACK:
|
|
|
|
{
|
|
|
|
int o=right_offset;
|
|
|
|
deserialize_ack(right_messages,&o,right_len,&right_ack_limit);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
unsigned int left_block_len;
|
|
|
|
int o=left_offset;
|
|
|
|
if (decode_length_forwards(left_messages,&o,left_len,&left_block_len)) break;
|
|
|
|
left_offset+=left_block_len;
|
|
|
|
}
|
|
|
|
// Process any outstanding messages from the right side
|
|
|
|
for(;right_offset<=right_len;)
|
|
|
|
{
|
|
|
|
unsigned int right_block_len;
|
|
|
|
int o=right_offset;
|
|
|
|
if (decode_length_forwards(right_messages,&o,right_len,
|
|
|
|
&right_block_len)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
int block_type=meshms_block_type(right_messages,right_offset,right_len);
|
|
|
|
switch(block_type) {
|
|
|
|
case RHIZOME_MESHMS_BLOCK_TYPE_BID_REFERENCE:
|
|
|
|
case RHIZOME_MESHMS_BLOCK_TYPE_MESSAGE:
|
|
|
|
offsets[message_count]=right_offset;
|
|
|
|
sides[message_count++]=1;
|
|
|
|
break;
|
|
|
|
case RHIZOME_MESHMS_BLOCK_TYPE_ACK:
|
|
|
|
{
|
|
|
|
int o=right_offset;
|
|
|
|
deserialize_ack(right_messages,&o,right_len,&right_ack_limit);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
right_offset+=right_block_len;
|
|
|
|
if (message_count>=MAX_MESSAGES_IN_THREAD) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Display list of messages in reverse order
|
2013-05-09 11:37:42 +00:00
|
|
|
const char *names[]={
|
2013-05-09 12:14:05 +00:00
|
|
|
"number",
|
2013-05-09 11:37:42 +00:00
|
|
|
"offset",
|
|
|
|
"length",
|
|
|
|
"sender",
|
|
|
|
"recipient",
|
|
|
|
"date",
|
|
|
|
"delivery_status",
|
2013-05-09 11:39:33 +00:00
|
|
|
"type",
|
2013-05-09 11:37:42 +00:00
|
|
|
"message"
|
|
|
|
};
|
2013-05-09 12:14:05 +00:00
|
|
|
cli_columns(9, names);
|
2013-05-09 11:37:42 +00:00
|
|
|
|
2013-05-09 10:51:21 +00:00
|
|
|
int i;
|
|
|
|
for(i=message_count-1;i>=0;i--)
|
|
|
|
{
|
2013-05-09 11:37:42 +00:00
|
|
|
char *delivery_status
|
|
|
|
=sides[i]?"received":
|
|
|
|
((offsets[i]<right_ack_limit)?"delivered":"unacknowledged");
|
2013-05-09 11:26:43 +00:00
|
|
|
int boffset=offsets[i];
|
2013-05-09 12:14:05 +00:00
|
|
|
deserialize_meshms(message_count-1-i,
|
|
|
|
sides[i]?right_messages:left_messages,&boffset,
|
2013-05-09 11:37:42 +00:00
|
|
|
sides[i]?right_len:left_len,delivery_status);
|
2013-05-09 10:51:21 +00:00
|
|
|
}
|
2013-05-09 09:35:50 +00:00
|
|
|
|
2013-05-09 10:51:21 +00:00
|
|
|
return 0;
|
2013-05-09 09:35:50 +00:00
|
|
|
}
|
2013-05-09 12:58:04 +00:00
|
|
|
|
2013-05-10 02:33:37 +00:00
|
|
|
|
|
|
|
int meshms_get_last_ack_offset(const char *left_sid,const char *right_sid)
|
|
|
|
{
|
|
|
|
rhizome_manifest *m_left=NULL;
|
|
|
|
m_left=meshms_find_or_create_manifestid(left_sid,right_sid,0);
|
|
|
|
if (!m_left) {
|
|
|
|
DEBUGF("Couldn't find manifest for thread ply");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
unsigned char *left_messages=malloc(m_left->fileLength);
|
|
|
|
if (!left_messages) {
|
|
|
|
WHYF("malloc(%d) failed while reading meshms logs",m_left->fileLength);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (meshms_read_message(m_left,left_messages)) {
|
|
|
|
DEBUGF("Couldn't read message log for thread ply");
|
|
|
|
rhizome_manifest_free(m_left); return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int left_len=m_left->fileLength;
|
|
|
|
rhizome_manifest_free(m_left); m_left=NULL;
|
|
|
|
|
|
|
|
// Scan through messages and look for acks
|
|
|
|
int left_ack_limit=0;
|
|
|
|
int left_offset=0;
|
|
|
|
for(left_offset=0;left_offset<left_len;)
|
|
|
|
{
|
|
|
|
int block_type=meshms_block_type(left_messages,left_offset,left_len);
|
|
|
|
switch(block_type) {
|
|
|
|
case RHIZOME_MESHMS_BLOCK_TYPE_ACK:
|
|
|
|
{
|
|
|
|
int o=left_offset;
|
|
|
|
deserialize_ack(left_messages,&o,left_len,&left_ack_limit);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
unsigned int left_block_len;
|
|
|
|
int o=left_offset;
|
|
|
|
if (decode_length_forwards(left_messages,&o,left_len,&left_block_len)) break;
|
|
|
|
left_offset+=left_block_len;
|
|
|
|
}
|
|
|
|
free(left_messages);
|
|
|
|
return left_ack_limit;
|
|
|
|
}
|
|
|
|
|
2013-05-09 12:58:04 +00:00
|
|
|
int app_meshms_ack_messages(const struct cli_parsed *parsed, void *context)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (create_serval_instance_dir() == -1)
|
|
|
|
return -1;
|
|
|
|
if (!(keyring = keyring_open_instance_cli(parsed)))
|
|
|
|
return -1;
|
|
|
|
if (rhizome_opendb() == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (config.debug.verbose)
|
|
|
|
DEBUG_cli_parsed(parsed);
|
|
|
|
//sender_sid = author_sid
|
|
|
|
const char *sender_sid, *recipient_sid;
|
|
|
|
|
|
|
|
const char *message_offset_str;
|
|
|
|
uint message_offset=0;
|
|
|
|
|
|
|
|
// Parse mandatory arguments
|
|
|
|
cli_arg(parsed, "sender_sid", &sender_sid, cli_optional_sid, "");
|
|
|
|
cli_arg(parsed, "recipient_sid", &recipient_sid, cli_optional_sid, "");
|
|
|
|
cli_arg(parsed, "message_offset", &message_offset_str, NULL, "0");
|
|
|
|
message_offset=atoi(message_offset_str);
|
|
|
|
// Sanity check passed arguments
|
|
|
|
if ( (strcmp(sender_sid,"") == 0) || (strcmp(recipient_sid,"" ) == 0) )
|
|
|
|
{
|
|
|
|
cli_puts("One or more missing arguments"); cli_delim("\n");
|
|
|
|
}
|
|
|
|
sid_t aSid;
|
|
|
|
if (sender_sid[0] && str_to_sid_t(&aSid, sender_sid) == -1)
|
|
|
|
return WHYF("invalid sender_sid: %s", sender_sid);
|
|
|
|
if (recipient_sid[0] && str_to_sid_t(&aSid, recipient_sid) == -1)
|
|
|
|
return WHYF("invalid recipient_sid: %s", recipient_sid);
|
|
|
|
|
2013-05-10 02:33:37 +00:00
|
|
|
DEBUGF("Message log previously acknowledged to %d",
|
|
|
|
meshms_get_last_ack_offset(sender_sid,recipient_sid));
|
|
|
|
if (meshms_get_last_ack_offset(sender_sid,recipient_sid)>=message_offset) {
|
|
|
|
INFO("Already acknowledged.");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-05-09 12:58:04 +00:00
|
|
|
// Create serialised ack message for appending to the conversation ply
|
2013-05-09 13:06:51 +00:00
|
|
|
int length_int = 0;
|
2013-05-09 12:58:04 +00:00
|
|
|
unsigned char buffer_serialize[100];
|
|
|
|
|
2013-05-09 13:06:51 +00:00
|
|
|
ret|=serialize_ack(buffer_serialize,&length_int,100,message_offset);
|
2013-05-09 12:58:04 +00:00
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
ret|=meshms_append_messageblock(sender_sid,recipient_sid,
|
|
|
|
buffer_serialize,length_int);
|
|
|
|
|
2013-05-10 02:33:37 +00:00
|
|
|
if (!ret) INFO("Acknowledged.");
|
|
|
|
|
2013-05-09 12:58:04 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2013-05-10 11:00:25 +00:00
|
|
|
|
|
|
|
int app_meshms_list_conversations(const struct cli_parsed *parsed, void *context)
|
|
|
|
{
|
|
|
|
if (create_serval_instance_dir() == -1)
|
|
|
|
return -1;
|
|
|
|
if (!(keyring = keyring_open_instance_cli(parsed)))
|
|
|
|
return -1;
|
|
|
|
if (rhizome_opendb() == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (config.debug.verbose)
|
|
|
|
DEBUG_cli_parsed(parsed);
|
|
|
|
const char *sid,*offset_str,*count_str;
|
|
|
|
|
|
|
|
// Parse mandatory arguments
|
|
|
|
cli_arg(parsed, "sid", &sid, cli_optional_sid, "");
|
|
|
|
cli_arg(parsed, "offset", &offset_str, NULL, "0");
|
|
|
|
cli_arg(parsed, "count", &count_str, NULL, "9999");
|
|
|
|
int offset=atoi(offset_str);
|
|
|
|
int count=atoi(count_str);
|
|
|
|
// Sanity check passed arguments
|
|
|
|
if ( strcmp(sid,"") == 0 )
|
|
|
|
{
|
|
|
|
cli_puts("One or more missing arguments"); cli_delim("\n");
|
|
|
|
}
|
|
|
|
sid_t aSid;
|
|
|
|
if (sid[0] && str_to_sid_t(&aSid, sid) == -1)
|
|
|
|
return WHYF("invalid sid: %s", sid);
|
|
|
|
|
|
|
|
return rhizome_meshms_find_conversations(sid,offset,count);
|
|
|
|
}
|