diff options
Diffstat (limited to 'toxcore')
-rw-r--r-- | toxcore/Messenger.c | 474 | ||||
-rw-r--r-- | toxcore/Messenger.h | 195 | ||||
-rw-r--r-- | toxcore/tox.c | 51 | ||||
-rw-r--r-- | toxcore/tox.h | 144 |
4 files changed, 861 insertions, 3 deletions
diff --git a/toxcore/Messenger.c b/toxcore/Messenger.c index 21cb2671..3d96f6ca 100644 --- a/toxcore/Messenger.c +++ b/toxcore/Messenger.c | |||
@@ -42,6 +42,7 @@ | |||
42 | static void set_friend_status(Messenger *m, int32_t friendnumber, uint8_t status); | 42 | static void set_friend_status(Messenger *m, int32_t friendnumber, uint8_t status); |
43 | static int write_cryptpacket_id(const Messenger *m, int32_t friendnumber, uint8_t packet_id, const uint8_t *data, | 43 | static int write_cryptpacket_id(const Messenger *m, int32_t friendnumber, uint8_t packet_id, const uint8_t *data, |
44 | uint32_t length, uint8_t congestion_control); | 44 | uint32_t length, uint8_t congestion_control); |
45 | static int send_avatar_data_control(const Messenger *m, const uint32_t friendnumber, uint8_t op); | ||
45 | 46 | ||
46 | // friend_not_valid determines if the friendnumber passed is valid in the Messenger object | 47 | // friend_not_valid determines if the friendnumber passed is valid in the Messenger object |
47 | static uint8_t friend_not_valid(const Messenger *m, int32_t friendnumber) | 48 | static uint8_t friend_not_valid(const Messenger *m, int32_t friendnumber) |
@@ -247,6 +248,10 @@ int32_t m_addfriend(Messenger *m, const uint8_t *address, const uint8_t *data, u | |||
247 | m->friendlist[i].statusmessage = calloc(1, 1); | 248 | m->friendlist[i].statusmessage = calloc(1, 1); |
248 | m->friendlist[i].statusmessage_length = 1; | 249 | m->friendlist[i].statusmessage_length = 1; |
249 | m->friendlist[i].userstatus = USERSTATUS_NONE; | 250 | m->friendlist[i].userstatus = USERSTATUS_NONE; |
251 | m->friendlist[i].avatar_info_sent = 0; | ||
252 | m->friendlist[i].avatar_recv_data = NULL; | ||
253 | m->friendlist[i].avatar_send_data.bytes_sent = 0; | ||
254 | m->friendlist[i].avatar_send_data.last_reset = 0; | ||
250 | m->friendlist[i].is_typing = 0; | 255 | m->friendlist[i].is_typing = 0; |
251 | memcpy(m->friendlist[i].info, data, length); | 256 | memcpy(m->friendlist[i].info, data, length); |
252 | m->friendlist[i].info_size = length; | 257 | m->friendlist[i].info_size = length; |
@@ -299,6 +304,10 @@ int32_t m_addfriend_norequest(Messenger *m, const uint8_t *client_id) | |||
299 | m->friendlist[i].statusmessage = calloc(1, 1); | 304 | m->friendlist[i].statusmessage = calloc(1, 1); |
300 | m->friendlist[i].statusmessage_length = 1; | 305 | m->friendlist[i].statusmessage_length = 1; |
301 | m->friendlist[i].userstatus = USERSTATUS_NONE; | 306 | m->friendlist[i].userstatus = USERSTATUS_NONE; |
307 | m->friendlist[i].avatar_info_sent = 0; | ||
308 | m->friendlist[i].avatar_recv_data = NULL; | ||
309 | m->friendlist[i].avatar_send_data.bytes_sent = 0; | ||
310 | m->friendlist[i].avatar_send_data.last_reset = 0; | ||
302 | m->friendlist[i].is_typing = 0; | 311 | m->friendlist[i].is_typing = 0; |
303 | m->friendlist[i].message_id = 0; | 312 | m->friendlist[i].message_id = 0; |
304 | m->friendlist[i].receives_read_receipts = 1; /* Default: YES. */ | 313 | m->friendlist[i].receives_read_receipts = 1; /* Default: YES. */ |
@@ -330,6 +339,7 @@ int m_delfriend(Messenger *m, int32_t friendnumber) | |||
330 | onion_delfriend(m->onion_c, m->friendlist[friendnumber].onion_friendnum); | 339 | onion_delfriend(m->onion_c, m->friendlist[friendnumber].onion_friendnum); |
331 | crypto_kill(m->net_crypto, m->friendlist[friendnumber].crypt_connection_id); | 340 | crypto_kill(m->net_crypto, m->friendlist[friendnumber].crypt_connection_id); |
332 | free(m->friendlist[friendnumber].statusmessage); | 341 | free(m->friendlist[friendnumber].statusmessage); |
342 | free(m->friendlist[friendnumber].avatar_recv_data); | ||
333 | remove_request_received(&(m->fr), m->friendlist[friendnumber].client_id); | 343 | remove_request_received(&(m->fr), m->friendlist[friendnumber].client_id); |
334 | memset(&(m->friendlist[friendnumber]), 0, sizeof(Friend)); | 344 | memset(&(m->friendlist[friendnumber]), 0, sizeof(Friend)); |
335 | uint32_t i; | 345 | uint32_t i; |
@@ -564,6 +574,131 @@ int m_set_userstatus(Messenger *m, uint8_t status) | |||
564 | return 0; | 574 | return 0; |
565 | } | 575 | } |
566 | 576 | ||
577 | int m_set_avatar(Messenger *m, uint8_t format, const uint8_t *data, uint32_t length) | ||
578 | { | ||
579 | if (length > MAX_AVATAR_DATA_LENGTH) | ||
580 | return -1; | ||
581 | |||
582 | if (format == AVATARFORMAT_NONE) { | ||
583 | free(m->avatar_data); | ||
584 | m->avatar_data = NULL; | ||
585 | m->avatar_data_length = 0; | ||
586 | m->avatar_format = format; | ||
587 | memset(m->avatar_hash, 0, AVATAR_HASH_LENGTH); | ||
588 | } else { | ||
589 | if (length == 0 || data == NULL) | ||
590 | return -1; | ||
591 | |||
592 | uint8_t *tmp = realloc(m->avatar_data, length); | ||
593 | |||
594 | if (tmp == NULL) | ||
595 | return -1; | ||
596 | |||
597 | m->avatar_format = format; | ||
598 | m->avatar_data = tmp; | ||
599 | m->avatar_data_length = length; | ||
600 | memcpy(m->avatar_data, data, length); | ||
601 | |||
602 | m_avatar_hash(m->avatar_hash, m->avatar_data, m->avatar_data_length); | ||
603 | } | ||
604 | |||
605 | uint32_t i; | ||
606 | |||
607 | for (i = 0; i < m->numfriends; ++i) | ||
608 | m->friendlist[i].avatar_info_sent = 0; | ||
609 | |||
610 | return 0; | ||
611 | } | ||
612 | |||
613 | int m_get_self_avatar(const Messenger *m, uint8_t *format, uint8_t *buf, uint32_t *length, uint32_t maxlen, | ||
614 | uint8_t *hash) | ||
615 | { | ||
616 | if (format) | ||
617 | *format = m->avatar_format; | ||
618 | |||
619 | if (length) | ||
620 | *length = m->avatar_data_length; | ||
621 | |||
622 | if (hash) | ||
623 | memcpy(hash, m->avatar_hash, AVATAR_HASH_LENGTH); | ||
624 | |||
625 | if (buf != NULL && maxlen > 0) { | ||
626 | if (m->avatar_data_length <= maxlen) | ||
627 | memcpy(buf, m->avatar_data, m->avatar_data_length); | ||
628 | else | ||
629 | return -1; | ||
630 | } | ||
631 | |||
632 | return 0; | ||
633 | } | ||
634 | |||
635 | int m_avatar_hash(uint8_t *hash, const uint8_t *data, const uint32_t datalen) | ||
636 | { | ||
637 | if (hash == NULL) | ||
638 | return -1; | ||
639 | |||
640 | crypto_hash_sha256(hash, data, datalen); | ||
641 | return 0; | ||
642 | } | ||
643 | |||
644 | |||
645 | int m_request_avatar_info(const Messenger *m, const int32_t friendnumber) | ||
646 | { | ||
647 | if (friend_not_valid(m, friendnumber)) | ||
648 | return -1; | ||
649 | |||
650 | if (write_cryptpacket_id(m, friendnumber, PACKET_ID_AVATAR_INFO_REQ, 0, 0, 0)) | ||
651 | return 0; | ||
652 | else | ||
653 | return -1; | ||
654 | } | ||
655 | |||
656 | int m_send_avatar_info(const Messenger *m, const int32_t friendnumber) | ||
657 | { | ||
658 | if (friend_not_valid(m, friendnumber)) | ||
659 | return -1; | ||
660 | |||
661 | uint8_t data[sizeof(uint8_t) + AVATAR_HASH_LENGTH]; | ||
662 | data[0] = m->avatar_format; | ||
663 | memcpy(data + 1, m->avatar_hash, AVATAR_HASH_LENGTH); | ||
664 | |||
665 | if (write_cryptpacket_id(m, friendnumber, PACKET_ID_AVATAR_INFO, data, sizeof(data), 0)) | ||
666 | return 0; | ||
667 | else | ||
668 | return -1; | ||
669 | } | ||
670 | |||
671 | int m_request_avatar_data(const Messenger *m, const int32_t friendnumber) | ||
672 | { | ||
673 | if (friend_not_valid(m, friendnumber)) | ||
674 | return -1; | ||
675 | |||
676 | AVATARRECEIVEDATA *avrd = m->friendlist[friendnumber].avatar_recv_data; | ||
677 | |||
678 | if (avrd == NULL) { | ||
679 | avrd = calloc(sizeof(AVATARRECEIVEDATA), 1); | ||
680 | |||
681 | if (avrd == NULL) | ||
682 | return -1; | ||
683 | |||
684 | avrd->started = 0; | ||
685 | m->friendlist[friendnumber].avatar_recv_data = avrd; | ||
686 | } | ||
687 | |||
688 | if (avrd->started) { | ||
689 | LOGGER_DEBUG("Resetting already started data request. " | ||
690 | "friendnumber == %u", friendnumber); | ||
691 | } | ||
692 | |||
693 | avrd->started = 0; | ||
694 | avrd->bytes_received = 0; | ||
695 | avrd->total_length = 0; | ||
696 | avrd->format = AVATARFORMAT_NONE; | ||
697 | |||
698 | return send_avatar_data_control(m, friendnumber, AVATARDATACONTROL_REQ); | ||
699 | } | ||
700 | |||
701 | |||
567 | /* return the size of friendnumber's user status. | 702 | /* return the size of friendnumber's user status. |
568 | * Guaranteed to be at most MAX_STATUSMESSAGE_LENGTH. | 703 | * Guaranteed to be at most MAX_STATUSMESSAGE_LENGTH. |
569 | */ | 704 | */ |
@@ -806,6 +941,20 @@ void m_callback_connectionstatus_internal_av(Messenger *m, void (*function)(Mess | |||
806 | m->friend_connectionstatuschange_internal_userdata = userdata; | 941 | m->friend_connectionstatuschange_internal_userdata = userdata; |
807 | } | 942 | } |
808 | 943 | ||
944 | void m_callback_avatar_info(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, uint8_t *, void *), | ||
945 | void *userdata) | ||
946 | { | ||
947 | m->avatar_info_recv = function; | ||
948 | m->avatar_info_recv_userdata = userdata; | ||
949 | } | ||
950 | |||
951 | void m_callback_avatar_data(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, uint8_t *, uint8_t *, | ||
952 | uint32_t, void *), void *userdata) | ||
953 | { | ||
954 | m->avatar_data_recv = function; | ||
955 | m->avatar_data_recv_userdata = userdata; | ||
956 | } | ||
957 | |||
809 | static void break_files(const Messenger *m, int32_t friendnumber); | 958 | static void break_files(const Messenger *m, int32_t friendnumber); |
810 | static void check_friend_connectionstatus(Messenger *m, int32_t friendnumber, uint8_t status) | 959 | static void check_friend_connectionstatus(Messenger *m, int32_t friendnumber, uint8_t status) |
811 | { | 960 | { |
@@ -1857,6 +2006,9 @@ Messenger *new_messenger(Messenger_Options *options) | |||
1857 | m->net = new_networking(ip, TOX_PORT_DEFAULT); | 2006 | m->net = new_networking(ip, TOX_PORT_DEFAULT); |
1858 | } | 2007 | } |
1859 | 2008 | ||
2009 | m->avatar_format = AVATARFORMAT_NONE; | ||
2010 | m->avatar_data = NULL; | ||
2011 | |||
1860 | if (m->net == NULL) { | 2012 | if (m->net == NULL) { |
1861 | free(m); | 2013 | free(m); |
1862 | return NULL; | 2014 | return NULL; |
@@ -1930,10 +2082,11 @@ void kill_messenger(Messenger *m) | |||
1930 | kill_networking(m->net); | 2082 | kill_networking(m->net); |
1931 | 2083 | ||
1932 | for (i = 0; i < m->numfriends; ++i) { | 2084 | for (i = 0; i < m->numfriends; ++i) { |
1933 | if (m->friendlist[i].statusmessage) | 2085 | free(m->friendlist[i].statusmessage); |
1934 | free(m->friendlist[i].statusmessage); | 2086 | free(m->friendlist[i].avatar_recv_data); |
1935 | } | 2087 | } |
1936 | 2088 | ||
2089 | free(m->avatar_data); | ||
1937 | free(m->friendlist); | 2090 | free(m->friendlist); |
1938 | free(m); | 2091 | free(m); |
1939 | } | 2092 | } |
@@ -1973,11 +2126,287 @@ static int handle_status(void *object, int i, uint8_t status) | |||
1973 | if (m->friendlist[i].status == FRIEND_ONLINE) { | 2126 | if (m->friendlist[i].status == FRIEND_ONLINE) { |
1974 | set_friend_status(m, i, FRIEND_CONFIRMED); | 2127 | set_friend_status(m, i, FRIEND_CONFIRMED); |
1975 | } | 2128 | } |
2129 | |||
2130 | /* Clear avatar transfer state */ | ||
2131 | if (m->friendlist[i].avatar_recv_data) { | ||
2132 | free(m->friendlist[i].avatar_recv_data); | ||
2133 | m->friendlist[i].avatar_recv_data = NULL; | ||
2134 | } | ||
1976 | } | 2135 | } |
1977 | 2136 | ||
1978 | return 0; | 2137 | return 0; |
1979 | } | 2138 | } |
1980 | 2139 | ||
2140 | |||
2141 | /* Sends an avatar data control packet to the peer. Usually to return status | ||
2142 | * values or request data. | ||
2143 | */ | ||
2144 | static int send_avatar_data_control(const Messenger *m, const uint32_t friendnumber, | ||
2145 | uint8_t op) | ||
2146 | { | ||
2147 | int ret = write_cryptpacket_id(m, friendnumber, PACKET_ID_AVATAR_DATA_CONTROL, | ||
2148 | &op, sizeof(op), 0); | ||
2149 | LOGGER_DEBUG("friendnumber = %u, op = %u, ret = %d", | ||
2150 | friendnumber, op, ret); | ||
2151 | return ret ? 0 : -1; | ||
2152 | } | ||
2153 | |||
2154 | |||
2155 | static int handle_avatar_data_control(Messenger *m, uint32_t friendnumber, | ||
2156 | uint8_t *data, uint32_t data_length) | ||
2157 | { | ||
2158 | if (data_length != 1) { | ||
2159 | LOGGER_DEBUG("Error: PACKET_ID_AVATAR_DATA_CONTROL with bad " | ||
2160 | "data_length = %u, friendnumber = %u", | ||
2161 | data_length, friendnumber); | ||
2162 | send_avatar_data_control(m, friendnumber, AVATARDATACONTROL_ERROR); | ||
2163 | return -1; /* Error */ | ||
2164 | } | ||
2165 | |||
2166 | LOGGER_DEBUG("friendnumber = %u, op = %u", friendnumber, data[0]); | ||
2167 | |||
2168 | switch (data[0]) { | ||
2169 | case AVATARDATACONTROL_REQ: { | ||
2170 | |||
2171 | /* Check data transfer limits for this friend */ | ||
2172 | AVATARSENDDATA *const avsd = &(m->friendlist[friendnumber].avatar_send_data); | ||
2173 | |||
2174 | if (avsd->bytes_sent >= AVATAR_DATA_TRANSFER_LIMIT) { | ||
2175 | /* User reached data limit. Check timeout */ | ||
2176 | uint64_t now = unix_time(); | ||
2177 | |||
2178 | if (avsd->last_reset > 0 | ||
2179 | && (avsd->last_reset + AVATAR_DATA_TRANSFER_TIMEOUT < now)) { | ||
2180 | avsd->bytes_sent = 0; | ||
2181 | avsd->last_reset = now; | ||
2182 | } else { | ||
2183 | /* Friend still rate-limitted. Send an error and stops. */ | ||
2184 | LOGGER_DEBUG("Avatar data transfer limit reached. " | ||
2185 | "friendnumber = %u", friendnumber); | ||
2186 | send_avatar_data_control(m, friendnumber, AVATARDATACONTROL_ERROR); | ||
2187 | return 0; | ||
2188 | } | ||
2189 | } | ||
2190 | |||
2191 | /* Start the transmission with a DATA_START message. Format: | ||
2192 | * uint8_t format | ||
2193 | * uint8_t hash[AVATAR_HASH_LENGTH] | ||
2194 | * uint32_t total_length | ||
2195 | */ | ||
2196 | LOGGER_DEBUG("Sending start msg to friend number %u. " | ||
2197 | "m->avatar_format = %u, m->avatar_data_length = %u", | ||
2198 | friendnumber, m->avatar_format, m->avatar_data_length); | ||
2199 | uint8_t start_data[1 + AVATAR_HASH_LENGTH + sizeof(uint32_t)]; | ||
2200 | uint32_t avatar_len = htonl(m->avatar_data_length); | ||
2201 | |||
2202 | start_data[0] = m->avatar_format; | ||
2203 | memcpy(start_data + 1, m->avatar_hash, AVATAR_HASH_LENGTH); | ||
2204 | memcpy(start_data + 1 + AVATAR_HASH_LENGTH, &avatar_len, sizeof(uint32_t)); | ||
2205 | |||
2206 | avsd->bytes_sent += sizeof(start_data); /* For rate limit */ | ||
2207 | |||
2208 | int ret = write_cryptpacket_id(m, friendnumber, PACKET_ID_AVATAR_DATA_START, | ||
2209 | start_data, sizeof(start_data), 0); | ||
2210 | |||
2211 | if (!ret) { | ||
2212 | /* Something went wrong, try to signal the error so the friend | ||
2213 | * can clear up the state. */ | ||
2214 | send_avatar_data_control(m, friendnumber, AVATARDATACONTROL_ERROR); | ||
2215 | return 0; | ||
2216 | } | ||
2217 | |||
2218 | /* User have no avatar data, nothing more to do. */ | ||
2219 | if (m->avatar_format == AVATARFORMAT_NONE) | ||
2220 | return 0; | ||
2221 | |||
2222 | /* Send the actual avatar data. */ | ||
2223 | uint32_t offset = 0; | ||
2224 | |||
2225 | while (offset < m->avatar_data_length) { | ||
2226 | uint32_t chunk_len = m->avatar_data_length - offset; | ||
2227 | |||
2228 | if (chunk_len > AVATAR_DATA_MAX_CHUNK_SIZE) | ||
2229 | chunk_len = AVATAR_DATA_MAX_CHUNK_SIZE; | ||
2230 | |||
2231 | uint8_t chunk[AVATAR_DATA_MAX_CHUNK_SIZE]; | ||
2232 | memcpy(chunk, m->avatar_data + offset, chunk_len); | ||
2233 | offset += chunk_len; | ||
2234 | avsd->bytes_sent += chunk_len; /* For rate limit */ | ||
2235 | |||
2236 | int ret = write_cryptpacket_id(m, friendnumber, | ||
2237 | PACKET_ID_AVATAR_DATA_PUSH, | ||
2238 | chunk, chunk_len, 0); | ||
2239 | |||
2240 | if (!ret) { | ||
2241 | LOGGER_DEBUG("write_cryptpacket_id failed. ret = %d, " | ||
2242 | "friendnumber = %u, offset = %u", | ||
2243 | ret, friendnumber, offset); | ||
2244 | send_avatar_data_control(m, friendnumber, AVATARDATACONTROL_ERROR); | ||
2245 | return -1; | ||
2246 | } | ||
2247 | } | ||
2248 | |||
2249 | return 0; | ||
2250 | } | ||
2251 | |||
2252 | case AVATARDATACONTROL_ERROR: { | ||
2253 | if (m->friendlist[friendnumber].avatar_recv_data) { | ||
2254 | /* We were receiving the data, sender detected an error | ||
2255 | (eg. changing avatar) and asked us to stop. */ | ||
2256 | free(m->friendlist[friendnumber].avatar_recv_data); | ||
2257 | m->friendlist[friendnumber].avatar_recv_data = NULL; | ||
2258 | } | ||
2259 | |||
2260 | return 0; | ||
2261 | } | ||
2262 | } | ||
2263 | |||
2264 | return -1; | ||
2265 | } | ||
2266 | |||
2267 | |||
2268 | static int handle_avatar_data_start(Messenger *m, uint32_t friendnumber, | ||
2269 | uint8_t *data, uint32_t data_length) | ||
2270 | { | ||
2271 | LOGGER_DEBUG("data_length = %u, friendnumber = %u", data_length, friendnumber); | ||
2272 | |||
2273 | if (data_length != 1 + AVATAR_HASH_LENGTH + sizeof(uint32_t)) { | ||
2274 | LOGGER_DEBUG("Invalid msg length = %u, friendnumber = %u", | ||
2275 | data_length, friendnumber); | ||
2276 | return -1; | ||
2277 | } | ||
2278 | |||
2279 | AVATARRECEIVEDATA *avrd = m->friendlist[friendnumber].avatar_recv_data; | ||
2280 | |||
2281 | if (avrd == NULL) { | ||
2282 | LOGGER_DEBUG("Received an unrequested DATA_START, friendnumber = %u", | ||
2283 | friendnumber); | ||
2284 | return -1; | ||
2285 | } | ||
2286 | |||
2287 | if (avrd->started) { | ||
2288 | /* Already receiving data from this friend. Must be an error | ||
2289 | * or an malicious request, because we zeroed the started bit | ||
2290 | * when we requested the data. */ | ||
2291 | LOGGER_DEBUG("Received an unrequested duplicated DATA_START, " | ||
2292 | "friendnumber = %u", friendnumber); | ||
2293 | return -1; | ||
2294 | } | ||
2295 | |||
2296 | /* Copy data from message to our control structure */ | ||
2297 | avrd->started = 1; | ||
2298 | avrd->format = data[0]; | ||
2299 | memcpy(avrd->hash, data + 1, AVATAR_HASH_LENGTH); | ||
2300 | uint32_t tmp_len; | ||
2301 | memcpy(&tmp_len, data + 1 + AVATAR_HASH_LENGTH, sizeof(uint32_t)); | ||
2302 | avrd->total_length = ntohl(tmp_len); | ||
2303 | avrd->bytes_received = 0; | ||
2304 | |||
2305 | LOGGER_DEBUG("friendnumber = %u, avrd->format = %u, " | ||
2306 | "avrd->total_length = %u, avrd->bytes_received = %u", | ||
2307 | friendnumber, avrd->format, avrd->total_length, | ||
2308 | avrd->bytes_received); | ||
2309 | |||
2310 | if (avrd->total_length > MAX_AVATAR_DATA_LENGTH) { | ||
2311 | /* Invalid data length. Stops. */ | ||
2312 | LOGGER_DEBUG("Error: total_length > MAX_AVATAR_DATA_LENGTH, " | ||
2313 | "friendnumber = %u", friendnumber); | ||
2314 | free(avrd); | ||
2315 | avrd = NULL; | ||
2316 | m->friendlist[friendnumber].avatar_recv_data = NULL; | ||
2317 | return 0; | ||
2318 | } | ||
2319 | |||
2320 | if (avrd->format == AVATARFORMAT_NONE || avrd->total_length == 0) { | ||
2321 | /* No real data to receive. Run callback function and finish. */ | ||
2322 | LOGGER_DEBUG("format == NONE, friendnumber = %u", friendnumber); | ||
2323 | |||
2324 | if (m->avatar_data_recv) { | ||
2325 | memset(avrd->hash, 0, AVATAR_HASH_LENGTH); | ||
2326 | (m->avatar_data_recv)(m, friendnumber, avrd->format, avrd->hash, | ||
2327 | NULL, 0, m->avatar_data_recv_userdata); | ||
2328 | } | ||
2329 | |||
2330 | free(avrd); | ||
2331 | avrd = NULL; | ||
2332 | m->friendlist[friendnumber].avatar_recv_data = NULL; | ||
2333 | return 0; | ||
2334 | } | ||
2335 | |||
2336 | /* Waits for more data to be received */ | ||
2337 | return 0; | ||
2338 | } | ||
2339 | |||
2340 | |||
2341 | static int handle_avatar_data_push(Messenger *m, uint32_t friendnumber, | ||
2342 | uint8_t *data, uint32_t data_length) | ||
2343 | { | ||
2344 | LOGGER_DEBUG("friendnumber = %u, data_length = %u", friendnumber, data_length); | ||
2345 | |||
2346 | AVATARRECEIVEDATA *avrd = m->friendlist[friendnumber].avatar_recv_data; | ||
2347 | |||
2348 | if (avrd == NULL) { | ||
2349 | /* No active transfer. It must be an error or a malicious request, | ||
2350 | * because we set the avatar_recv_data on the first DATA_START. */ | ||
2351 | LOGGER_DEBUG("Error: avrd == NULL, friendnumber = %u", friendnumber); | ||
2352 | return -1; /* Error */ | ||
2353 | } | ||
2354 | |||
2355 | if (avrd->started == 0) { | ||
2356 | /* Receiving data for a non-started request. Must be an error | ||
2357 | * or an malicious request. */ | ||
2358 | LOGGER_DEBUG("Received an data push for a yet non started data " | ||
2359 | "request. friendnumber = %u", friendnumber); | ||
2360 | return -1; /* Error */ | ||
2361 | } | ||
2362 | |||
2363 | uint32_t new_length = avrd->bytes_received + data_length; | ||
2364 | |||
2365 | if (new_length > avrd->total_length | ||
2366 | || new_length >= MAX_AVATAR_DATA_LENGTH) { | ||
2367 | /* Invalid data length due to error or malice. Stops. */ | ||
2368 | LOGGER_DEBUG("Invalid data length. friendnumber = %u, " | ||
2369 | "new_length = %u, avrd->total_length = %u", | ||
2370 | friendnumber, new_length, avrd->total_length); | ||
2371 | free(avrd); | ||
2372 | m->friendlist[friendnumber].avatar_recv_data = NULL; | ||
2373 | return 0; | ||
2374 | } | ||
2375 | |||
2376 | memcpy(avrd->data + avrd->bytes_received, data, data_length); | ||
2377 | avrd->bytes_received += data_length; | ||
2378 | |||
2379 | if (avrd->bytes_received == avrd->total_length) { | ||
2380 | LOGGER_DEBUG("All data received. friendnumber = %u", friendnumber); | ||
2381 | |||
2382 | /* All data was received. Check if the hashes match. It the | ||
2383 | * requester's responsability to do this. The sender may have done | ||
2384 | * anything with its avatar data between the DATA_START and now. | ||
2385 | */ | ||
2386 | uint8_t cur_hash[AVATAR_HASH_LENGTH]; | ||
2387 | m_avatar_hash(cur_hash, avrd->data, avrd->bytes_received); | ||
2388 | |||
2389 | if (memcmp(cur_hash, avrd->hash, AVATAR_HASH_LENGTH) == 0) { | ||
2390 | /* Avatar successfuly received! */ | ||
2391 | if (m->avatar_data_recv) { | ||
2392 | (m->avatar_data_recv)(m, friendnumber, avrd->format, cur_hash, | ||
2393 | avrd->data, avrd->bytes_received, m->avatar_data_recv_userdata); | ||
2394 | } | ||
2395 | } else { | ||
2396 | LOGGER_DEBUG("Avatar hash error. friendnumber = %u", friendnumber); | ||
2397 | } | ||
2398 | |||
2399 | free(avrd); | ||
2400 | m->friendlist[friendnumber].avatar_recv_data = NULL; | ||
2401 | return 0; | ||
2402 | } | ||
2403 | |||
2404 | /* Waits for more data to be received */ | ||
2405 | return 0; | ||
2406 | } | ||
2407 | |||
2408 | |||
2409 | |||
1981 | static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len) | 2410 | static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len) |
1982 | { | 2411 | { |
1983 | if (len == 0) | 2412 | if (len == 0) |
@@ -2115,6 +2544,42 @@ static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len) | |||
2115 | break; | 2544 | break; |
2116 | } | 2545 | } |
2117 | 2546 | ||
2547 | case PACKET_ID_AVATAR_INFO_REQ: { | ||
2548 | /* Send our avatar information */ | ||
2549 | m_send_avatar_info(m, i); | ||
2550 | break; | ||
2551 | } | ||
2552 | |||
2553 | case PACKET_ID_AVATAR_INFO: { | ||
2554 | if (m->avatar_info_recv) { | ||
2555 | /* | ||
2556 | * A malicious user may send an incomplete avatar info message. | ||
2557 | * Check if it have the correct size for the format: | ||
2558 | * [1 uint8_t: avatar format] [32 uint8_t: hash] | ||
2559 | */ | ||
2560 | if (data_length == AVATAR_HASH_LENGTH + 1) { | ||
2561 | (m->avatar_info_recv)(m, i, data[0], data + 1, m->avatar_info_recv_userdata); | ||
2562 | } | ||
2563 | } | ||
2564 | |||
2565 | break; | ||
2566 | } | ||
2567 | |||
2568 | case PACKET_ID_AVATAR_DATA_CONTROL: { | ||
2569 | handle_avatar_data_control(m, i, data, data_length); | ||
2570 | break; | ||
2571 | } | ||
2572 | |||
2573 | case PACKET_ID_AVATAR_DATA_START: { | ||
2574 | handle_avatar_data_start(m, i, data, data_length); | ||
2575 | break; | ||
2576 | } | ||
2577 | |||
2578 | case PACKET_ID_AVATAR_DATA_PUSH: { | ||
2579 | handle_avatar_data_push(m, i, data, data_length); | ||
2580 | break; | ||
2581 | } | ||
2582 | |||
2118 | case PACKET_ID_RECEIPT: { | 2583 | case PACKET_ID_RECEIPT: { |
2119 | uint32_t msgid; | 2584 | uint32_t msgid; |
2120 | 2585 | ||
@@ -2343,6 +2808,11 @@ void do_friends(Messenger *m) | |||
2343 | m->friendlist[i].userstatus_sent = 1; | 2808 | m->friendlist[i].userstatus_sent = 1; |
2344 | } | 2809 | } |
2345 | 2810 | ||
2811 | if (m->friendlist[i].avatar_info_sent == 0) { | ||
2812 | if (m_send_avatar_info(m, i) == 0) | ||
2813 | m->friendlist[i].avatar_info_sent = 1; | ||
2814 | } | ||
2815 | |||
2346 | if (m->friendlist[i].user_istyping_sent == 0) { | 2816 | if (m->friendlist[i].user_istyping_sent == 0) { |
2347 | if (send_user_istyping(m, i, m->friendlist[i].user_istyping)) | 2817 | if (send_user_istyping(m, i, m->friendlist[i].user_istyping)) |
2348 | m->friendlist[i].user_istyping_sent = 1; | 2818 | m->friendlist[i].user_istyping_sent = 1; |
diff --git a/toxcore/Messenger.h b/toxcore/Messenger.h index 6c641a9a..a107d80f 100644 --- a/toxcore/Messenger.h +++ b/toxcore/Messenger.h | |||
@@ -36,6 +36,9 @@ | |||
36 | #define MAX_NAME_LENGTH 128 | 36 | #define MAX_NAME_LENGTH 128 |
37 | /* TODO: this must depend on other variable. */ | 37 | /* TODO: this must depend on other variable. */ |
38 | #define MAX_STATUSMESSAGE_LENGTH 1007 | 38 | #define MAX_STATUSMESSAGE_LENGTH 1007 |
39 | #define MAX_AVATAR_DATA_LENGTH 16384 | ||
40 | #define AVATAR_HASH_LENGTH 32 | ||
41 | |||
39 | 42 | ||
40 | #define FRIEND_ADDRESS_SIZE (crypto_box_PUBLICKEYBYTES + sizeof(uint32_t) + sizeof(uint16_t)) | 43 | #define FRIEND_ADDRESS_SIZE (crypto_box_PUBLICKEYBYTES + sizeof(uint32_t) + sizeof(uint16_t)) |
41 | 44 | ||
@@ -46,6 +49,11 @@ | |||
46 | #define PACKET_ID_STATUSMESSAGE 49 | 49 | #define PACKET_ID_STATUSMESSAGE 49 |
47 | #define PACKET_ID_USERSTATUS 50 | 50 | #define PACKET_ID_USERSTATUS 50 |
48 | #define PACKET_ID_TYPING 51 | 51 | #define PACKET_ID_TYPING 51 |
52 | #define PACKET_ID_AVATAR_INFO_REQ 52 | ||
53 | #define PACKET_ID_AVATAR_INFO 53 | ||
54 | #define PACKET_ID_AVATAR_DATA_CONTROL 54 | ||
55 | #define PACKET_ID_AVATAR_DATA_START 55 | ||
56 | #define PACKET_ID_AVATAR_DATA_PUSH 56 | ||
49 | #define PACKET_ID_RECEIPT 63 | 57 | #define PACKET_ID_RECEIPT 63 |
50 | #define PACKET_ID_MESSAGE 64 | 58 | #define PACKET_ID_MESSAGE 64 |
51 | #define PACKET_ID_ACTION 65 | 59 | #define PACKET_ID_ACTION 65 |
@@ -109,6 +117,13 @@ enum { | |||
109 | /* If no packets are received from friend in this time interval, kill the connection. */ | 117 | /* If no packets are received from friend in this time interval, kill the connection. */ |
110 | #define FRIEND_CONNECTION_TIMEOUT (FRIEND_PING_INTERVAL * 3) | 118 | #define FRIEND_CONNECTION_TIMEOUT (FRIEND_PING_INTERVAL * 3) |
111 | 119 | ||
120 | /* Must be < MAX_CRYPTO_DATA_SIZE */ | ||
121 | #define AVATAR_DATA_MAX_CHUNK_SIZE (MAX_CRYPTO_DATA_SIZE-1) | ||
122 | |||
123 | /* Per-friend data limit for avatar data requests */ | ||
124 | #define AVATAR_DATA_TRANSFER_LIMIT (10*MAX_AVATAR_DATA_LENGTH) | ||
125 | #define AVATAR_DATA_TRANSFER_TIMEOUT (60) /* 164kB every 60 seconds is not a lot */ | ||
126 | |||
112 | 127 | ||
113 | /* USERSTATUS - | 128 | /* USERSTATUS - |
114 | * Represents userstatuses someone can have. | 129 | * Represents userstatuses someone can have. |
@@ -122,6 +137,42 @@ typedef enum { | |||
122 | } | 137 | } |
123 | USERSTATUS; | 138 | USERSTATUS; |
124 | 139 | ||
140 | /* AVATARFORMAT - | ||
141 | * Data formats for user avatar images | ||
142 | */ | ||
143 | typedef enum { | ||
144 | AVATARFORMAT_NONE, | ||
145 | AVATARFORMAT_PNG | ||
146 | } | ||
147 | AVATARFORMAT; | ||
148 | |||
149 | /* AVATARDATACONTROL | ||
150 | * To control avatar data requests (PACKET_ID_AVATAR_DATA_CONTROL) | ||
151 | */ | ||
152 | typedef enum { | ||
153 | AVATARDATACONTROL_REQ, | ||
154 | AVATARDATACONTROL_ERROR | ||
155 | } | ||
156 | AVATARDATACONTROL; | ||
157 | |||
158 | typedef struct { | ||
159 | uint8_t started; | ||
160 | AVATARFORMAT format; | ||
161 | uint8_t hash[AVATAR_HASH_LENGTH]; | ||
162 | uint32_t total_length; | ||
163 | uint32_t bytes_received; | ||
164 | uint8_t data[MAX_AVATAR_DATA_LENGTH]; | ||
165 | } | ||
166 | AVATARRECEIVEDATA; | ||
167 | |||
168 | typedef struct { | ||
169 | /* Fields only used to limit the network usage from a given friend */ | ||
170 | uint32_t bytes_sent; /* Total bytes send to this user */ | ||
171 | uint64_t last_reset; /* Time the data counter was last reset */ | ||
172 | } | ||
173 | AVATARSENDDATA; | ||
174 | |||
175 | |||
125 | struct File_Transfers { | 176 | struct File_Transfers { |
126 | uint64_t size; | 177 | uint64_t size; |
127 | uint64_t transferred; | 178 | uint64_t transferred; |
@@ -163,6 +214,7 @@ typedef struct { | |||
163 | uint8_t statusmessage_sent; | 214 | uint8_t statusmessage_sent; |
164 | USERSTATUS userstatus; | 215 | USERSTATUS userstatus; |
165 | uint8_t userstatus_sent; | 216 | uint8_t userstatus_sent; |
217 | uint8_t avatar_info_sent; | ||
166 | uint8_t user_istyping; | 218 | uint8_t user_istyping; |
167 | uint8_t user_istyping_sent; | 219 | uint8_t user_istyping_sent; |
168 | uint8_t is_typing; | 220 | uint8_t is_typing; |
@@ -178,6 +230,9 @@ typedef struct { | |||
178 | int invited_groups[MAX_INVITED_GROUPS]; | 230 | int invited_groups[MAX_INVITED_GROUPS]; |
179 | uint16_t invited_groups_num; | 231 | uint16_t invited_groups_num; |
180 | 232 | ||
233 | AVATARSENDDATA avatar_send_data; | ||
234 | AVATARRECEIVEDATA *avatar_recv_data; // We are receiving avatar data from this friend. | ||
235 | |||
181 | struct { | 236 | struct { |
182 | int (*function)(void *object, const uint8_t *data, uint32_t len); | 237 | int (*function)(void *object, const uint8_t *data, uint32_t len); |
183 | void *object; | 238 | void *object; |
@@ -209,6 +264,11 @@ typedef struct Messenger { | |||
209 | 264 | ||
210 | USERSTATUS userstatus; | 265 | USERSTATUS userstatus; |
211 | 266 | ||
267 | AVATARFORMAT avatar_format; | ||
268 | uint8_t *avatar_data; | ||
269 | uint32_t avatar_data_length; | ||
270 | uint8_t avatar_hash[AVATAR_HASH_LENGTH]; | ||
271 | |||
212 | Friend *friendlist; | 272 | Friend *friendlist; |
213 | uint32_t numfriends; | 273 | uint32_t numfriends; |
214 | 274 | ||
@@ -243,6 +303,10 @@ typedef struct Messenger { | |||
243 | void *friend_connectionstatuschange_userdata; | 303 | void *friend_connectionstatuschange_userdata; |
244 | void (*friend_connectionstatuschange_internal)(struct Messenger *m, int32_t, uint8_t, void *); | 304 | void (*friend_connectionstatuschange_internal)(struct Messenger *m, int32_t, uint8_t, void *); |
245 | void *friend_connectionstatuschange_internal_userdata; | 305 | void *friend_connectionstatuschange_internal_userdata; |
306 | void *avatar_info_recv_userdata; | ||
307 | void (*avatar_info_recv)(struct Messenger *m, int32_t, uint8_t, uint8_t *, void *); | ||
308 | void *avatar_data_recv_userdata; | ||
309 | void (*avatar_data_recv)(struct Messenger *m, int32_t, uint8_t, uint8_t *, uint8_t *, uint32_t, void *); | ||
246 | 310 | ||
247 | void (*group_invite)(struct Messenger *m, int32_t, const uint8_t *, void *); | 311 | void (*group_invite)(struct Messenger *m, int32_t, const uint8_t *, void *); |
248 | void *group_invite_userdata; | 312 | void *group_invite_userdata; |
@@ -437,6 +501,94 @@ int m_copy_self_statusmessage(const Messenger *m, uint8_t *buf, uint32_t maxlen) | |||
437 | uint8_t m_get_userstatus(const Messenger *m, int32_t friendnumber); | 501 | uint8_t m_get_userstatus(const Messenger *m, int32_t friendnumber); |
438 | uint8_t m_get_self_userstatus(const Messenger *m); | 502 | uint8_t m_get_self_userstatus(const Messenger *m); |
439 | 503 | ||
504 | |||
505 | /* Set the user avatar image data. | ||
506 | * This should be made before connecting, so we will not announce that the user have no avatar | ||
507 | * before setting and announcing a new one, forcing the peers to re-download it. | ||
508 | * | ||
509 | * Notice that the library treats the image as raw data and does not interpret it by any way. | ||
510 | * | ||
511 | * Arguments: | ||
512 | * format - Avatar image format or NONE for user with no avatar (see AVATARFORMAT); | ||
513 | * data - pointer to the avatar data (may be NULL it the format is NONE); | ||
514 | * length - length of image data. Must be <= MAX_AVATAR_DATA_LENGTH. | ||
515 | * | ||
516 | * returns 0 on success | ||
517 | * returns -1 on failure. | ||
518 | */ | ||
519 | int m_set_avatar(Messenger *m, uint8_t format, const uint8_t *data, uint32_t length); | ||
520 | |||
521 | /* Get avatar data from the current user. | ||
522 | * Copies the current user avatar data to the destination buffer and sets the image format | ||
523 | * accordingly. | ||
524 | * | ||
525 | * If the avatar format is NONE, the buffer 'buf' isleft uninitialized, 'hash' is zeroed, and | ||
526 | * 'length' is set to zero. | ||
527 | * | ||
528 | * If any of the pointers format, buf, length, and hash are NULL, that particular field will be ignored. | ||
529 | * | ||
530 | * Arguments: | ||
531 | * format - destination pointer to the avatar image format (see AVATARFORMAT); | ||
532 | * buf - destination buffer to the image data. Must have at least 'maxlen' bytes; | ||
533 | * length - destination pointer to the image data length; | ||
534 | * maxlen - length of the destination buffer 'buf'; | ||
535 | * hash - destination pointer to the avatar hash (it must be exactly AVATAR_HASH_LENGTH bytes long). | ||
536 | * | ||
537 | * returns 0 on success; | ||
538 | * returns -1 on failure. | ||
539 | * | ||
540 | */ | ||
541 | int m_get_self_avatar(const Messenger *m, uint8_t *format, uint8_t *buf, uint32_t *length, uint32_t maxlen, | ||
542 | uint8_t *hash); | ||
543 | |||
544 | /* Generates a cryptographic hash of the given avatar data. | ||
545 | * This function is a wrapper to internal message-digest functions and specifically provided | ||
546 | * to generate hashes from user avatars that may be memcmp()ed with the values returned by the | ||
547 | * other avatar functions. It is specially important to validate cached avatars. | ||
548 | * | ||
549 | * Arguments: | ||
550 | * hash - destination buffer for the hash data, it must be exactly AVATAR_HASH_LENGTH bytes long. | ||
551 | * data - avatar image data; | ||
552 | * datalen - length of the avatar image data; it must be <= MAX_AVATAR_DATA_LENGTH. | ||
553 | * | ||
554 | * returns 0 on success | ||
555 | * returns -1 on failure. | ||
556 | */ | ||
557 | int m_avatar_hash(uint8_t *hash, const uint8_t *data, const uint32_t datalen); | ||
558 | |||
559 | /* Request avatar information from a friend. | ||
560 | * Asks a friend to provide their avatar information (image format and hash). The friend may | ||
561 | * or may not answer this request and, if answered, the information will be provided through | ||
562 | * the callback 'avatar_info'. | ||
563 | * | ||
564 | * returns 0 on success | ||
565 | * returns -1 on failure. | ||
566 | */ | ||
567 | int m_request_avatar_info(const Messenger *m, const int32_t friendnumber); | ||
568 | |||
569 | /* Send an unrequested avatar information to a friend. | ||
570 | * Sends our avatar format and hash to a friend; he/she can use this information to validate | ||
571 | * an avatar from the cache and may (or not) reply with an avatar data request. | ||
572 | * | ||
573 | * Notice: it is NOT necessary to send these notification after changing the avatar or | ||
574 | * connecting. The library already does this. | ||
575 | * | ||
576 | * returns 0 on success | ||
577 | * returns -1 on failure. | ||
578 | */ | ||
579 | int m_send_avatar_info(const Messenger *m, const int32_t friendnumber); | ||
580 | |||
581 | |||
582 | /* Request the avatar data from a friend. | ||
583 | * Ask a friend to send their avatar data. The friend may or may not answer this request and, | ||
584 | * if answered, the information will be provided in callback 'avatar_data'. | ||
585 | * | ||
586 | * returns 0 on sucess | ||
587 | * returns -1 on failure. | ||
588 | */ | ||
589 | int m_request_avatar_data(const Messenger *m, const int32_t friendnumber); | ||
590 | |||
591 | |||
440 | /* returns timestamp of last time friendnumber was seen online, or 0 if never seen. | 592 | /* returns timestamp of last time friendnumber was seen online, or 0 if never seen. |
441 | * returns -1 on error. | 593 | * returns -1 on error. |
442 | */ | 594 | */ |
@@ -533,6 +685,49 @@ void m_callback_connectionstatus(Messenger *m, void (*function)(Messenger *m, in | |||
533 | void m_callback_connectionstatus_internal_av(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, void *), | 685 | void m_callback_connectionstatus_internal_av(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, void *), |
534 | void *userdata); | 686 | void *userdata); |
535 | 687 | ||
688 | |||
689 | /* Set the callback function for avatar information. | ||
690 | * This callback will be called when avatar information are received from friends. These events | ||
691 | * can arrive at anytime, but are usually received uppon connection and in reply of avatar | ||
692 | * information requests. | ||
693 | * | ||
694 | * Function format is: | ||
695 | * function(Tox *tox, int32_t friendnumber, uint8_t format, uint8_t *hash, void *userdata) | ||
696 | * | ||
697 | * where 'format' is the avatar image format (see AVATARFORMAT) and 'hash' is the hash of | ||
698 | * the avatar data for caching purposes and it is exactly AVATAR_HASH_LENGTH long. If the | ||
699 | * image format is NONE, the hash is zeroed. | ||
700 | * | ||
701 | */ | ||
702 | void m_callback_avatar_info(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, uint8_t *, void *), | ||
703 | void *userdata); | ||
704 | |||
705 | |||
706 | /* Set the callback function for avatar data. | ||
707 | * This callback will be called when the complete avatar data was correctly received from a | ||
708 | * friend. This only happens in reply of a avatar data request (see tox_request_avatar_data); | ||
709 | * | ||
710 | * Function format is: | ||
711 | * function(Tox *tox, int32_t friendnumber, uint8_t format, uint8_t *hash, uint8_t *data, uint32_t datalen, void *userdata) | ||
712 | * | ||
713 | * where 'format' is the avatar image format (see AVATARFORMAT); 'hash' is the | ||
714 | * locally-calculated cryptographic hash of the avatar data and it is exactly | ||
715 | * AVATAR_HASH_LENGTH long; 'data' is the avatar image data and 'datalen' is the length | ||
716 | * of such data. | ||
717 | * | ||
718 | * If format is NONE, 'data' is NULL, 'datalen' is zero, and the hash is zeroed. The hash is | ||
719 | * always validated locally with the function tox_avatar_hash and ensured to match the image | ||
720 | * data, so this value can be safely used to compare with cached avatars. | ||
721 | * | ||
722 | * WARNING: users MUST treat all avatar image data received from another peer as untrusted and | ||
723 | * potentially malicious. The library only ensures that the data which arrived is the same the | ||
724 | * other user sent, and does not interpret or validate any image data. | ||
725 | */ | ||
726 | void m_callback_avatar_data(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, uint8_t *, uint8_t *, | ||
727 | uint32_t, void *), void *userdata); | ||
728 | |||
729 | |||
730 | |||
536 | /**********GROUP CHATS************/ | 731 | /**********GROUP CHATS************/ |
537 | 732 | ||
538 | /* Set the callback for group invites. | 733 | /* Set the callback for group invites. |
diff --git a/toxcore/tox.c b/toxcore/tox.c index a4413c4f..b44f0ee6 100644 --- a/toxcore/tox.c +++ b/toxcore/tox.c | |||
@@ -798,6 +798,57 @@ uint64_t tox_file_data_remaining(const Tox *tox, int32_t friendnumber, uint8_t f | |||
798 | return file_dataremaining(m, friendnumber, filenumber, send_receive); | 798 | return file_dataremaining(m, friendnumber, filenumber, send_receive); |
799 | } | 799 | } |
800 | 800 | ||
801 | |||
802 | /****************AVATAR FUNCTIONS*****************/ | ||
803 | |||
804 | void tox_callback_avatar_info(Tox *tox, void (*function)(Tox *tox, int32_t, uint8_t, uint8_t *, void *), void *userdata) | ||
805 | { | ||
806 | Messenger *m = tox; | ||
807 | m_callback_avatar_info(m, function, userdata); | ||
808 | } | ||
809 | |||
810 | void tox_callback_avatar_data(Tox *tox, void (*function)(Tox *tox, int32_t, uint8_t, uint8_t *, uint8_t *, uint32_t, | ||
811 | void *), void *userdata) | ||
812 | { | ||
813 | Messenger *m = tox; | ||
814 | m_callback_avatar_data(m, function, userdata); | ||
815 | } | ||
816 | |||
817 | int tox_set_avatar(Tox *tox, uint8_t format, const uint8_t *data, uint32_t length) | ||
818 | { | ||
819 | Messenger *m = tox; | ||
820 | return m_set_avatar(m, format, data, length); | ||
821 | } | ||
822 | |||
823 | int tox_get_self_avatar(const Tox *tox, uint8_t *format, uint8_t *buf, uint32_t *length, uint32_t maxlen, uint8_t *hash) | ||
824 | { | ||
825 | const Messenger *m = tox; | ||
826 | return m_get_self_avatar(m, format, buf, length, maxlen, hash); | ||
827 | } | ||
828 | |||
829 | int tox_avatar_hash(const Tox *tox, uint8_t *hash, const uint8_t *data, const uint32_t datalen) | ||
830 | { | ||
831 | return m_avatar_hash(hash, data, datalen); | ||
832 | } | ||
833 | |||
834 | int tox_request_avatar_info(const Tox *tox, const int32_t friendnumber) | ||
835 | { | ||
836 | const Messenger *m = tox; | ||
837 | return m_request_avatar_info(m, friendnumber); | ||
838 | } | ||
839 | |||
840 | int tox_send_avatar_info(Tox *tox, const int32_t friendnumber) | ||
841 | { | ||
842 | const Messenger *m = tox; | ||
843 | return m_send_avatar_info(m, friendnumber); | ||
844 | } | ||
845 | |||
846 | int tox_request_avatar_data(const Tox *tox, const int32_t friendnumber) | ||
847 | { | ||
848 | const Messenger *m = tox; | ||
849 | return m_request_avatar_data(m, friendnumber); | ||
850 | } | ||
851 | |||
801 | /***************END OF FILE SENDING FUNCTIONS******************/ | 852 | /***************END OF FILE SENDING FUNCTIONS******************/ |
802 | 853 | ||
803 | /* Like tox_bootstrap_from_address but for TCP relays only. | 854 | /* Like tox_bootstrap_from_address but for TCP relays only. |
diff --git a/toxcore/tox.h b/toxcore/tox.h index 278a19cd..a5efee34 100644 --- a/toxcore/tox.h +++ b/toxcore/tox.h | |||
@@ -37,6 +37,8 @@ extern "C" { | |||
37 | #define TOX_MAX_MESSAGE_LENGTH 1368 | 37 | #define TOX_MAX_MESSAGE_LENGTH 1368 |
38 | #define TOX_MAX_STATUSMESSAGE_LENGTH 1007 | 38 | #define TOX_MAX_STATUSMESSAGE_LENGTH 1007 |
39 | #define TOX_CLIENT_ID_SIZE 32 | 39 | #define TOX_CLIENT_ID_SIZE 32 |
40 | #define TOX_MAX_AVATAR_DATA_LENGTH 16384 | ||
41 | #define TOX_AVATAR_HASH_LENGTH 32 | ||
40 | 42 | ||
41 | #define TOX_FRIEND_ADDRESS_SIZE (TOX_CLIENT_ID_SIZE + sizeof(uint32_t) + sizeof(uint16_t)) | 43 | #define TOX_FRIEND_ADDRESS_SIZE (TOX_CLIENT_ID_SIZE + sizeof(uint32_t) + sizeof(uint16_t)) |
42 | 44 | ||
@@ -70,6 +72,16 @@ typedef enum { | |||
70 | } | 72 | } |
71 | TOX_USERSTATUS; | 73 | TOX_USERSTATUS; |
72 | 74 | ||
75 | |||
76 | /* AVATARFORMAT - | ||
77 | * Data formats for user avatar images | ||
78 | */ | ||
79 | typedef enum { | ||
80 | TOX_AVATARFORMAT_NONE, | ||
81 | TOX_AVATARFORMAT_PNG | ||
82 | } | ||
83 | TOX_AVATARFORMAT; | ||
84 | |||
73 | #ifndef __TOX_DEFINED__ | 85 | #ifndef __TOX_DEFINED__ |
74 | #define __TOX_DEFINED__ | 86 | #define __TOX_DEFINED__ |
75 | typedef struct Tox Tox; | 87 | typedef struct Tox Tox; |
@@ -242,7 +254,6 @@ int tox_get_self_status_message(const Tox *tox, uint8_t *buf, uint32_t maxlen); | |||
242 | uint8_t tox_get_user_status(const Tox *tox, int32_t friendnumber); | 254 | uint8_t tox_get_user_status(const Tox *tox, int32_t friendnumber); |
243 | uint8_t tox_get_self_user_status(const Tox *tox); | 255 | uint8_t tox_get_self_user_status(const Tox *tox); |
244 | 256 | ||
245 | |||
246 | /* returns timestamp of last time friendnumber was seen online, or 0 if never seen. | 257 | /* returns timestamp of last time friendnumber was seen online, or 0 if never seen. |
247 | * returns -1 on error. | 258 | * returns -1 on error. |
248 | */ | 259 | */ |
@@ -517,6 +528,137 @@ uint32_t tox_count_chatlist(const Tox *tox); | |||
517 | * of out_list will be truncated to list_size. */ | 528 | * of out_list will be truncated to list_size. */ |
518 | uint32_t tox_get_chatlist(const Tox *tox, int *out_list, uint32_t list_size); | 529 | uint32_t tox_get_chatlist(const Tox *tox, int *out_list, uint32_t list_size); |
519 | 530 | ||
531 | /****************AVATAR FUNCTIONS*****************/ | ||
532 | |||
533 | /* Set the callback function for avatar information. | ||
534 | * This callback will be called when avatar information are received from friends. These events | ||
535 | * can arrive at anytime, but are usually received uppon connection and in reply of avatar | ||
536 | * information requests. | ||
537 | * | ||
538 | * Function format is: | ||
539 | * function(Tox *tox, int32_t friendnumber, uint8_t format, uint8_t *hash, void *userdata) | ||
540 | * | ||
541 | * where 'format' is the avatar image format (see TOX_AVATARFORMAT) and 'hash' is the hash of | ||
542 | * the avatar data for caching purposes and it is exactly TOX_AVATAR_HASH_LENGTH long. If the | ||
543 | * image format is NONE, the hash is zeroed. | ||
544 | * | ||
545 | */ | ||
546 | void tox_callback_avatar_info(Tox *tox, void (*function)(Tox *tox, int32_t, uint8_t, uint8_t *, void *), | ||
547 | void *userdata); | ||
548 | |||
549 | |||
550 | /* Set the callback function for avatar data. | ||
551 | * This callback will be called when the complete avatar data was correctly received from a | ||
552 | * friend. This only happens in reply of a avatar data request (see tox_request_avatar_data); | ||
553 | * | ||
554 | * Function format is: | ||
555 | * function(Tox *tox, int32_t friendnumber, uint8_t format, uint8_t *hash, uint8_t *data, uint32_t datalen, void *userdata) | ||
556 | * | ||
557 | * where 'format' is the avatar image format (see TOX_AVATARFORMAT); 'hash' is the | ||
558 | * locally-calculated cryptographic hash of the avatar data and it is exactly | ||
559 | * TOX_AVATAR_HASH_LENGTH long; 'data' is the avatar image data and 'datalen' is the length | ||
560 | * of such data. | ||
561 | * | ||
562 | * If format is NONE, 'data' is NULL, 'datalen' is zero, and the hash is zeroed. The hash is | ||
563 | * always validated locally with the function tox_avatar_hash and ensured to match the image | ||
564 | * data, so this value can be safely used to compare with cached avatars. | ||
565 | * | ||
566 | * WARNING: users MUST treat all avatar image data received from another peer as untrusted and | ||
567 | * potentially malicious. The library only ensures that the data which arrived is the same the | ||
568 | * other user sent, and does not interpret or validate any image data. | ||
569 | */ | ||
570 | void tox_callback_avatar_data(Tox *tox, void (*function)(Tox *tox, int32_t, uint8_t, uint8_t *, uint8_t *, uint32_t, | ||
571 | void *), void *userdata); | ||
572 | |||
573 | /* Set the user avatar image data. | ||
574 | * This should be made before connecting, so we will not announce that the user have no avatar | ||
575 | * before setting and announcing a new one, forcing the peers to re-download it. | ||
576 | * | ||
577 | * Notice that the library treats the image as raw data and does not interpret it by any way. | ||
578 | * | ||
579 | * Arguments: | ||
580 | * format - Avatar image format or NONE for user with no avatar (see TOX_AVATARFORMAT); | ||
581 | * data - pointer to the avatar data (may be NULL it the format is NONE); | ||
582 | * length - length of image data. Must be <= TOX_MAX_AVATAR_DATA_LENGTH. | ||
583 | * | ||
584 | * returns 0 on success | ||
585 | * returns -1 on failure. | ||
586 | */ | ||
587 | int tox_set_avatar(Tox *tox, uint8_t format, const uint8_t *data, uint32_t length); | ||
588 | |||
589 | |||
590 | /* Get avatar data from the current user. | ||
591 | * Copies the current user avatar data to the destination buffer and sets the image format | ||
592 | * accordingly. | ||
593 | * | ||
594 | * If the avatar format is NONE, the buffer 'buf' isleft uninitialized, 'hash' is zeroed, and | ||
595 | * 'length' is set to zero. | ||
596 | * | ||
597 | * If any of the pointers format, buf, length, and hash are NULL, that particular field will be ignored. | ||
598 | * | ||
599 | * Arguments: | ||
600 | * format - destination pointer to the avatar image format (see TOX_AVATARFORMAT); | ||
601 | * buf - destination buffer to the image data. Must have at least 'maxlen' bytes; | ||
602 | * length - destination pointer to the image data length; | ||
603 | * maxlen - length of the destination buffer 'buf'; | ||
604 | * hash - destination pointer to the avatar hash (it must be exactly TOX_AVATAR_HASH_LENGTH bytes long). | ||
605 | * | ||
606 | * returns 0 on success; | ||
607 | * returns -1 on failure. | ||
608 | * | ||
609 | */ | ||
610 | int tox_get_self_avatar(const Tox *tox, uint8_t *format, uint8_t *buf, uint32_t *length, uint32_t maxlen, | ||
611 | uint8_t *hash); | ||
612 | |||
613 | |||
614 | /* Generates a cryptographic hash of the given avatar data. | ||
615 | * This function is a wrapper to internal message-digest functions and specifically provided | ||
616 | * to generate hashes from user avatars that may be memcmp()ed with the values returned by the | ||
617 | * other avatar functions. It is specially important to validate cached avatars. | ||
618 | * | ||
619 | * Arguments: | ||
620 | * hash - destination buffer for the hash data, it must be exactly TOX_AVATAR_HASH_LENGTH bytes long. | ||
621 | * data - avatar image data; | ||
622 | * datalen - length of the avatar image data; it must be <= TOX_MAX_AVATAR_DATA_LENGTH. | ||
623 | * | ||
624 | * returns 0 on success | ||
625 | * returns -1 on failure. | ||
626 | */ | ||
627 | int tox_avatar_hash(const Tox *tox, uint8_t *hash, const uint8_t *data, const uint32_t datalen); | ||
628 | |||
629 | |||
630 | /* Request avatar information from a friend. | ||
631 | * Asks a friend to provide their avatar information (image format and hash). The friend may | ||
632 | * or may not answer this request and, if answered, the information will be provided through | ||
633 | * the callback 'avatar_info'. | ||
634 | * | ||
635 | * returns 0 on success | ||
636 | * returns -1 on failure. | ||
637 | */ | ||
638 | int tox_request_avatar_info(const Tox *tox, const int32_t friendnumber); | ||
639 | |||
640 | |||
641 | /* Send an unrequested avatar information to a friend. | ||
642 | * Sends our avatar format and hash to a friend; he/she can use this information to validate | ||
643 | * an avatar from the cache and may (or not) reply with an avatar data request. | ||
644 | * | ||
645 | * Notice: it is NOT necessary to send these notification after changing the avatar or | ||
646 | * connecting. The library already does this. | ||
647 | * | ||
648 | * returns 0 on success | ||
649 | * returns -1 on failure. | ||
650 | */ | ||
651 | int tox_send_avatar_info(Tox *tox, const int32_t friendnumber); | ||
652 | |||
653 | |||
654 | /* Request the avatar data from a friend. | ||
655 | * Ask a friend to send their avatar data. The friend may or may not answer this request and, | ||
656 | * if answered, the information will be provided in callback 'avatar_data'. | ||
657 | * | ||
658 | * returns 0 on sucess | ||
659 | * returns -1 on failure. | ||
660 | */ | ||
661 | int tox_request_avatar_data(const Tox *tox, const int32_t friendnumber); | ||
520 | 662 | ||
521 | /****************FILE SENDING FUNCTIONS*****************/ | 663 | /****************FILE SENDING FUNCTIONS*****************/ |
522 | /* NOTE: This how to will be updated. | 664 | /* NOTE: This how to will be updated. |