/* Serval DNA MeshMS Copyright (C) 2014 Serval Project Inc. 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. */ #ifndef __SERVAL_DNA__MESHMS_H #define __SERVAL_DNA__MESHMS_H #ifndef __MESHMS_INLINE # if __GNUC__ && !__GNUC_STDC_INLINE__ # define __MESHMS_INLINE extern inline # else # define __MESHMS_INLINE inline # endif #endif #include "rhizome.h" #define MESHMS_MESSAGE_MAX_LEN 4095 /* The result of a MeshMS operation. Negative indicates failure, zero or * positive success. */ enum meshms_status { MESHMS_STATUS_ERROR = -1, // unexpected error (underlying failure) MESHMS_STATUS_OK = 0, // operation succeeded, no bundle changed MESHMS_STATUS_UPDATED = 1, // operation succeeded, bundle updated MESHMS_STATUS_SID_LOCKED = 2, // cannot decode or send messages for that SID MESHMS_STATUS_PROTOCOL_FAULT = 3, // missing or faulty ply bundle }; __MESHMS_INLINE int meshms_failed(enum meshms_status status) { return status != MESHMS_STATUS_OK && status != MESHMS_STATUS_UPDATED; } const char *meshms_status_message(enum meshms_status); // the manifest details for one half of a conversation struct meshms_ply { rhizome_bid_t bundle_id; uint64_t version; uint64_t tail; uint64_t size; }; struct meshms_conversations { // binary tree struct meshms_conversations *_left; struct meshms_conversations *_right; // keeping a pointer to parent node here means the traversal iterator does not need a stack, so // there is no fixed limit on the tree depth struct meshms_conversations *_parent; // who are we talking to? sid_t them; char found_my_ply; struct meshms_ply my_ply; char found_their_ply; struct meshms_ply their_ply; // what is the offset of their last message uint64_t their_last_message; // what is the last message we marked as read uint64_t read_offset; // our cached value for the last known size of their ply uint64_t their_size; }; // cursor state for reading one half of a conversation struct meshms_ply_read { // rhizome payload struct rhizome_read read; // block buffer struct rhizome_read_buffer buff; // details of the current record uint64_t record_end_offset; uint16_t record_length; size_t record_size; char type; // raw record data unsigned char *record; }; /* Fetch the list of all MeshMS conversations into a binary tree whose nodes * are all allocated by malloc(3). */ enum meshms_status meshms_conversations_list(const sid_t *my_sid, const sid_t *their_sid, struct meshms_conversations **conv); void meshms_free_conversations(struct meshms_conversations *conv); /* For iterating over a binary tree of all MeshMS conversations, as created by * meshms_conversations_list(). * * struct meshms_conversation_iterator it; * meshms_conversation_iterator_start(&it, conv); * while (it.current) { * ... * meshms_conversation_iterator_advance(&it); * } */ struct meshms_conversation_iterator { struct meshms_conversations *current; }; void meshms_conversation_iterator_start(struct meshms_conversation_iterator *, struct meshms_conversations *); void meshms_conversation_iterator_advance(struct meshms_conversation_iterator *); /* For iterating through the messages in a single MeshMS conversation; both * plys threaded (interleaved) in the order as seen by the sender. The * meshms_message_iterator_prev() function returns MESHMS_STATUS_UPDATED if it * advances the iterator to a message, or MESHMS_STATUS_OK if there are no more * messages. Any other return value indicates failure. * * struct meshms_message_iterator it; * enum meshms_status status; * if (meshms_failed(status = meshms_message_iterator_open(&it, &sender_sid, &recip_sid))) * return -1; * while ((status = meshms_message_iterator_prev(&it)) == MESHMS_STATUS_UPDATED) { * ... * } * meshms_message_iterator_close(&it); * if (meshms_failed(status)) * return -1; * ... */ struct meshms_message_iterator { // Public fields that remain fixed for the life of the iterator: const sid_t *my_sid; const sid_t *their_sid; const rhizome_bid_t *my_ply_bid; const rhizome_bid_t *their_ply_bid; uint64_t latest_ack_offset; // offset in remote (their) ply of most recent ACK uint64_t latest_ack_my_offset; // offset in my ply of most recent message ACKed by them uint64_t read_offset; // offset in remote (their) ply of most recent message read by me // The following public fields change per message: enum meshms_which_ply { MY_PLY, THEIR_PLY } which_ply; enum { MESSAGE_SENT, MESSAGE_RECEIVED, ACK_RECEIVED } type; // For MESSAGE_SENT 'offset' is the byte position within the local ply // (mine). For MESSAGE_RECEIVED and ACK_RECEIVED, it is the byte position // within the remote ply (theirs). uint64_t offset; const char *text; // text of UTF8 message (NUL terminated) size_t text_length; // excluding terminating NUL union { bool_t delivered; // for MESSAGE_SENT bool_t read; // for MESSAGE_RECEIVED uint64_t ack_offset; // for ACK_RECEIVED }; // Private implementation -- could change, so don't use them. sid_t _my_sid; struct meshms_conversations *_conv; rhizome_manifest *_my_manifest; rhizome_manifest *_their_manifest; struct meshms_ply_read _my_reader; struct meshms_ply_read _their_reader; uint64_t _end_range; bool_t _in_ack; }; enum meshms_status meshms_message_iterator_open(struct meshms_message_iterator *, const sid_t *me, const sid_t *them); int meshms_message_iterator_is_open(const struct meshms_message_iterator *); void meshms_message_iterator_close(struct meshms_message_iterator *); enum meshms_status meshms_message_iterator_prev(struct meshms_message_iterator *); /* Append a message ('message_len' bytes of UTF8 at 'message') to the sender's * ply in the conversation between 'sender' and 'recipient'. If no * conversation (ply bundle) exists, then create it. Returns * MESHMS_STATUS_UPDATED on success, any other value indicates a failure or * error (which is already logged). */ enum meshms_status meshms_send_message(const sid_t *sender, const sid_t *recipient, const char *message, size_t message_len); /* Update the read offset for one or more conversations. Returns * MESHMS_STATUS_UPDATED on success, any other value indicates a failure or * error (which is already logged). * * If 'offset' is greater than a conversation's last-received offset, then it * is clamped to the last-received offset. This means that passing an offset * of UINT64_MAX will mark the conversation as fully read, and an offset of * zero will have no effect. * * If 'recipient' is NULL then all of the sender's conversations are marked * with the given read offset. In this case it only makes sense to pass an * offest of UINT64_MAX. */ enum meshms_status meshms_mark_read(const sid_t *sender, const sid_t *recipient, uint64_t offset); #endif // __SERVAL_DNA__MESHMS_H