diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 6e3fe0ea..7bafa40e 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -107,6 +107,7 @@ static uint32_t _atoi(const char* sp) { #define CMD_RESET_PATH 13 #define CMD_SET_ADVERT_LATLON 14 #define CMD_REMOVE_CONTACT 15 +#define CMD_SHARE_CONTACT 16 #define RESP_CODE_OK 0 #define RESP_CODE_ERR 1 @@ -229,6 +230,49 @@ class MyMesh : public BaseChatMesh { } } + int getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) override { + char path[64]; + char fname[18]; + + if (key_len > 8) key_len = 8; // just use first 8 bytes (prefix) + mesh::Utils::toHex(fname, key, key_len); + sprintf(path, "/bl/%s", fname); + + if (_fs->exists(path)) { + File f = _fs->open(path); + if (f) { + int len = f.read(dest_buf, 255); // currently MAX 255 byte blob len supported!! + f.close(); + return len; + } + } + return 0; // not found + } + + bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len) override { + char path[64]; + char fname[18]; + + if (key_len > 8) key_len = 8; // just use first 8 bytes (prefix) + mesh::Utils::toHex(fname, key, key_len); + sprintf(path, "/bl/%s", fname); + + #if defined(NRF52_PLATFORM) + File f = _fs->open(path, FILE_O_WRITE); + if (f) { f.seek(0); f.truncate(); } + #else + File f = _fs->open(path, "w", true); + #endif + if (f) { + int n = f.write(src_buf, len); + f.close(); + if (n == len) return true; // success! + + _fs->remove(path); // blob was only partially written! + } + return false; // error + } + void writeOKFrame() { uint8_t buf[1]; buf[0] = RESP_CODE_OK; @@ -441,6 +485,9 @@ public: } } + // init 'blob store' support + _fs->mkdir("/bl"); + loadContacts(); _public = addChannel(PUBLIC_GROUP_PSK); // pre-configure Andy's public channel @@ -657,6 +704,14 @@ public: } else { writeErrFrame(); // not found, or unable to remove } + } else if (cmd_frame[0] == CMD_SHARE_CONTACT) { + uint8_t* pub_key = &cmd_frame[1]; + ContactInfo* recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE); + if (recipient && shareContactZeroHop(*recipient)) { + writeOKFrame(); + } else { + writeErrFrame(); // not found, or unable to send + } } else if (cmd_frame[0] == CMD_SYNC_NEXT_MESSAGE) { int out_len; if ((out_len = getFromOfflineQueue(out_frame)) > 0) { diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index 1eadda45..28538726 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -119,8 +119,8 @@ struct PostInfo { }; #define REPLY_DELAY_MILLIS 1500 -#define PUSH_NOTIFY_DELAY_MILLIS 1000 -#define SYNC_PUSH_INTERVAL 1000 +#define PUSH_NOTIFY_DELAY_MILLIS 2000 +#define SYNC_PUSH_INTERVAL 2000 #define PUSH_ACK_TIMEOUT_FLOOD 12000 #define PUSH_TIMEOUT_BASE 4000 diff --git a/src/helpers/BaseChatMesh.cpp b/src/helpers/BaseChatMesh.cpp index 51f1353f..9c91f640 100644 --- a/src/helpers/BaseChatMesh.cpp +++ b/src/helpers/BaseChatMesh.cpp @@ -50,6 +50,10 @@ void BaseChatMesh::onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, } } + // save a copy of raw advert packet (to support "Share..." function) + int plen = packet->writeTo(temp_buf); + putBlobByKey(id.pub_key, PUB_KEY_SIZE, temp_buf, plen); + // update strncpy(from->name, parser.getName(), sizeof(from->name)-1); from->name[sizeof(from->name)-1] = 0; @@ -246,6 +250,18 @@ bool BaseChatMesh::sendGroupMessage(uint32_t timestamp, mesh::GroupChannel& chan return false; } +bool BaseChatMesh::shareContactZeroHop(const ContactInfo& contact) { + int plen = getBlobByKey(contact.id.pub_key, PUB_KEY_SIZE, temp_buf); // retrieve last raw advert packet + if (plen == 0) return false; // not found + + auto packet = obtainNewPacket(); + if (packet == NULL) return false; // no Packets available + + packet->readFrom(temp_buf, plen); // restore Packet from 'blob' + sendZeroHop(packet); + return true; // success +} + bool BaseChatMesh::sendLogin(const ContactInfo& recipient, const char* password, uint32_t& est_timeout) { uint8_t shared_secret[32]; self_id.calcSharedSecret(shared_secret, recipient.id); // TODO: cache this diff --git a/src/helpers/BaseChatMesh.h b/src/helpers/BaseChatMesh.h index c01e18ab..1fc79026 100644 --- a/src/helpers/BaseChatMesh.h +++ b/src/helpers/BaseChatMesh.h @@ -60,6 +60,7 @@ class BaseChatMesh : public mesh::Mesh { mesh::GroupChannel channels[MAX_GROUP_CHANNELS]; int num_channels; #endif + uint8_t temp_buf[MAX_TRANS_UNIT]; mesh::Packet* composeMsgPacket(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char *text, uint32_t& expected_ack); @@ -85,6 +86,10 @@ protected: virtual void onChannelMessageRecv(const mesh::GroupChannel& channel, int in_path_len, uint32_t timestamp, const char *text) = 0; virtual void onContactResponse(const ContactInfo& contact, const uint8_t* data, uint8_t len) = 0; + // storage concepts, for sub-classes to override/implement + virtual int getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) { return 0; } // not implemented + virtual bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len) { return false; } + // Mesh overrides void onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, uint32_t timestamp, const uint8_t* app_data, size_t app_data_len) override; int searchPeersByHash(const uint8_t* hash) override; @@ -102,6 +107,7 @@ public: int sendMessage(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char* text, uint32_t& expected_ack, uint32_t& est_timeout); bool sendGroupMessage(uint32_t timestamp, mesh::GroupChannel& channel, const char* sender_name, const char* text, int text_len); bool sendLogin(const ContactInfo& recipient, const char* password, uint32_t& est_timeout); + bool shareContactZeroHop(const ContactInfo& contact); void resetPathTo(ContactInfo& recipient); void scanRecentContacts(int last_n, ContactVisitor* visitor); ContactInfo* searchContactsByPrefix(const char* name_prefix);