diff --git a/CMakeLists.txt b/CMakeLists.txt index 2452e80..e501edf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,7 @@ add_library(tox MODULE src/twc-commands.c src/twc-completion.c src/twc-config.c + src/twc-data.c src/twc-friend-request.c src/twc-gui.c src/twc-list.c @@ -50,6 +51,7 @@ add_library(tox MODULE set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -Werror-implicit-function-declaration -Wno-unused-parameter") target_link_libraries(tox toxcore) +target_link_libraries(tox jansson) # remove lib prefix (libtox.so -> tox.so) set_target_properties(tox PROPERTIES PREFIX "") diff --git a/README.md b/README.md index 2cb3a09..c846d64 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,24 @@ Tox-WeeChat =========== -Tox-WeeChat is a plugin for [WeeChat][1] that enables it to connect to the [Tox][2] network. It is functional, but currently only intended for experimental use. +Tox-WeeChat is a [Tox][1] protocol plugin for [WeeChat][2]. It is functional, but lacks certain features like Tox DNS (e.g. user@toxme.se) and group chats. -Current build status: [![Build Status](https://travis-ci.org/haavardp/tox-weechat.svg?branch=master)](https://travis-ci.org/haavardp/tox-weechat) +Current build status: [![Build Status](https://travis-ci.org/haavardp/tox-weechat.svg?branch=master)][3] Installation ------------ -> Tox-WeeChat is available in the [AUR][3]. +> Tox-WeeChat is available in the [AUR][4]. -Tox-WeeChat requires [WeeChat][1] >= 1.0.1, [libjansson][4] >= 2.5 and the latest-ish [libtoxcore][5]. It also requires CMake to be built. Installation is fairly simple; after getting the source, compile and install using CMake: +Tox-WeeChat requires [WeeChat][1] >= 1.0.1, [libjansson][5] >= 2.5 and the latest-ish [libtoxcore][6]. It also requires CMake to be built. Installation is fairly simple; after getting the source, compile and install using CMake: $ mkdir build && cd build $ cmake -DHOME_FOLDER_INSTALL=ON .. - $ make $ make install This installs the plugin binary `tox.so` to the recommended location `~/.weechat/plugins`. Without the home folder flag, the binary is placed in `/usr/local/lib/weechat/plugins`. Installing to a custom WeeChat folder or elsewhere is achieved by setting `INSTALL_PATH`. Usage ----- - - If the plugin does not automatically load, load it with `/plugin load tox`. You may have to specify the full path to the plugin binary. + - If the plugin does not load automatically, load it with `/plugin load tox`. You may have to specify the full path to the plugin binary. - Create a new profile with `/tox create `. The data file is stored in `~/.weechat/tox/` by default. - Load your profile and connect to the Tox network with `/tox load `. - Change your name with `/name `. @@ -27,7 +26,7 @@ Usage - To add friends or respond to friend requests, `/help friend` will get you started. - Message a friend with `/msg `. Get their friend number with `/friend list`. -Run `/help -list tox` to get a list of all available commands. +Run `/help -listfull tox` to get a list of all available commands. TODO ---- @@ -37,7 +36,7 @@ TODO - Group chats - Support proxies (e.g. TOR) - Support WeeChat `/upgrade` - - A/V + - Audio/video chats License --------- @@ -58,9 +57,10 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Tox-WeeChat. If not, see . -[1]: http://weechat.org -[2]: http://tox.im -[3]: https://aur.archlinux.org/packages/tox-weechat-git -[3]: http://www.digip.org/jansson -[5]: https://github.com/irungentoo/toxcore +[1]: http://tox.im +[2]: http://weechat.org +[3]: https://travis-ci.org/haavardp/tox-weechat +[4]: https://aur.archlinux.org/packages/tox-weechat-git +[5]: http://www.digip.org/jansson +[6]: https://github.com/irungentoo/toxcore diff --git a/src/twc-data.c b/src/twc-data.c index 1574530..ccfe8f4 100644 --- a/src/twc-data.c +++ b/src/twc-data.c @@ -17,3 +17,294 @@ * along with Tox-WeeChat. If not, see . */ +#include +#include +#include + +#include "twc.h" +#include "twc-list.h" +#include "twc-profile.h" +#include "twc-friend-request.h" +#include "twc-message-queue.h" +#include "twc-utils.h" + +#include "twc-data.h" + +// TODO: move to config +#define twc_JSON_CONFIG_PATH "%h/tox/data.json" + +const char *twc_json_key_friend_requests = "freqs"; +const char *twc_json_friend_request_key_tox_id = "tox_id"; +const char *twc_json_friend_request_key_message = "msg"; +const char *twc_json_key_message_queues = "msg_q"; +const char *twc_json_key_message_queue_key_message = "msg"; +const char *twc_json_key_message_queue_key_message_type = "type"; +const char *twc_json_key_message_queue_key_message_timestamp = "time"; + +/** + * The object holding the JSON config data. + */ +json_t *twc_json_data = NULL; + +/** + * Return the full path to the config file. Return value must be freed. + */ +char * +twc_data_json_path() +{ + const char *weechat_dir = weechat_info_get("weechat_dir", NULL); + return weechat_string_replace(twc_JSON_CONFIG_PATH, "%h", weechat_dir); +} + +/** + * Return the key used for a profile in the JSON data file. Must be freed. + */ +char * +twc_data_profile_key(struct t_twc_profile *profile) +{ + uint8_t address[TOX_FRIEND_ADDRESS_SIZE]; + tox_get_address(profile->tox, address); + + char *hex_id = malloc(TOX_CLIENT_ID_SIZE * 2 + 1); + twc_bin2hex(address, TOX_CLIENT_ID_SIZE, hex_id); + + return hex_id; +} + +/** + * Save an profile's friend requests to a json array. Returns a new + * reference. + */ +json_t * +twc_data_save_profile_friend_requests(struct t_twc_profile *profile) +{ + json_t *friend_request_array = json_array(); + if (!friend_request_array) + return NULL; + + size_t index; + struct t_twc_list_item *item; + twc_list_foreach(profile->friend_requests, index, item) + { + char hex_id[TOX_CLIENT_ID_SIZE * 2 + 1]; + twc_bin2hex(item->friend_request->tox_id, TOX_CLIENT_ID_SIZE, hex_id); + + json_t *json_request = json_object(); + json_t *json_id = json_string(hex_id); + json_t *json_message = json_string(item->friend_request->message); + if (!json_request || !json_id || !json_message) + break; + + json_object_set_new(json_request, + twc_json_friend_request_key_tox_id, + json_id); + + json_object_set_new(json_request, + twc_json_friend_request_key_message, + json_message); + + json_array_append_new(friend_request_array, json_request); + } + + return friend_request_array; +} + +void +twc_data_save_profile_message_queues_map_callback(void *data, + struct t_hashtable *hashtable, + const void *key, const void *value) +{ + struct t_twc_list *message_queue = ((struct t_twc_list *)value); + json_t *json_message_queues = data; + + json_t *json_message_queue = json_array(); + if (!json_message_queue) + return; + + size_t index; + struct t_twc_list_item *item; + twc_list_foreach(message_queue, index, item) + { + struct t_twc_queued_message *queued_message = item->queued_message; + + json_t *message = json_string(queued_message->message); + json_t *message_type = json_integer(queued_message->message_type); + json_t *timestamp = json_integer(mktime(queued_message->time)); + json_t *message_object = json_object(); + + if (!message || !message_type || !timestamp || !message_object) + return; + + json_object_set_new(message_object, + twc_json_key_message_queue_key_message, + message); + json_object_set_new(message_object, + twc_json_key_message_queue_key_message_type, + message_type); + json_object_set_new(message_object, + twc_json_key_message_queue_key_message_timestamp, + timestamp); + + json_array_append_new(json_message_queue, message_object); + } + + json_object_set_new(json_message_queues, key, json_message_queue); +} + +/** + * Save a profile's message queue to a json object. Returns a new reference. + */ +json_t * +twc_data_save_profile_message_queues(struct t_twc_profile *profile) +{ + json_t *message_queues = json_object(); + + weechat_hashtable_map(profile->message_queues, + twc_data_save_profile_message_queues_map_callback, + message_queues); + + return message_queues; +} + +/** + * Save a profile's data. + */ +void +twc_data_save_profile(struct t_twc_profile *profile) +{ + json_t *json_data = json_object(); + if (!json_data) + return; + + json_t *friend_requests = twc_data_save_profile_friend_requests(profile); + json_object_set_new(json_data, + twc_json_key_friend_requests, + friend_requests); + + json_t *message_queues = twc_data_save_profile_message_queues(profile); + json_object_set_new(json_data, + twc_json_key_message_queues, + message_queues); + + char *profile_key = twc_data_profile_key(profile); + json_object_set_new(twc_json_data, profile_key, json_data); + free(profile_key); +} + +/** + * Load friend requests from a json array. + */ +void +twc_data_load_profile_friend_requests(struct t_twc_profile *profile, + json_t *friend_requests) +{ + twc_friend_request_free_profile(profile); + + size_t index; + json_t *json_request; + json_array_foreach(friend_requests, index, json_request) + { + char client_id[TOX_CLIENT_ID_SIZE]; + const char *message; + + json_t *json_id = json_object_get(json_request, + twc_json_friend_request_key_tox_id); + json_t *json_message = json_object_get(json_request, + twc_json_friend_request_key_message); + + twc_hex2bin(json_string_value(json_id), TOX_CLIENT_ID_SIZE, client_id); + message = json_string_value(json_message); + + twc_friend_request_add(profile, + (uint8_t *)client_id, + message); + } +} + +/** + * Load message queues from a json object. + */ +void +twc_data_load_profile_message_queues(struct t_twc_profile *profile, + json_t *message_queues) +{ + twc_message_queue_free_profile(profile); + + const char *key; + json_t *message_queue; + json_object_foreach(message_queues, key, message_queue) + { + + } +} + +/** + * Load an profile's data from a JSON object. + */ +void +twc_data_profile_load(struct t_twc_identity *identity) +{ + char *profile_key = twc_json_get_identity_key(identity); + json_t *profile_data = json_object_get(twc_json_data, identity_key); + free(profile_key); + + json_t *friend_requests = json_object_get(profile_data, + twc_json_key_friend_requests); + if (friend_requests) + twc_data_load_friend_requests(profile, friend_requests); + + json_t *unsent_messages = json_object_get(profile_data, + twc_json_key_unsent_messages); + if (unsent_messages) + twc_data_load_unsent_messages(profile, unsent_messages); +} + +/** + * Load the data on disk into memory. + */ +void +twc_data_load() +{ + char *full_path = twc_json_data_file_path(); + + + json_error_t error; + twc_json_data = json_load_file(full_path, 0, &error); + free(full_path); + + if (!twc_json_data) + { + weechat_printf(NULL, + "%s%s: could not load on-disk data", + weechat_prefix("error"), + weechat_plugin->name); + + twc_json_data = json_object(); + } +} + +/** + * Save all in-memory data to data on disk. Return 0 on success, -1 on + * error. + */ +int +twc_data_save() +{ + char *full_path = twc_json_data_file_path(); + int rc = json_dump_file(twc_json_data, + full_path, + 0); + free(full_path); + + return rc; +} + +/** + * Free in-memory JSON data. + */ +void +twc_data_free() +{ + json_decref(twc_json_data); +} + diff --git a/src/twc-data.h b/src/twc-data.h index 28f4310..4508d77 100644 --- a/src/twc-data.h +++ b/src/twc-data.h @@ -20,5 +20,24 @@ #ifndef TOX_WEECHAT_DATA_H #define TOX_WEECHAT_DATA_H -#endif // TOX_WEECHAT_DATA_H +struct t_twc_profile; +void +twc_data_read(); + +int +twc_data_write(); + +void +twc_data_load_profile(struct t_twc_profile *profile); + +void +twc_data_save_profile(struct t_twc_profile *profile); + +void +twc_data_delete_profile(struct t_twc_profile *profile); + +void +twc_data_free(); + +#endif // TOX_WEECHAT_DATA_H diff --git a/src/twc-message-queue.c b/src/twc-message-queue.c index 447fa59..68e8b0c 100644 --- a/src/twc-message-queue.c +++ b/src/twc-message-queue.c @@ -155,6 +155,9 @@ twc_message_queue_free_map_callback(void *data, struct t_hashtable *hashtable, free(message_queue); } +/** + * Free the entire message queue for a profile. + */ void twc_message_queue_free_profile(struct t_twc_profile *profile) {