/* tox.c * * The Tox public API. * * Copyright (C) 2013 Tox project All Rights Reserved. * * This file is part of Tox. * * Tox 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 3 of the License, or * (at your option) any later version. * * Tox 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 Tox. If not, see . * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "Messenger.h" #include "group.h" #include "logger.h" #include "../toxencryptsave/defines.h" #define TOX_DEFINED typedef struct Messenger Tox; #include "tox.h" #define SET_ERROR_PARAMETER(param, x) {if(param) {*param = x;}} uint32_t tox_version_major(void) { return 0; } uint32_t tox_version_minor(void) { return 0; } uint32_t tox_version_patch(void) { return 0; } bool tox_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch) { //TODO return 1; } void tox_options_default(struct Tox_Options *options) { if (options) { memset(options, 0, sizeof(struct Tox_Options)); options->ipv6_enabled = 1; options->udp_enabled = 1; options->proxy_type = TOX_PROXY_TYPE_NONE; } } struct Tox_Options *tox_options_new(TOX_ERR_OPTIONS_NEW *error) { struct Tox_Options *options = calloc(sizeof(struct Tox_Options), 1); if (options) { SET_ERROR_PARAMETER(error, TOX_ERR_OPTIONS_NEW_OK); return options; } SET_ERROR_PARAMETER(error, TOX_ERR_OPTIONS_NEW_MALLOC); return NULL; } void tox_options_free(struct Tox_Options *options) { free(options); } Tox *tox_new(const struct Tox_Options *options, const uint8_t *data, size_t length, TOX_ERR_NEW *error) { if (!logger_get_global()) logger_set_global(logger_new(LOGGER_OUTPUT_FILE, LOGGER_LEVEL, "toxcore")); if (data) { if (memcmp(data, TOX_ENC_SAVE_MAGIC_NUMBER, TOX_ENC_SAVE_MAGIC_LENGTH) == 0) { SET_ERROR_PARAMETER(error, TOX_ERR_NEW_LOAD_ENCRYPTED); return NULL; } } Messenger_Options m_options = {0}; if (options == NULL) { m_options.ipv6enabled = TOX_ENABLE_IPV6_DEFAULT; } else { m_options.ipv6enabled = options->ipv6_enabled; m_options.udp_disabled = !options->udp_enabled; switch (options->proxy_type) { case TOX_PROXY_TYPE_HTTP: m_options.proxy_info.proxy_type = TCP_PROXY_HTTP; break; case TOX_PROXY_TYPE_SOCKS5: m_options.proxy_info.proxy_type = TCP_PROXY_SOCKS5; break; case TOX_PROXY_TYPE_NONE: m_options.proxy_info.proxy_type = TCP_PROXY_NONE; break; default: SET_ERROR_PARAMETER(error, TOX_ERR_PROXY_TYPE); return NULL; } if (m_options.proxy_info.proxy_type != TCP_PROXY_NONE) { if (options->proxy_port == 0) { SET_ERROR_PARAMETER(error, TOX_ERR_NEW_PROXY_BAD_PORT); return NULL; } ip_init(&m_options.proxy_info.ip_port.ip, m_options.ipv6enabled); if (m_options.ipv6enabled) m_options.proxy_info.ip_port.ip.family = AF_UNSPEC; if (!addr_resolve_or_parse_ip(options->proxy_address, &m_options.proxy_info.ip_port.ip, NULL)) { SET_ERROR_PARAMETER(error, TOX_ERR_NEW_PROXY_BAD_HOST); //TODO: TOX_ERR_NEW_PROXY_NOT_FOUND if domain. return NULL; } m_options.proxy_info.ip_port.port = htons(options->proxy_port); } } Messenger *m = new_messenger(&m_options); //TODO: TOX_ERR_NEW_MALLOC //TODO: TOX_ERR_NEW_PORT_ALLOC if (!new_groupchats(m)) { kill_messenger(m); SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC); return NULL; } if (messenger_load(m, data, length) == -1) { /* TODO: uncomment this when tox is stable. tox_kill(m); SET_ERROR_PARAMETER(error, TOX_ERR_NEW_LOAD_BAD_FORMAT); return NULL; */ } SET_ERROR_PARAMETER(error, TOX_ERR_NEW_OK); return m; } void tox_kill(Tox *tox) { Messenger *m = tox; kill_groupchats(m->group_chat_object); kill_messenger(m); logger_kill_global(); } size_t tox_save_size(const Tox *tox) { const Messenger *m = tox; return messenger_size(m); } void tox_save(const Tox *tox, uint8_t *data) { if (data) { const Messenger *m = tox; messenger_save(m, data); } } static int address_to_ip(Messenger *m, const char *address, IP_Port *ip_port, IP_Port *ip_port_v4) { if (!addr_parse_ip(address, &ip_port->ip)) { if (m->options.udp_disabled) { /* Disable DNS when udp is disabled. */ return -1; } IP *ip_extra = NULL; ip_init(&ip_port->ip, m->options.ipv6enabled); if (m->options.ipv6enabled && ip_port_v4) { /* setup for getting BOTH: an IPv6 AND an IPv4 address */ ip_port->ip.family = AF_UNSPEC; ip_reset(&ip_port_v4->ip); ip_extra = &ip_port_v4->ip; } if (!addr_resolve(address, &ip_port->ip, ip_extra)) { return -1; } } return 0; } bool tox_bootstrap(Tox *tox, const char *address, uint16_t port, const uint8_t *public_key, TOX_ERR_BOOTSTRAP *error) { if (!address || !public_key) { SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_NULL); return 0; } Messenger *m = tox; bool ret = tox_add_tcp_relay(tox, address, port, public_key, error); if (!ret) { return 0; } if (m->options.udp_disabled) { return ret; } else { /* DHT only works on UDP. */ if (DHT_bootstrap_from_address(m->dht, address, m->options.ipv6enabled, htons(port), public_key) == 0) { SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_BAD_ADDRESS); return 0; } SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_OK); return 1; } } bool tox_add_tcp_relay(Tox *tox, const char *address, uint16_t port, const uint8_t *public_key, TOX_ERR_BOOTSTRAP *error) { if (!address || !public_key) { SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_NULL); return 0; } Messenger *m = tox; IP_Port ip_port, ip_port_v4; if (port == 0) { SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_BAD_PORT); return 0; } if (address_to_ip(m, address, &ip_port, &ip_port_v4) == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_BAD_ADDRESS); return 0; } ip_port.port = htons(port); add_tcp_relay(m->net_crypto, ip_port, public_key); onion_add_bs_path_node(m->onion_c, ip_port, public_key); //TODO: move this SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_OK); return 1; } TOX_CONNECTION tox_get_connection_status(const Tox *tox) { const Messenger *m = tox; if (onion_isconnected(m->onion_c)) { if (DHT_non_lan_connected(m->dht)) { return TOX_CONNECTION_UDP; } return TOX_CONNECTION_TCP; } return TOX_CONNECTION_NONE; } void tox_callback_connection_status(Tox *tox, tox_connection_status_cb *function, void *user_data) { //TODO } uint32_t tox_iteration_interval(const Tox *tox) { const Messenger *m = tox; return messenger_run_interval(m); } void tox_iteration(Tox *tox) { Messenger *m = tox; do_messenger(m); do_groupchats(m->group_chat_object); } void tox_self_get_address(const Tox *tox, uint8_t *address) { if (address) { const Messenger *m = tox; getaddress(m, address); } } void tox_self_set_nospam(Tox *tox, uint32_t nospam) { Messenger *m = tox; set_nospam(&(m->fr), nospam); } uint32_t tox_self_get_nospam(const Tox *tox) { const Messenger *m = tox; return get_nospam(&(m->fr)); } void tox_self_get_public_key(const Tox *tox, uint8_t *public_key) { const Messenger *m = tox; if (public_key) memcpy(public_key, m->net_crypto->self_public_key, crypto_box_PUBLICKEYBYTES); } void tox_self_get_private_key(const Tox *tox, uint8_t *private_key) { const Messenger *m = tox; if (private_key) memcpy(private_key, m->net_crypto->self_secret_key, crypto_box_SECRETKEYBYTES); } bool tox_self_set_name(Tox *tox, const uint8_t *name, size_t length, TOX_ERR_SET_INFO *error) { if (!name && length != 0) { SET_ERROR_PARAMETER(error, TOX_ERR_SET_INFO_NULL); return 0; } Messenger *m = tox; if (setname(m, name, length) == 0) { //TODO: function to set different per group names? send_name_all_groups(m->group_chat_object); SET_ERROR_PARAMETER(error, TOX_ERR_SET_INFO_OK); return 1; } else { SET_ERROR_PARAMETER(error, TOX_ERR_SET_INFO_TOO_LONG); return 0; } } size_t tox_self_get_name_size(const Tox *tox) { const Messenger *m = tox; return m_get_self_name_size(m); } void tox_self_get_name(const Tox *tox, uint8_t *name) { if (name) { const Messenger *m = tox; getself_name(m, name); } } bool tox_self_set_status_message(Tox *tox, const uint8_t *status, size_t length, TOX_ERR_SET_INFO *error) { if (!status && length != 0) { SET_ERROR_PARAMETER(error, TOX_ERR_SET_INFO_NULL); return 0; } Messenger *m = tox; if (m_set_statusmessage(m, status, length) == 0) { SET_ERROR_PARAMETER(error, TOX_ERR_SET_INFO_OK); return 1; } else { SET_ERROR_PARAMETER(error, TOX_ERR_SET_INFO_TOO_LONG); return 0; } } size_t tox_self_get_status_message_size(const Tox *tox) { const Messenger *m = tox; return m_get_self_statusmessage_size(m); } void tox_self_get_status_message(const Tox *tox, uint8_t *status) { if (status) { const Messenger *m = tox; m_copy_self_statusmessage(m, status); } } void tox_self_set_status(Tox *tox, TOX_STATUS user_status) { Messenger *m = tox; m_set_userstatus(m, user_status); } TOX_STATUS tox_self_get_status(const Tox *tox) { const Messenger *m = tox; return m_get_self_userstatus(m); } static void set_friend_error(int32_t ret, TOX_ERR_FRIEND_ADD *error) { switch (ret) { case FAERR_TOOLONG: SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_TOO_LONG); break; case FAERR_NOMESSAGE: SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_NO_MESSAGE); break; case FAERR_OWNKEY: SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_OWN_KEY); break; case FAERR_ALREADYSENT: SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_ALREADY_SENT); break; case FAERR_BADCHECKSUM: SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_BAD_CHECKSUM); break; case FAERR_SETNEWNOSPAM: SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_SET_NEW_NOSPAM); break; case FAERR_NOMEM: SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_MALLOC); break; } } uint32_t tox_friend_add(Tox *tox, const uint8_t *address, const uint8_t *message, size_t length, TOX_ERR_FRIEND_ADD *error) { if (!address || !message) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_NULL); return UINT32_MAX; } Messenger *m = tox; int32_t ret = m_addfriend(m, address, message, length); if (ret >= 0) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_OK); return ret; } set_friend_error(ret, error); return UINT32_MAX; } uint32_t tox_friend_add_norequest(Tox *tox, const uint8_t *public_key, TOX_ERR_FRIEND_ADD *error) { if (!public_key) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_NULL); return UINT32_MAX; } Messenger *m = tox; int32_t ret = m_addfriend_norequest(m, public_key); if (ret >= 0) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_ADD_OK); return ret; } set_friend_error(ret, error); return UINT32_MAX; } bool tox_friend_delete(Tox *tox, uint32_t friend_number, TOX_ERR_FRIEND_DELETE *error) { Messenger *m = tox; int ret = m_delfriend(m, friend_number); //TODO handle if realloc fails? if (ret == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_DELETE_FRIEND_NOT_FOUND); return 0; } SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_DELETE_OK); return 1; } uint32_t tox_friend_by_public_key(const Tox *tox, const uint8_t *public_key, TOX_ERR_FRIEND_BY_PUBLIC_KEY *error) { if (!public_key) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_BY_PUBLIC_KEY_NULL); return UINT32_MAX; } const Messenger *m = tox; int32_t ret = getfriend_id(m, public_key); if (ret == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_BY_PUBLIC_KEY_NOT_FOUND); return UINT32_MAX; } SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_BY_PUBLIC_KEY_OK); return ret; } bool tox_friend_get_public_key(const Tox *tox, uint32_t friend_number, uint8_t *public_key, TOX_ERR_FRIEND_GET_PUBLIC_KEY *error) { if (!public_key) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_GET_PUBLIC_KEY_NULL); return 0; } const Messenger *m = tox; if (get_real_pk(m, friend_number, public_key) == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_GET_PUBLIC_KEY_FRIEND_NOT_FOUND); return 0; } SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_GET_PUBLIC_KEY_OK); return 1; } bool tox_friend_exists(const Tox *tox, uint32_t friend_number) { const Messenger *m = tox; return m_friend_exists(m, friend_number); } size_t tox_friend_list_size(const Tox *tox) { const Messenger *m = tox; return count_friendlist(m); } void tox_friend_list(const Tox *tox, uint32_t *list) { if (list) { const Messenger *m = tox; //TODO: size parameter? copy_friendlist(m, list, tox_friend_list_size(tox)); } } size_t tox_friend_get_name_size(const Tox *tox, uint32_t friend_number, TOX_ERR_FRIEND_QUERY *error) { const Messenger *m = tox; int ret = m_get_name_size(m, friend_number); if (ret == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND); return SIZE_MAX; } SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_OK); return ret; } bool tox_friend_get_name(const Tox *tox, uint32_t friend_number, uint8_t *name, TOX_ERR_FRIEND_QUERY *error) { if (!name) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_NULL); return 0; } const Messenger *m = tox; int ret = getname(m, friend_number, name); if (ret == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND); return 0; } SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_OK); return 1; } void tox_callback_friend_name(Tox *tox, tox_friend_name_cb *function, void *user_data) { Messenger *m = tox; m_callback_namechange(m, function, user_data); } size_t tox_friend_get_status_message_size(const Tox *tox, uint32_t friend_number, TOX_ERR_FRIEND_QUERY *error) { const Messenger *m = tox; int ret = m_get_statusmessage_size(m, friend_number); if (ret == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND); return SIZE_MAX; } SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_OK); return ret; } bool tox_friend_get_status_message(const Tox *tox, uint32_t friend_number, uint8_t *message, TOX_ERR_FRIEND_QUERY *error) { if (!message) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_NULL); return 0; } const Messenger *m = tox; //TODO: size parameter? int ret = m_copy_statusmessage(m, friend_number, message, m_get_statusmessage_size(m, friend_number)); if (ret == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND); return 0; } SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_OK); return 1; } void tox_callback_friend_status_message(Tox *tox, tox_friend_status_message_cb *function, void *user_data) { Messenger *m = tox; m_callback_statusmessage(m, function, user_data); } TOX_STATUS tox_friend_get_status(const Tox *tox, uint32_t friend_number, TOX_ERR_FRIEND_QUERY *error) { const Messenger *m = tox; int ret = m_get_userstatus(m, friend_number); if (ret == USERSTATUS_INVALID) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND); return TOX_STATUS_INVALID; } SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_OK); return ret; } void tox_callback_friend_status(Tox *tox, tox_friend_status_cb *function, void *user_data) { Messenger *m = tox; m_callback_userstatus(m, function, user_data); } TOX_CONNECTION tox_friend_get_connection_status(const Tox *tox, uint32_t friend_number, TOX_ERR_FRIEND_QUERY *error) { const Messenger *m = tox; int ret = m_get_friend_connectionstatus(m, friend_number); if (ret == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND); return TOX_CONNECTION_NONE; } SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_OK); return ret; } void tox_callback_friend_connection_status(Tox *tox, tox_friend_connection_status_cb *function, void *user_data) { Messenger *m = tox; m_callback_connectionstatus(m, function, user_data); } bool tox_friend_get_typing(const Tox *tox, uint32_t friend_number, TOX_ERR_FRIEND_QUERY *error) { const Messenger *m = tox; int ret = m_get_istyping(m, friend_number); if (ret == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_FRIEND_NOT_FOUND); return 0; } SET_ERROR_PARAMETER(error, TOX_ERR_FRIEND_QUERY_OK); return !!ret; } void tox_callback_friend_typing(Tox *tox, tox_friend_typing_cb *function, void *user_data) { Messenger *m = tox; m_callback_typingchange(m, function, user_data); } bool tox_self_set_typing(Tox *tox, uint32_t friend_number, bool is_typing, TOX_ERR_SET_TYPING *error) { Messenger *m = tox; if (m_set_usertyping(m, friend_number, is_typing) == -1) { SET_ERROR_PARAMETER(error, TOX_ERR_SET_TYPING_FRIEND_NOT_FOUND); return 0; } SET_ERROR_PARAMETER(error, TOX_ERR_SET_TYPING_OK); return 1; } static void set_message_error(int ret, TOX_ERR_SEND_MESSAGE *error) { switch (ret) { case 0: SET_ERROR_PARAMETER(error, TOX_ERR_SEND_MESSAGE_OK); break; case -1: SET_ERROR_PARAMETER(error, TOX_ERR_SEND_MESSAGE_FRIEND_NOT_FOUND); break; case -2: SET_ERROR_PARAMETER(error, TOX_ERR_SEND_MESSAGE_TOO_LONG); break; case -3: SET_ERROR_PARAMETER(error, TOX_ERR_SEND_MESSAGE_FRIEND_NOT_CONNECTED); break; case -4: SET_ERROR_PARAMETER(error, TOX_ERR_SEND_MESSAGE_SENDQ); break; } } uint32_t tox_send_message(Tox *tox, uint32_t friend_number, const uint8_t *message, size_t length, TOX_ERR_SEND_MESSAGE *error) { if (!message) { SET_ERROR_PARAMETER(error, TOX_ERR_SEND_MESSAGE_NULL); return 0; } if (!length) { SET_ERROR_PARAMETER(error, TOX_ERR_SEND_MESSAGE_EMPTY); return 0; } Messenger *m = tox; uint32_t message_id = 0; set_message_error(m_sendmessage(m, friend_number, message, length, &message_id), error); return message_id; } uint32_t tox_send_action(Tox *tox, uint32_t friend_number, const uint8_t *action, size_t length, TOX_ERR_SEND_MESSAGE *error) { if (!action) { SET_ERROR_PARAMETER(error, TOX_ERR_SEND_MESSAGE_NULL); return 0; } if (!length) { SET_ERROR_PARAMETER(error, TOX_ERR_SEND_MESSAGE_EMPTY); return 0; } Messenger *m = tox; uint32_t message_id = 0; set_message_error(m_sendaction(m, friend_number, action, length, &message_id), error); return message_id; } void tox_callback_read_receipt(Tox *tox, tox_read_receipt_cb *function, void *user_data) { Messenger *m = tox; m_callback_read_receipt(m, function, user_data); } void tox_callback_friend_request(Tox *tox, tox_friend_request_cb *function, void *user_data) { Messenger *m = tox; m_callback_friendrequest(m, function, user_data); } void tox_callback_friend_message(Tox *tox, tox_friend_message_cb *function, void *user_data) { Messenger *m = tox; m_callback_friendmessage(m, function, user_data); } void tox_callback_friend_action(Tox *tox, tox_friend_action_cb *function, void *user_data) { Messenger *m = tox; m_callback_action(m, function, user_data); } static void set_custom_packet_error(int ret, TOX_ERR_SEND_CUSTOM_PACKET *error) { switch (ret) { case 0: SET_ERROR_PARAMETER(error, TOX_ERR_SEND_CUSTOM_PACKET_OK); break; case -1: SET_ERROR_PARAMETER(error, TOX_ERR_SEND_CUSTOM_PACKET_FRIEND_NOT_FOUND); break; case -2: SET_ERROR_PARAMETER(error, TOX_ERR_SEND_CUSTOM_PACKET_TOO_LONG); break; case -3: SET_ERROR_PARAMETER(error, TOX_ERR_SEND_CUSTOM_PACKET_INVALID); break; case -4: SET_ERROR_PARAMETER(error, TOX_ERR_SEND_CUSTOM_PACKET_FRIEND_NOT_CONNECTED); break; case -5: SET_ERROR_PARAMETER(error, TOX_ERR_SEND_CUSTOM_PACKET_SENDQ); break; } } bool tox_send_lossy_packet(Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length, TOX_ERR_SEND_CUSTOM_PACKET *error) { if (!data) { SET_ERROR_PARAMETER(error, TOX_ERR_SEND_CUSTOM_PACKET_NULL); return 0; } Messenger *m = tox; if (length == 0) { SET_ERROR_PARAMETER(error, TOX_ERR_SEND_CUSTOM_PACKET_EMPTY); return 0; } if (data[0] < (PACKET_ID_LOSSY_RANGE_START + PACKET_LOSSY_AV_RESERVED)) { SET_ERROR_PARAMETER(error, TOX_ERR_SEND_CUSTOM_PACKET_INVALID); return 0; } int ret = send_custom_lossy_packet(m, friend_number, data, length); set_custom_packet_error(ret, error); if (ret == 0) { return 1; } else { return 0; } } void tox_callback_friend_lossy_packet(Tox *tox, tox_friend_lossy_packet_cb *function, void *user_data) { Messenger *m = tox; custom_lossy_packet_registerhandler(m, function, user_data); } bool tox_send_lossless_packet(Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length, TOX_ERR_SEND_CUSTOM_PACKET *error) { if (!data) { SET_ERROR_PARAMETER(error, TOX_ERR_SEND_CUSTOM_PACKET_NULL); return 0; } Messenger *m = tox; if (length == 0) { SET_ERROR_PARAMETER(error, TOX_ERR_SEND_CUSTOM_PACKET_EMPTY); return 0; } int ret = send_custom_lossless_packet(m, friend_number, data, length); set_custom_packet_error(ret, error); if (ret == 0) { return 1; } else { return 0; } } void tox_callback_friend_lossless_packet(Tox *tox, tox_friend_lossless_packet_cb *function, void *user_data) { Messenger *m = tox; custom_lossless_packet_registerhandler(m, function, user_data); }