summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/Avatars.md611
-rw-r--r--testing/Makefile.inc15
-rw-r--r--testing/test_avatars.c770
-rw-r--r--toxcore/Messenger.c469
-rw-r--r--toxcore/Messenger.h195
-rw-r--r--toxcore/tox.c54
-rw-r--r--toxcore/tox.h145
7 files changed, 2259 insertions, 0 deletions
diff --git a/docs/Avatars.md b/docs/Avatars.md
new file mode 100644
index 00000000..97b65c10
--- /dev/null
+++ b/docs/Avatars.md
@@ -0,0 +1,611 @@
1# User avatars in Tox
2
3
4
5## Introduction and rationale
6
7User avatars are small icons or images used to identify users in the friend
8list; they exists in virtually all VoIP and IM protocols and provide an easy
9way to an user identify another in the friend list.
10
11This document describes the implementation of avatars in the Tox protocol,
12according to the following design considerations:
13
14 - Avatars are handled as private information, ie., only exchanged over
15 Tox encrypted channels among previously authenticated friends;
16
17 - The library treats all images as blobs and does not interpret or
18 understands image formats, only ensures that the avatar data sent by
19 an user is correctly received by the other. The client application is
20 responsible for validating, decoding, resizing, and presenting the
21 image to the user.
22
23 - There is a strict limit of 16 KiB to the avatar raw data size -- this
24 seems suitable for practical use as, for example, the raw data of an
25 uncompressed 64 x 64 pixels 24 bpp RGB bitmap is 12288 bytes long; the
26 data limit provides enough space for larger bitmaps if the usual
27 compressed formats are used.
28
29 **Notice:** As designed, this limit can be changed in the future without
30 breaking the protocol compatibility, but clients using the original
31 limit will reject larger avatars;
32
33 - The protocol MUST provide means to allow caching and avoid unnecessary
34 data transfers;
35
36 - Avatars are transfered between clients in a background operation;
37
38 - Avatars are served in a "best effort" basis, without breaking clients
39 who do not support them;
40
41 - The protocol MUST resist to malicious users;
42
43 - The protocol MUST work with both UDP and TCP networks.
44
45
46The Single Tox Standard Draft v.0.1.0 recommends implementing avatars as
47a purely client-side feature through a procedure that can be summarized as
48sending a specially named file as a file transfer request and accepting
49it silently. This procedure can be improved to provide the previously stated
50design considerations, but this requires a higher integration with the core
51protocol. Moving this feature to the core protocol also:
52
53 - Provides a simpler and cleaner interfaces for client applications;
54
55 - Hides protocol complexities from the client;
56
57 - Avoids code duplication and ad-hoc protocols in the clients;
58
59 - Avoids incompatibility between client implementations;
60
61 - Allows important optimizations such as lightweight notification of
62 removed and updated avatars;
63
64 - Plays well with cache schemes;
65
66 - Makes avatar transfer an essentially background operation.
67
68
69
70
71
72
73## High level description
74
75The avatar exchange is implemented with the following new elements in the
76Tox protocol. This is a very high level description and the usage patterns
77expected from client applications are described in Section "Using Avatars
78in Client Applications" and a low level protocol description is available
79in Section "Internal Protocol Description".
80
81 - **Avatar Information Notifications** are events which may be sent by
82 an user to another anytime, but are usually sent after one of them
83 connects to the network, changes his avatar, or in reply to an **avatar
84 information request**. They are delivered by a very lightweight message
85 but with information enough to allow an user to validate or discard an
86 avatar from the local cache and decide if is interesting to request the
87 avatar data from the peer.
88
89 This event contain two data fields: (1) the image format and (2) the
90 cryptographic hash of the actual image data. Image format may be NONE
91 (for users who have no avatar or removed their avatars) or PNG. The
92 cryptographic hash is intended to be compared with the hash o the
93 currently cached avatar (if any) and check if it stills up to date.
94
95 - **Avatar Information Requests** are very lightweight messages sent by an
96 user asking for an **avatar information notification**. They may be sent
97 as part of the login process or when the client thinks the currently
98 cached avatar is outdated. The receiver may or may not answer to this
99 request. This message contains no data fields;
100
101 - An **Avatar Data Request** is sent by an user asking another for his
102 complete avatar data. It is sent only when the requesting user decides
103 the avatar do not exists in the local cache or is outdated. The receiver
104 may or may not answer to this request. This message contains no data
105 fields.
106
107 - An **Avatar Data Notification** is an event signaling the client that
108 the complete avatar image data of another user is available. The actual
109 data transfer is implemented using several data and control messages,
110 but the details are hidden from the client applications. This event can
111 only arrive in reply to an **avatar data request**.
112
113 This event contains three data fields: (1) the image format, (2) the
114 cryptographic hash of the image data, and (3) the raw image data. If the
115 image format is NONE (i.e. no avatar) the hash is zeroed and the image
116 data is empty. The raw image data is locally validated and ensured to
117 match the hash (the event is **not** triggered otherwise).
118
119
120
121
122
123## API
124
125To implement this feature, the following public symbols were added. The
126complete API documentation is available in `tox.h`.
127
128
129```
130#define TOX_MAX_AVATAR_DATA_LENGTH 16384
131#define TOX_AVATAR_HASH_LENGTH 32
132
133
134/* Data formats for user avatar images */
135typedef enum {
136 TOX_AVATARFORMAT_NONE,
137 TOX_AVATARFORMAT_PNG
138}
139TOX_AVATARFORMAT;
140
141
142
143/* Set the user avatar image data. */
144int tox_set_avatar(Tox *tox, uint8_t format, const uint8_t *data, uint32_t length);
145
146/* Get avatar data from the current user. */
147int tox_get_self_avatar(const Tox *tox, uint8_t *format, uint8_t *buf, uint32_t *length, uint32_t maxlen, uint8_t *hash);
148
149/* Generates a cryptographic hash of the given avatar data. */
150int tox_avatar_hash(const Tox *tox, uint8_t *hash, const uint8_t *data, const uint32_t datalen);
151
152/* Request avatar information from a friend. */
153int tox_request_avatar_info(const Tox *tox, const int32_t friendnumber);
154
155/* Send an unrequested avatar information to a friend. */
156int tox_send_avatar_info(Tox *tox, const int32_t friendnumber);
157
158/* Request the avatar data from a friend. */
159int tox_request_avatar_data(const Tox *tox, const int32_t friendnumber);
160
161/* Set the callback function for avatar data. */
162void tox_callback_avatar_info(Tox *tox, void (*function)(Tox *tox, int32_t, uint8_t, uint8_t*, void *), void *userdata);
163
164/* Set the callback function for avatar data. */
165void tox_callback_avatar_data(Tox *tox, void (*function)(Tox *tox, int32_t, uint8_t, uint8_t*, uint8_t*, uint32_t, void *), void *userdata);
166```
167
168
169
170
171## Using Avatars in Client Applications
172
173
174### General recommendations
175
176 - Clients MUST NOT imply the availability of avatars in other users.
177 Avatars are an optional feature and not all users and clients may
178 support them;
179
180 - Clients MUST NOT block waiting for avatar information and avatar data
181 packets;
182
183 - Clients MUST treat avatar data as insecure and potentially malicious;
184 For example, users may accidentally use corrupted images as avatars,
185 a malicious user may send a specially crafted image to exploit a know
186 vulnerability in an image decoding library, etc. It is recommended to
187 handle the avatar image data in the same way as an image downloaded
188 from an unknown Internet source;
189
190 - The peers MUST NOT assume any coupling between the operations of
191 receiving an avatar information packet, sending unrequested avatar
192 information packets, requesting avatar data, or receiving avatar data.
193
194 For example, the following situations are valid:
195
196 * A text-mode client may send avatars to other users, but never
197 request them;
198
199 * A client may not understand a particular image format and ignore
200 avatars using it, but request and handle other formats;
201
202 - Clients SHOULD implement a local cache of avatars and do not request
203 avatar data from other peers unless necessary;
204
205 - When an avatar information is received, the client should delete the
206 avatar if the new avatar format is NONE or compare the hash received
207 from the peer with the hash of the currently cached avatar. If they
208 differ, send an avatar data request;
209
210 - If the cached avatar is older than a given threshold, the client may
211 also send an avatar info request to that friend once he is online and
212 mark the avatar as updated *before* any avatar information is received
213 (to not spam the peer with such requests);
214
215 - When an avatar data notification is received, the client must update
216 the cached avatar with the new one;
217
218 - Clients should resize or crop the image to the way it better adapts
219 to the client user interface;
220
221 - If the user already have an avatar defined in the client configuration,
222 it must be set before connecting to the network to avoid spurious avatar
223 change notifications and unnecessary data transfers.
224
225 - If no avatar data is available for a given friend, the client should
226 show a placeholder image.
227
228
229
230### Interoperability and sharing avatars among different clients
231
232**This section is a tentative recommendation of how clients should store
233avatars to ensure local interoperability and should be revised if this
234code is accepted into Tox core.**
235
236It is desirable that the user avatar and the cached friends avatars could be
237shared among different Tox clients in the same system, in the spirit of the
238proposed Single Tox Standard. This not only makes switching from one client
239to another easier, but also minimizes the need of data transfers, as avatars
240already downloaded by other clients can be reused.
241
242Given the Tox data directory described in STS Draft v0.1.0:
243
244 - The user avatar is stored in a file named "avatar.png". As more formats
245 may be used in the future, another extensions are reserved and clients
246 should keep just one file named "avatar.*", with the data of the last
247 avatar set by the user. If the user have no avatar, no such files should
248 be kept in the data directory;
249
250 - Friends avatars are stored in a directory called "avatars" and named
251 as "xxxxx.png", where "xxxxx" is the complete client id encoded as an
252 uppercase hexadecimal string and "png" is the extension for the PNG
253 avatar. As new image formats may be used in the future, clients should
254 ensure no other file "xxxxx.*" exists. No file should be kept for an user
255 who have no avatar.
256
257 **To be discussed:** User keys are usually presented in Tox clients as
258 upper case strings, but lower case file names are more usual.
259
260
261Example for Linux and other Unix systems, assuming an user called "gildor":
262
263 Tox data directory: /home/gildor/.config/tox/
264 Tox data file: /home/gildor/.config/tox/data
265 Gildor's avatar: /home/gildor/.config/tox/avatar.png
266 Avatar data dir: /home/gildor/.config/tox/avatars/
267 Elrond's avatar: /home/gildor/.config/tox/avatars/43656C65627269616E20646F6E277420546F782E426164206D656D6F72696573.png
268 Elladan's avatar: /home/gildor/.config/tox/avatars/49486174655768656E48756D616E735468696E6B49416D4D7942726F74686572.png
269 Elrohir's avatar /home/gildor/.config/tox/avatars/726568746F7242794D6D41496B6E696854736E616D75486E6568576574614849.png
270 Arwen's avatar: /home/gildor/.config/tox/avatars/53686520746F6F6B20476C6F7266696E64656C277320706C6163652068657265.png
271 Lindir's avatar: /home/gildor/.config/tox/avatars/417070735772697474656E42794D6F7274616C734C6F6F6B54686553616D652E.png
272
273This recommendation is partially implemented by "testing/test_avatars.c".
274
275
276
277
278
279### Common operations
280
281These are minimal examples of how perform common operations with avatar
282functions. For a complete, working, example, see `testing/test_avatars.c`.
283
284
285#### Setting an avatar for the current user
286
287In this example `load_data_file` is just an hypothetical function that loads
288data from a file into the buffer and sets the length accordingly.
289
290 uint8_t buf[TOX_MAX_AVATAR_DATA_LENGTH];
291 uint32_t len;
292
293 if (load_data_file("avatar.png", buf, &len) == 0)
294 if (tox_set_avatar(tox, TOX_AVATARFORMAT_PNG, buf, len) != 0)
295 fprintf(stderr, "Failed to set avatar.\n");
296
297If the user is connected, this function will also notify all connected
298friends about the avatar change.
299
300If the user already have an avatar defined in the client configuration, it
301must be set before connecting to the network to avoid spurious avatar change
302notifications and unnecessary data transfers.
303
304
305
306
307#### Removing the avatar from the current user
308
309To remove an avatar, an application must set it to `TOX_AVATARFORMAT_NONE`.
310
311 tox_set_avatar(tox, TOX_AVATARFORMAT_NONE, NULL, 0);
312
313If the user is connected, this function will also notify all connected
314friends about the avatar change.
315
316
317
318
319
320#### Receiving avatar information from friends
321
322All avatar information is passed to a callback function with the prototype:
323
324 void function(Tox *tox, int32_t friendnumber, uint8_t format,
325 uint8_t *hash, uint8_t *data, uint32_t datalen, void *userdata)
326
327As in this example:
328
329 static void avatar_info_cb(Tox *tox, int32_t friendnumber, uint8_t format,
330 uint8_t *hash, void *userdata)
331 {
332 printf("Receiving avatar information from friend %d. Format = %d\n",
333 friendnumber, format);
334 printf("Data hash: ");
335 hex_printf(hash, TOX_AVATAR_HASH_LENGTH); /* Hypothetical function */
336 printf("\n");
337 }
338
339And, somewhere in the Tox initialization calls, set if as the callback to be
340triggered when an avatar information event arrives:
341
342 tox_callback_avatar_info(tox, avatar_info_cb, NULL);
343
344
345A typical client will test the currently cached avatar against the hash given
346in the avatar information event and, if needed, request the avatar data.
347
348
349
350#### Receiving avatar data from friends
351
352Avatar data events are only delivered in reply of avatar data requests which
353**should** only be sent after getting the user avatar information (format
354and hash) from an avatar information event and checking it against a local
355cache.
356
357For this, an application must define an avatar information callback which
358checks the local avatar cache and emits an avatar data request if necessary:
359
360 static void avatar_info_cb(Tox *tox, int32_t friendnumber, uint8_t format,
361 uint8_t *hash, void *userdata)
362 {
363 printf("Receiving avatar information from friend %d. Format = %d\n",
364 friendnumber, format);
365 if (format = TOX_AVATARFORMAT_NONE) {
366 /* User have no avatar or removed the avatar */
367 delete_avatar_from_cache(tox, friendnumber);
368 } else {
369 /* Use the received hash to check if the cached avatar is
370 still updated. */
371 if (!is_user_cached_avatar_updated(tox, friendnumber, hash)) {
372 /* User avatar is outdated, send data request */
373 tox_request_avatar_data(tox, friendnumber);
374 }
375 }
376 }
377
378
379Then define an avatar data callback to store the received data in the local
380cache:
381
382 static void avatar_data_cb(Tox *tox, int32_t friendnumber, uint8_t format,
383 uint8_t *hash, uint8_t *data, uint32_t datalen, void *userdata)
384 {
385 if (format = TOX_AVATARFORMAT_NONE) {
386 /* User have no avatar or removed the avatar */
387 delete_avatar_from_cache(tox, friendnumber);
388 } else {
389 save_avatar_data_to_cache(tox, friendnumber, format, hash,
390 data, datalen);
391 }
392 }
393
394
395And, finally, register both callbacks somewhere in the Tox initialization
396calls:
397
398 tox_callback_avatar_info(tox, avatar_info_cb, NULL);
399 tox_callback_avatar_data(tox, avatar_data_cb, NULL);
400
401
402In the previous examples, implementation of the functions to check, store
403and retrieve data from the cache were omitted for brevity. These functions
404will also need to get the friend client ID (public key) from they friend
405number and, usually, convert it from a byte string to a hexadecimal
406string. A complete, yet more complex, example is available in the file
407`testing/test_avatars.c`.
408
409
410
411
412
413
414
415
416
417
418
419## Internal Protocol Description
420
421### New packet types
422
423The avatar transfer protocol adds the following new packet types and ids:
424
425 PACKET_ID_AVATAR_INFO_REQ = 52
426 PACKET_ID_AVATAR_INFO = 53
427 PACKET_ID_AVATAR_DATA_CONTROL = 54
428 PACKET_ID_AVATAR_DATA_START = 55
429 PACKET_ID_AVATAR_DATA_PUSH = 56
430
431
432
433
434### Requesting avatar information
435
436To request avatar information, an user must send a packet of type
437`PACKET_ID_AVATAR_INFO_REQ`. This packet have no data fields. Upon
438receiving this packet, a client which supports avatars should answer with
439a `PACKET_ID_AVATAR_INFO`. The sender must accept that the friend may
440not answer at all.
441
442
443
444
445### Receiving avatar information
446
447Avatar information arrives in a packet of type `PACKET_ID_AVATAR_INFO` with
448the following structure:
449
450 PACKET_ID_AVATAR_INFO (53)
451 Packet data size: 33 bytes
452 [1: uint8_t format][32: uint8_t hash]
453
454Where 'format' is the image data format, one of the following:
455
456 0 = AVATARFORMAT_NONE (no avatar set)
457 1 = AVATARFORMAT_PNG
458
459and 'hash' is the SHA-256 message digest of the avatar data.
460
461This packet may be sent at any time and no previous request is required.
462Clients should send this packet upon connection or when a friend
463connects, in the same way Tox sends name, status and action information.
464
465
466
467
468
469### Requesting avatar data
470
471Transmission of avatar data is a multi-step procedure using three new packet
472types.
473
474 - Packet `PACKET_ID_AVATAR_DATA_CONTROL` have the format:
475
476 PACKET_ID_AVATAR_DATA_CONTROL (54)
477 Packet data size: 1 byte
478 [1: uint8_t op]
479
480 where 'op' is a code signaling both an operation request or a status
481 return, which semantics are explained bellow. The following values are
482 defined:
483
484 0 = AVATARDATACONTROL_REQ
485 1 = AVATARDATACONTROL_ERROR
486
487
488 - Packet `PACKET_ID_AVATAR_DATA_START` have the following format:
489
490 PACKET_ID_AVATAR_DATA_START (55)
491 Packet data size: 37 bytes
492 [1: uint8_t format][32: uint8_t hash][1: uint32_t data_length]
493
494
495 where 'format' is the image format, with the same values accepted for
496 the field 'format' in packet type `PACKET_ID_AVATAR_INFO`, 'hash' is
497 the SHA-256 cryptographic hash of the avatar raw data and 'data_length'
498 is the total number of bytes the raw avatar data.
499
500
501 - Packet `PACKET_ID_AVATAR_DATA_PUSH` have no format structure, just up
502 to `AVATAR_DATA_MAX_CHUNK_SIZE` bytes of raw avatar image data; this
503 value is defined according to the maximum amount of data a Tox crypted
504 packet can hold.
505
506
507
508The following procedure assumes that a client "A" is requesting avatar data
509from a client "B":
510
511 - "A" must initialize its control structures and mark its data transfer
512 as not yet started. Then it requests avatar data from "B" by sending a
513 packet `PACKET_ID_AVATAR_DATA_CONTROL` with 'op' set to
514 `AVATARDATACONTROL_REQ`.
515
516 - If "B" accepts this transfer, it answers by sending an
517 `PACKET_ID_AVATAR_DATA_START` with the fields 'format', 'hash' and
518 'data_length' set to the respective values from the current avatar.
519 If "B" have no avatar set, 'format' must be `AVATARFORMAT_NONE`, 'hash'
520 must be zeroed and 'data_length' must be zero.
521
522 If "B" does not accept sending the avatar, it may send a packet
523 `PACKET_ID_AVATAR_DATA_CONTROL` with the field 'op' set to
524 `AVATARDATACONTROL_ERROR` or simply ignore this request. "A" must cope
525 with this.
526
527 If "B" have an avatar, it sends a variable number of
528 `PACKET_ID_AVATAR_DATA_PUSH` packets with the avatar data in a single
529 shot.
530
531 - Upon receiving a `PACKET_ID_AVATAR_DATA_START`, "A" checks if it
532 has sent a data request to "B". If not, just ignores the packet.
533
534 If "A" really requested avatar data and the format is `AVATARFORMAT_NONE`,
535 it triggers the avatar data callback, and clears all the temporary data,
536 finishing the process. For other formats, "A" just waits for packets
537 of type `PACKET_ID_AVATAR_DATA_PUSH`.
538
539 - Upon receiving a `PACKET_ID_AVATAR_DATA_PUSH`, "A" checks if it really
540 sent an avatar data request and if the `PACKET_ID_AVATAR_DATA_START` was
541 already received. If this conditions are valid, it checks if the total
542 length of the data already stored in the receiving buffer plus the data
543 present in the push packet is still less or equal than
544 `TOX_MAX_AVATAR_DATA_LENGTH`. If invalid, it replies with a
545 `PACKET_ID_AVATAR_DATA_CONTROL` with the field 'op' set to
546 `AVATARDATACONTROL_ERROR`.
547
548 If valid, "A" updates the 'bytes_received' counter and concatenates the
549 newly arrived data to the buffer.
550
551 The "A" checks if all the data was already received by comparing the
552 counter 'bytes_received' with the field 'total_length'. If they are
553 equal, "A" takes a SHA-256 hash of the data and compares it with the
554 hash stored in the field 'hash' received from the first
555 `PACKET_ID_AVATAR_DATA_START`.
556
557 If the hashes match, the avatar data was correctly received and "A"
558 triggers the avatar data callback, and clears all the temporary data,
559 finishing the process.
560
561 If not all data was received, "A" simply waits for more data.
562
563 Client "A" is always responsible for controlling the transfer and
564 validating the data received. "B" don't need to keep any state for the
565 protocol, have full control over the data sent and should implement
566 some transfer limit for the data it sends.
567
568 - Any peer receiving a `PACKET_ID_AVATAR_DATA_CONTROL` with the field 'op'
569 set to `AVATARDATACONTROL_ERROR` clears any existing control state and
570 finishes sending or receiving data.
571
572
573
574
575
576## Security considerations
577
578The major security implication of background data transfers of large objects,
579like avatars, is the possibility of exhausting the network resources from a
580client. This problem is exacerbated when there is the possibility of an
581amplification attack as happens, for example, when sending a very small
582avatar request message will force the user to reply with a larger avatar
583data message.
584
585The present proposal mitigates this situation by:
586
587 - Only transferring data between previously authenticated friends;
588
589 - Enforcing strict limits on the avatar data size;
590
591 - Providing an alternate, smaller, message to cooperative users refresh
592 avatar information when nothing has changed (`PACKET_ID_AVATAR_INFO`);
593
594 - Having per-friend data transfer limit. As the current protocol still
595 allows an user to request an infinite data stream by asking the the
596 same offset of the avatar again and again, the implementation limits
597 the amount of data a single user can request for some time. For now,
598 the library will not allow an user to request more than
599 `10*TOX_MAX_AVATAR_DATA_LENGTH` in less than 20 minutes;
600
601 - Making the requester responsible for storing partial data and state
602 information;
603
604Another problem present in the avatars is the possibility of a friend send
605a maliciously crafted image intended to exploit vulnerabilities in image
606decoders. Without an intermediate server to recompress and validate and
607convert the images to neutral formats, the client applications must handle
608this situation by themselves using stable and secure image libraries and
609imposing limits on the maximum amount of system resources the decoding
610process can take. Images coming from Tox friends must be treated in the same
611way as images coming from random Internet sources.
diff --git a/testing/Makefile.inc b/testing/Makefile.inc
index 42d89e05..6bb87913 100644
--- a/testing/Makefile.inc
+++ b/testing/Makefile.inc
@@ -105,6 +105,21 @@ tox_shell_LDADD = $(LIBSODIUM_LDFLAGS) \
105 $(NACL_LIBS) \ 105 $(NACL_LIBS) \
106 -lutil 106 -lutil
107 107
108
109noinst_PROGRAMS += test_avatars
110
111test_avatars_SOURCES = ../testing/test_avatars.c
112
113test_avatars_CFLAGS = $(LIBSODIUM_CFLAGS) \
114 $(NACL_CFLAGS)
115
116test_avatars_LDADD = $(LIBSODIUM_LDFLAGS) \
117 $(NACL_LDFLAGS) \
118 libtoxcore.la \
119 $(LIBSODIUM_LIBS) \
120 $(NACL_OBJECTS) \
121 $(NACL_LIBS)
122
108endif 123endif
109 124
110EXTRA_DIST += $(top_srcdir)/testing/misc_tools.c 125EXTRA_DIST += $(top_srcdir)/testing/misc_tools.c
diff --git a/testing/test_avatars.c b/testing/test_avatars.c
new file mode 100644
index 00000000..7d96bd52
--- /dev/null
+++ b/testing/test_avatars.c
@@ -0,0 +1,770 @@
1/*
2 * A bot to test Tox avatars
3 *
4 * Usage: ./test_avatars <data dir>
5 *
6 * Connects to the Tox network, publishes our avatar, requests our friends
7 * avatars and, if available, saves them to a local cache.
8 * This bot automatically accepts any friend request.
9 *
10 *
11 * Data dir MUST have:
12 *
13 * - A file named "data" (named accordingly to STS Draft v0.1.0) with
14 * user id, friends, bootstrap data, etc. from a previously configured
15 * Tox session; use a client (eg. toxic) to configure it, add friends,
16 * etc.
17 *
18 * Data dir MAY have:
19 *
20 * - A file named avatar.png. If given, the bot will publish it. Otherwise,
21 * no avatar will be set.
22 *
23 * - A directory named "avatars" with the currently cached avatars.
24 *
25 *
26 * The bot will answer to these commands:
27 *
28 * !debug-on - Enable extended debug messages
29 * !debug-off - Disenable extended debug messages
30 * !set-avatar - Set our avatar to the contents of the file avatar.*
31 * !remove-avatar - Remove our avatar
32 *
33 */
34
35#define DATA_FILE_NAME "data"
36#define AVATAR_DIR_NAME "avatars"
37
38#ifdef HAVE_CONFIG_H
39#include "config.h"
40#endif
41
42#include "../toxcore/tox.h"
43
44#include <stdlib.h>
45#include <stdio.h>
46#include <string.h>
47#include <unistd.h>
48#include <time.h>
49#include <stdbool.h>
50#include <linux/limits.h>
51#include <sys/stat.h>
52#include <unistd.h>
53#include <errno.h>
54#include <stdarg.h>
55
56
57
58/* Basic debug utils */
59
60#define DEBUG(format, ...) debug_printf("DEBUG: %s:%d %s: " format "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__)
61
62static bool print_debug_msgs = true;
63
64static void debug_printf(const char *fmt, ...)
65{
66 if (print_debug_msgs == true) {
67 va_list ap;
68 va_start(ap, fmt);
69 vprintf(fmt, ap);
70 va_end(ap);
71 }
72}
73
74
75
76
77
78
79/* ------------ Avatar cache managenment functions ------------ */
80
81typedef struct {
82 uint8_t format;
83 char *suffix;
84 char *file_name;
85} def_avatar_name_t;
86
87static const def_avatar_name_t def_avatar_names[] = {
88 /* In order of preference */
89 { TOX_AVATARFORMAT_PNG, "png", "avatar.png" },
90 { TOX_AVATARFORMAT_NONE, NULL, NULL }, /* Must be the last one */
91};
92
93
94
95static void set_avatar(Tox *tox, const char *base_dir);
96
97
98static char *get_avatar_suffix_from_format(uint8_t format)
99{
100 int i;
101
102 for (i = 0; def_avatar_names[i].format != TOX_AVATARFORMAT_NONE; i++)
103 if (def_avatar_names[i].format == format)
104 return def_avatar_names[i].suffix;
105
106 return NULL;
107}
108
109
110/* Load avatar data from a file into a memory buffer 'buf'.
111 * buf must have at least TOX_MAX_AVATAR_DATA_LENGTH bytes
112 * Returns the length of the data sucess or < 0 on error
113 */
114static int load_avatar_data(char *fname, uint8_t *buf)
115{
116 FILE *fp = fopen(fname, "rb");
117
118 if (fp == NULL)
119 return -1; /* Error */
120
121 size_t n = fread(buf, 1, TOX_MAX_AVATAR_DATA_LENGTH, fp);
122 int ret;
123
124 if (ferror(fp) != 0 || n == 0)
125 ret = -1; /* Error */
126 else
127 ret = n;
128
129 fclose(fp);
130 return ret;
131}
132
133
134/* Save avatar data to a file */
135static int save_avatar_data(char *fname, uint8_t *data, uint32_t len)
136{
137 FILE *fp = fopen(fname, "wb");
138
139 if (fp == NULL)
140 return -1; /* Error */
141
142 int ret = 0; /* Ok */
143
144 if (fwrite(data, 1, len, fp) != len)
145 ret = -1; /* Error */
146
147 if (fclose(fp) != 0)
148 ret = -1; /* Error */
149
150 return ret;
151}
152
153
154static void byte_to_hex_str(const uint8_t *buf, const size_t buflen, char *dst)
155{
156 const char *hex_chars = "0123456789ABCDEF";
157 size_t i = 0;
158 size_t j = 0;
159
160 while (i < buflen) {
161 dst[j++] = hex_chars[(buf[i] >> 4) & 0xf];
162 dst[j++] = hex_chars[buf[i] & 0xf];
163 i++;
164 }
165
166 dst[j++] = '\0';
167}
168
169/* Make the cache file name for a avatar of the given format for the given
170 * client id.
171 */
172static int make_avatar_file_name(char *dst, size_t dst_len,
173 char *base_dir, uint8_t format, uint8_t *client_id)
174{
175 char client_id_str[2 * TOX_CLIENT_ID_SIZE + 1];
176 byte_to_hex_str(client_id, TOX_CLIENT_ID_SIZE, client_id_str);
177
178 const char *suffix = get_avatar_suffix_from_format(format);
179
180 if (suffix == NULL)
181 return -1; /* Error */
182
183 int n = snprintf(dst, dst_len, "%s/%s/%s.%s", base_dir, AVATAR_DIR_NAME,
184 client_id_str, suffix);
185 dst[dst_len - 1] = '\0';
186
187 if (n >= dst_len)
188 return -1; /* Error: Output truncated */
189
190 return 0; /* Ok */
191}
192
193
194/* Load a cached avatar into the buffer 'data' (which must be at least
195 * TOX_MAX_AVATAR_DATA_LENGTH bytes long). Gets the file name from client
196 * id and the given data format.
197 * Returns 0 on success, or -1 on error.
198 */
199static int load_user_avatar(Tox *tox, char *base_dir, int friendnum,
200 uint8_t format, uint8_t *hash, uint8_t *data, uint32_t *datalen)
201{
202 uint8_t addr[TOX_CLIENT_ID_SIZE];
203
204 if (tox_get_client_id(tox, friendnum, addr) != 0) {
205 DEBUG("Bad client id, friendnumber=%d", friendnum);
206 return -1;
207 }
208
209 char path[PATH_MAX];
210 int ret = make_avatar_file_name(path, sizeof(path), base_dir, format, addr);
211
212 if (ret != 0) {
213 DEBUG("Can't create an file name for this user/avatar.");
214 return -1;
215 }
216
217 ret = load_avatar_data(path, data);
218
219 if (ret < 0) {
220 DEBUG("Failed to load avatar data.");
221 return -1;
222 }
223
224 *datalen = ret;
225 tox_avatar_hash(tox, hash, data, *datalen);
226
227 return 0;
228}
229
230/* Save a user avatar into the cache. Gets the file name from client id and
231 * the given data format.
232 * Returns 0 on success, or -1 on error.
233 */
234static int save_user_avatar(Tox *tox, char *base_dir, int friendnum,
235 uint8_t format, uint8_t *data, uint32_t datalen)
236{
237 uint8_t addr[TOX_CLIENT_ID_SIZE];
238
239 if (tox_get_client_id(tox, friendnum, addr) != 0) {
240 DEBUG("Bad client id, friendnumber=%d", friendnum);
241 return -1;
242 }
243
244 char path[PATH_MAX];
245 int ret = make_avatar_file_name(path, sizeof(path), base_dir, format, addr);
246
247 if (ret != 0) {
248 DEBUG("Can't create a file name for this user/avatar");
249 return -1;
250 }
251
252 return save_avatar_data(path, data, datalen);
253}
254
255/* Delete all cached avatars for a given user */
256static int delete_user_avatar(Tox *tox, char *base_dir, int friendnum)
257{
258 uint8_t addr[TOX_CLIENT_ID_SIZE];
259
260 if (tox_get_client_id(tox, friendnum, addr) != 0) {
261 DEBUG("Bad client id, friendnumber=%d", friendnum);
262 return -1;
263 }
264
265 char path[PATH_MAX];
266
267 /* This iteration is dumb and inefficient */
268 int i;
269
270 for (i = 0; def_avatar_names[i].format != TOX_AVATARFORMAT_NONE; i++) {
271 int ret = make_avatar_file_name(path, sizeof(path), base_dir,
272 def_avatar_names[i].format, addr);
273
274 if (ret != 0) {
275 DEBUG("Failed to create avatar path for friend #%d, format %d\n",
276 friendnum, def_avatar_names[i].format);
277 continue;
278 }
279
280 if (unlink(path) == 0)
281 printf("Avatar file %s deleted.\n", path);
282 }
283
284 return 0;
285}
286
287
288
289
290/* ------------ Protocol callbacks ------------ */
291
292static void friend_status_cb(Tox *tox, int n, uint8_t status, void *ud)
293{
294 uint8_t addr[TOX_CLIENT_ID_SIZE];
295 char addr_str[2 * TOX_CLIENT_ID_SIZE + 1];
296
297 if (tox_get_client_id(tox, n, addr) == 0) {
298 byte_to_hex_str(addr, TOX_CLIENT_ID_SIZE, addr_str);
299 printf("Receiving status from %s: %u\n", addr_str, status);
300 }
301}
302
303static void friend_avatar_info_cb(Tox *tox, int32_t n, uint8_t format, uint8_t *hash, void *ud)
304{
305 char *base_dir = (char *) ud;
306 uint8_t addr[TOX_CLIENT_ID_SIZE];
307 char addr_str[2 * TOX_CLIENT_ID_SIZE + 1];
308 char hash_str[2 * TOX_AVATAR_HASH_LENGTH + 1];
309
310 if (tox_get_client_id(tox, n, addr) == 0) {
311 byte_to_hex_str(addr, TOX_CLIENT_ID_SIZE, addr_str);
312 printf("Receiving avatar information from %s.\n", addr_str);
313 } else {
314 DEBUG("tox_get_client_id failed");
315 printf("Receiving avatar information from friend number %u.\n", n);
316 }
317
318 byte_to_hex_str(hash, TOX_AVATAR_HASH_LENGTH, hash_str);
319 DEBUG("format=%u, hash=%s", format, hash_str);
320
321 if (format == TOX_AVATARFORMAT_NONE) {
322 printf(" -> User do not have an avatar.\n");
323 /* User have no avatar anymore, delete it from our cache */
324 delete_user_avatar(tox, base_dir, n);
325 } else {
326 /* Check the hash of the currently cached user avatar
327 * WARNING: THIS IS ONLY AN EXAMPLE!
328 *
329 * Real clients should keep the hashes in memory (eg. in the object
330 * used to represent a friend in the friend list) and do not access
331 * the file system or do anything resource intensive in reply of
332 * these events.
333 */
334 uint32_t cur_av_len;
335 uint8_t cur_av_data[TOX_MAX_AVATAR_DATA_LENGTH];
336 uint8_t cur_av_hash[TOX_AVATAR_HASH_LENGTH];
337 int ret;
338
339 ret = load_user_avatar(tox, base_dir, n, format, cur_av_hash, cur_av_data, &cur_av_len);
340
341 if (ret != 0
342 && memcpy(cur_av_hash, hash, TOX_AVATAR_HASH_LENGTH) != 0) {
343 printf(" -> Cached avatar is outdated. Requesting avatar data.\n");
344 tox_request_avatar_data(tox, n);
345 } else {
346 printf(" -> Cached avatar is still updated.\n");
347 }
348 }
349
350}
351
352static void friend_avatar_data_cb(Tox *tox, int32_t n, uint8_t format,
353 uint8_t *hash, uint8_t *data, uint32_t datalen, void *ud)
354{
355 char *base_dir = (char *) ud;
356 uint8_t addr[TOX_CLIENT_ID_SIZE];
357 char addr_str[2 * TOX_CLIENT_ID_SIZE + 1];
358 char hash_str[2 * TOX_AVATAR_HASH_LENGTH + 1];
359
360 if (tox_get_client_id(tox, n, addr) == 0) {
361 byte_to_hex_str(addr, TOX_CLIENT_ID_SIZE, addr_str);
362 printf("Receiving avatar data from %s.\n", addr_str);
363 } else {
364 DEBUG("tox_get_client_id failed");
365 printf("Receiving avatar data from friend number %u.\n", n);
366 }
367
368 byte_to_hex_str(hash, TOX_AVATAR_HASH_LENGTH, hash_str);
369 DEBUG("format=%u, datalen=%d, hash=%s\n", format, datalen, hash_str);
370
371 delete_user_avatar(tox, base_dir, n);
372
373 if (format != TOX_AVATARFORMAT_NONE) {
374 int ret = save_user_avatar(tox, base_dir, n, format, data, datalen);
375
376 if (ret == 0)
377 printf(" -> Avatar updated in the cache.\n");
378 else
379 printf(" -> Failed to save user avatar.\n");
380 }
381}
382
383
384static void friend_msg_cb(Tox *tox, int n, const uint8_t *msg, uint16_t len, void *ud)
385{
386 const char *base_dir = (char *) ud;
387 const char *msg_str = (char *) msg;
388 uint8_t addr[TOX_CLIENT_ID_SIZE];
389 char addr_str[2 * TOX_CLIENT_ID_SIZE + 1];
390
391 if (tox_get_client_id(tox, n, addr) == 0) {
392 byte_to_hex_str(addr, TOX_FRIEND_ADDRESS_SIZE, addr_str);
393 printf("Receiving message from %s:\n %s\n", addr_str, msg);
394 }
395
396 /* Handle bot commands for the tests */
397 char *reply_ptr = NULL;
398
399 if (strstr(msg_str, "!debug-on") != NULL) {
400 print_debug_msgs = true;
401 reply_ptr = "Debug enabled.";
402 } else if (strstr(msg_str, "!debug-off") != NULL) {
403 print_debug_msgs = false;
404 reply_ptr = "Debug disabled.";
405 } else if (strstr(msg_str, "!set-avatar") != NULL) {
406 set_avatar(tox, base_dir);
407 reply_ptr = "Setting image avatar";
408 } else if (strstr(msg_str, "!remove-avatar") != NULL) {
409 int r = tox_set_avatar(tox, TOX_AVATARFORMAT_NONE, NULL, 0);
410 DEBUG("tox_set_avatar returned %d", r);
411 reply_ptr = "Removing avatar";
412 }
413
414 /* Add more useful commands here: add friend, etc. */
415
416 char reply[TOX_MAX_MESSAGE_LENGTH];
417 int reply_len;
418
419 if (reply_ptr)
420 reply_len = snprintf(reply, sizeof(reply), "%s", reply_ptr);
421 else
422 reply_len = snprintf(reply, sizeof(reply),
423 "No command found in message: %s", msg);
424
425 reply[sizeof(reply) - 1] = '\0';
426 printf(" -> Reply: %s\n", reply);
427 tox_send_message(tox, n, (uint8_t *) reply, reply_len);
428}
429
430
431static void friend_request_cb(Tox *tox, const uint8_t *public_key,
432 const uint8_t *data, uint16_t length, void *ud)
433{
434 char addr_str[2 * TOX_CLIENT_ID_SIZE + 1];
435 byte_to_hex_str(public_key, TOX_CLIENT_ID_SIZE, addr_str);
436 printf("Accepting friend request from %s.\n %s\n", addr_str, data);
437 tox_add_friend_norequest(tox, public_key);
438}
439
440
441static int try_avatar_file(Tox *tox, const char *base_dir, const def_avatar_name_t *an)
442{
443 char path[PATH_MAX];
444 int n = snprintf(path, sizeof(path), "%s/%s", base_dir, an->file_name);
445 path[sizeof(path) - 1] = '\0';
446
447 if (n >= sizeof(path)) {
448 DEBUG("error: path %s too big\n", path);
449 return -1;
450 }
451
452 DEBUG("trying file %s: ", path);
453 FILE *fp = fopen(path, "rb");
454
455 if (fp != NULL) {
456 uint8_t buf[2 * TOX_MAX_AVATAR_DATA_LENGTH];
457 int len = fread(buf, 1, sizeof(buf), fp);
458
459 if (len >= 0 && len <= TOX_MAX_AVATAR_DATA_LENGTH) {
460 int r = tox_set_avatar(tox, an->format, buf, len);
461 DEBUG("%d bytes, tox_set_avatar returned=%d", len, r);
462
463 if (r == 0)
464 printf("Setting avatar file %s\n", path);
465 else
466 printf("Error setting avatar file %s\n", path);
467 } else if (len < 0) {
468 DEBUG("read error %d", len);
469 } else {
470 printf("Avatar file %s if too big (more than %d bytes)",
471 path, TOX_MAX_AVATAR_DATA_LENGTH);
472 }
473
474 fclose(fp);
475 return 0;
476 } else {
477 DEBUG("File %s not found", path);
478 }
479
480 return -1;
481}
482
483
484static void set_avatar(Tox *tox, const char *base_dir)
485{
486 int i;
487
488 for (i = 0; i < 4; i++) {
489 if (def_avatar_names[i].format == TOX_AVATARFORMAT_NONE) {
490 tox_set_avatar(tox, TOX_AVATARFORMAT_NONE, NULL, 0);
491 printf("No avatar file found, setting to NONE.\n");
492 return;
493 } else {
494 if (try_avatar_file(tox, base_dir, &def_avatar_names[i]) == 0)
495 return;
496 }
497 }
498
499 /* Should be unreachable */
500 printf("UNEXPECTED CODE PATH\n");
501}
502
503
504static void print_avatar_info(Tox *tox)
505{
506 uint8_t format;
507 uint8_t data[TOX_MAX_AVATAR_DATA_LENGTH];
508 uint8_t hash[TOX_AVATAR_HASH_LENGTH];
509 uint32_t data_length;
510 char hash_str[2 * TOX_AVATAR_HASH_LENGTH + 1];
511
512 int ret = tox_get_self_avatar(tox, &format, data, &data_length, sizeof(data), hash);
513 DEBUG("tox_get_self_avatar returned %d", ret);
514 DEBUG("format: %d, data_length: %d", format, data_length);
515 byte_to_hex_str(hash, TOX_AVATAR_HASH_LENGTH, hash_str);
516 DEBUG("hash: %s", hash_str);
517}
518
519
520/* ------------ Initialization functions ------------ */
521
522/* Create directory to store tha avatars. Returns 0 if it was sucessfuly
523 * created or already existed. Returns -1 on error.
524 */
525static int create_avatar_diretory(const char *base_dir)
526{
527 char path[PATH_MAX];
528 int n = snprintf(path, sizeof(path), "%s/%s", base_dir, AVATAR_DIR_NAME);
529 path[sizeof(path) - 1] = '\0';
530
531 if (n >= sizeof(path))
532 return -1;
533
534 if (mkdir(path, 0755) == 0) {
535 return 0; /* Done */
536 } else if (errno == EEXIST) {
537 /* Check if the existing path is a directory */
538 struct stat st;
539
540 if (stat(path, &st) != 0) {
541 perror("stat()ing avatar directory");
542 return -1;
543 }
544
545 if (S_ISDIR(st.st_mode))
546 return 0;
547 }
548
549 return -1; /* Error */
550}
551
552
553static void *load_bootstrap_data(const char *base_dir, uint32_t *len)
554{
555 char path[PATH_MAX];
556 int n = snprintf(path, sizeof(path), "%s/%s", base_dir, DATA_FILE_NAME);
557 path[sizeof(path) - 1] = '\0';
558
559 if (n >= sizeof(path)) {
560 printf("Load error: path %s too long\n", path);
561 return NULL;
562 }
563
564 /* We should be using POSIX functions here, but let's try to be
565 * compatible with Windows.
566 */
567
568 FILE *fp = fopen(path, "rb");
569
570 if (fp == NULL) {
571 printf("fatal error: file %s not found.\n", path);
572 return NULL;
573 }
574
575 if (fseek(fp, 0, SEEK_END) != 0) {
576 printf("seek fail\n");
577 fclose(fp);
578 return NULL;
579 }
580
581 int32_t flen = ftell(fp);
582
583 if (flen < 8 || flen > 2e6) {
584 printf("Fatal error: file %s have %u bytes. Out of acceptable range.\n", path, flen);
585 fclose(fp);
586 return NULL;
587 }
588
589 if (fseek(fp, 0, SEEK_SET) != 0) {
590 printf("seek fail\n");
591 fclose(fp);
592 return NULL;
593 }
594
595 void *buf = malloc(flen);
596
597 if (buf == NULL) {
598 printf("malloc failed, %u bytes", flen);
599 fclose(fp);
600 return NULL;
601 }
602
603 *len = fread(buf, 1, flen, fp);
604 fclose(fp);
605
606 if (*len != flen) {
607 printf("fatal: %s have %u bytes, read only %u\n", path, flen, *len);
608 free(buf);
609 return NULL;
610 }
611
612 printf("bootstrap data loaded from %s (%u bytes)\n", path, flen);
613 return buf;
614}
615
616static int save_bootstrap_data(Tox *tox, const char *base_dir)
617{
618 char path[PATH_MAX];
619 int n = snprintf(path, sizeof(path), "%s/%s", base_dir, DATA_FILE_NAME);
620 path[sizeof(path) - 1] = '\0';
621
622 if (n >= sizeof(path)) {
623 printf("Save error: path %s too long\n", path);
624 return -1;
625 }
626
627 char path_tmp[PATH_MAX];
628 n = snprintf(path_tmp, sizeof(path_tmp), "%s.tmp", path);
629 path_tmp[sizeof(path_tmp) - 1] = '\0';
630
631 if (n >= sizeof(path_tmp)) {
632 printf("error: path %s too long\n", path);
633 return -1;
634 }
635
636 uint32_t len = tox_size(tox);
637
638 if (len < 8 || len > 2e6) {
639 printf("save data length == %u, out of acceptable range\n", len);
640 return -1;
641 }
642
643 void *buf = malloc(len);
644
645 if (buf == NULL) {
646 printf("save data: malloc failed\n");
647 return -1;
648 }
649
650 tox_save(tox, buf);
651
652 FILE *fp = fopen(path_tmp, "wb");
653
654 if (fp == NULL) {
655 printf("Error saving data: can't open %s\n", path_tmp);
656 free(buf);
657 return -1;
658 }
659
660 if (fwrite(buf, 1, len, fp) != len) {
661 printf("Error writing data to %s\n", path_tmp);
662 free(buf);
663 fclose(fp);
664 return -1;
665 }
666
667 free(buf);
668
669 if (fclose(fp) != 0) {
670 printf("Error writing data to %s\n", path_tmp);
671 return -1;
672 }
673
674 if (rename(path_tmp, path) != 0) {
675 printf("Error renaming %s to %s\n", path_tmp, path);
676 return -1;
677 }
678
679 printf("Bootstrap data saved to %s\n", path);
680 return 0; /* Done */
681}
682
683
684
685
686int main(int argc, char *argv[])
687{
688 int ret;
689
690 if (argc != 2) {
691 printf("usage: %s <data dir>\n", argv[0]);
692 return 1;
693 }
694
695 char *base_dir = argv[1];
696
697 if (create_avatar_diretory(base_dir) != 0)
698 printf("Error creating avatar directory.\n");
699
700 Tox *tox = tox_new(NULL);
701
702 uint32_t len;
703 void *data = load_bootstrap_data(base_dir, &len);
704
705 if (data == NULL)
706 return 1;
707
708 ret = tox_load(tox, data, len);
709 free(data);
710
711 if (ret == 0) {
712 printf("Tox initialized\n");
713 } else {
714 printf("Fatal: tox_load returned %d\n", ret);
715 return 1;
716 }
717
718 tox_callback_connection_status(tox, friend_status_cb, NULL);
719 tox_callback_friend_message(tox, friend_msg_cb, base_dir);
720 tox_callback_friend_request(tox, friend_request_cb, NULL);
721 tox_callback_avatar_info(tox, friend_avatar_info_cb, base_dir);
722 tox_callback_avatar_data(tox, friend_avatar_data_cb, base_dir);
723
724 uint8_t addr[TOX_FRIEND_ADDRESS_SIZE];
725 char addr_str[2 * TOX_FRIEND_ADDRESS_SIZE + 1];
726 tox_get_address(tox, addr);
727 byte_to_hex_str(addr, TOX_FRIEND_ADDRESS_SIZE, addr_str);
728 printf("Using local tox address: %s\n", addr_str);
729
730#ifdef TEST_SET_RESET_AVATAR
731 printf("Printing default avatar information:\n");
732 print_avatar_info(tox);
733
734 printf("Setting a new avatar:\n");
735 set_avatar(tox, base_dir);
736 print_avatar_info(tox);
737
738 printf("Removing the avatar we just set:\n");
739 tox_avatar(tox, TOX_AVATARFORMAT_NONE, NULL, 0);
740 print_avatar_info(tox);
741
742 printf("Setting that avatar again:\n");
743#endif /* TEST_SET_RESET_AVATAR */
744
745 set_avatar(tox, base_dir);
746 print_avatar_info(tox);
747
748 bool waiting = true;
749 time_t last_save = time(0);
750
751 while (1) {
752 if (tox_isconnected(tox) && waiting) {
753 printf("DHT connected.\n");
754 waiting = false;
755 }
756
757 tox_do(tox);
758
759 time_t now = time(0);
760
761 if (now - last_save > 120) {
762 save_bootstrap_data(tox, base_dir);
763 last_save = now;
764 }
765
766 usleep(500000);
767 }
768
769 return 0;
770}
diff --git a/toxcore/Messenger.c b/toxcore/Messenger.c
index 21cb2671..108159a3 100644
--- a/toxcore/Messenger.c
+++ b/toxcore/Messenger.c
@@ -42,6 +42,7 @@
42static void set_friend_status(Messenger *m, int32_t friendnumber, uint8_t status); 42static void set_friend_status(Messenger *m, int32_t friendnumber, uint8_t status);
43static int write_cryptpacket_id(const Messenger *m, int32_t friendnumber, uint8_t packet_id, const uint8_t *data, 43static 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);
45static 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
47static uint8_t friend_not_valid(const Messenger *m, int32_t friendnumber) 48static 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;
@@ -330,6 +335,7 @@ int m_delfriend(Messenger *m, int32_t friendnumber)
330 onion_delfriend(m->onion_c, m->friendlist[friendnumber].onion_friendnum); 335 onion_delfriend(m->onion_c, m->friendlist[friendnumber].onion_friendnum);
331 crypto_kill(m->net_crypto, m->friendlist[friendnumber].crypt_connection_id); 336 crypto_kill(m->net_crypto, m->friendlist[friendnumber].crypt_connection_id);
332 free(m->friendlist[friendnumber].statusmessage); 337 free(m->friendlist[friendnumber].statusmessage);
338 free(m->friendlist[friendnumber].avatar_recv_data);
333 remove_request_received(&(m->fr), m->friendlist[friendnumber].client_id); 339 remove_request_received(&(m->fr), m->friendlist[friendnumber].client_id);
334 memset(&(m->friendlist[friendnumber]), 0, sizeof(Friend)); 340 memset(&(m->friendlist[friendnumber]), 0, sizeof(Friend));
335 uint32_t i; 341 uint32_t i;
@@ -564,6 +570,134 @@ int m_set_userstatus(Messenger *m, uint8_t status)
564 return 0; 570 return 0;
565} 571}
566 572
573int m_set_avatar(Messenger *m, uint8_t format, const uint8_t *data, uint32_t length)
574{
575 if (length > MAX_AVATAR_DATA_LENGTH)
576 return -1;
577
578 if (format == AVATARFORMAT_NONE) {
579 free(m->avatar_data);
580 m->avatar_data = NULL;
581 m->avatar_data_length = 0;
582 m->avatar_format = format;
583 memset(m->avatar_hash, 0, AVATAR_HASH_LENGTH);
584 } else {
585 if (length == 0 || data == NULL)
586 return -1;
587
588 uint8_t *tmp = realloc(m->avatar_data, length);
589
590 if (tmp == NULL)
591 return -1;
592
593 m->avatar_format = format;
594 m->avatar_data = tmp;
595 m->avatar_data_length = length;
596 memcpy(m->avatar_data, data, length);
597
598 m_avatar_hash(m->avatar_hash, m->avatar_data, m->avatar_data_length);
599 }
600
601 uint32_t i;
602
603 for (i = 0; i < m->numfriends; ++i)
604 m->friendlist[i].avatar_info_sent = 0;
605
606 return 0;
607}
608
609int m_get_self_avatar(const Messenger *m, uint8_t *format, uint8_t *buf, uint32_t *length, uint32_t maxlen,
610 uint8_t *hash)
611{
612 if (format)
613 *format = m->avatar_format;
614
615 if (length)
616 *length = m->avatar_data_length;
617
618 if (hash)
619 memcpy(hash, m->avatar_hash, AVATAR_HASH_LENGTH);
620
621 if (buf != NULL && maxlen > 0) {
622 if (m->avatar_data_length <= maxlen)
623 memcpy(buf, m->avatar_data, m->avatar_data_length);
624 else
625 return -1;
626 }
627
628 return 0;
629}
630
631int m_avatar_hash(uint8_t *hash, const uint8_t *data, const uint32_t datalen)
632{
633 if (hash == NULL)
634 return -1;
635
636 crypto_hash_sha256(hash, data, datalen);
637 return 0;
638}
639
640
641int m_request_avatar_info(const Messenger *m, const int32_t friendnumber)
642{
643 if (friend_not_valid(m, friendnumber))
644 return -1;
645
646 if (write_cryptpacket_id(m, friendnumber, PACKET_ID_AVATAR_INFO_REQ, 0, 0, 0) >= 0)
647 return 0;
648 else
649 return -1;
650}
651
652int m_send_avatar_info(const Messenger *m, const int32_t friendnumber)
653{
654 if (friend_not_valid(m, friendnumber))
655 return -1;
656
657 uint8_t data[sizeof(uint8_t) + AVATAR_HASH_LENGTH];
658 data[0] = m->avatar_format;
659 memcpy(data + 1, m->avatar_hash, AVATAR_HASH_LENGTH);
660
661 int ret = write_cryptpacket_id(m, friendnumber, PACKET_ID_AVATAR_INFO, data, sizeof(data), 0);
662
663 if (ret >= 0)
664 return 0;
665 else
666 return -1;
667}
668
669int m_request_avatar_data(const Messenger *m, const int32_t friendnumber)
670{
671 if (friend_not_valid(m, friendnumber))
672 return -1;
673
674 AVATARRECEIVEDATA *avrd = m->friendlist[friendnumber].avatar_recv_data;
675
676 if (avrd == NULL) {
677 avrd = malloc(sizeof(AVATARRECEIVEDATA));
678
679 if (avrd == NULL)
680 return -1;
681
682 memset(avrd, 0, sizeof(AVATARRECEIVEDATA));
683 avrd->started = 0;
684 m->friendlist[friendnumber].avatar_recv_data = avrd;
685 }
686
687 if (avrd->started) {
688 LOGGER_DEBUG("Resetting already started data request. "
689 "friendnumber == %u", friendnumber);
690 }
691
692 avrd->started = 0;
693 avrd->bytes_received = 0;
694 avrd->total_length = 0;
695 avrd->format = AVATARFORMAT_NONE;
696
697 return send_avatar_data_control(m, friendnumber, AVATARDATACONTROL_REQ);
698}
699
700
567/* return the size of friendnumber's user status. 701/* return the size of friendnumber's user status.
568 * Guaranteed to be at most MAX_STATUSMESSAGE_LENGTH. 702 * Guaranteed to be at most MAX_STATUSMESSAGE_LENGTH.
569 */ 703 */
@@ -806,6 +940,20 @@ void m_callback_connectionstatus_internal_av(Messenger *m, void (*function)(Mess
806 m->friend_connectionstatuschange_internal_userdata = userdata; 940 m->friend_connectionstatuschange_internal_userdata = userdata;
807} 941}
808 942
943void m_callback_avatar_info(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, uint8_t *, void *),
944 void *userdata)
945{
946 m->avatar_info_recv = function;
947 m->avatar_info_recv_userdata = userdata;
948}
949
950void m_callback_avatar_data(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, uint8_t *, uint8_t *,
951 uint32_t, void *), void *userdata)
952{
953 m->avatar_data_recv = function;
954 m->avatar_data_recv_userdata = userdata;
955}
956
809static void break_files(const Messenger *m, int32_t friendnumber); 957static void break_files(const Messenger *m, int32_t friendnumber);
810static void check_friend_connectionstatus(Messenger *m, int32_t friendnumber, uint8_t status) 958static void check_friend_connectionstatus(Messenger *m, int32_t friendnumber, uint8_t status)
811{ 959{
@@ -1857,6 +2005,9 @@ Messenger *new_messenger(Messenger_Options *options)
1857 m->net = new_networking(ip, TOX_PORT_DEFAULT); 2005 m->net = new_networking(ip, TOX_PORT_DEFAULT);
1858 } 2006 }
1859 2007
2008 m->avatar_format = AVATARFORMAT_NONE;
2009 m->avatar_data = NULL;
2010
1860 if (m->net == NULL) { 2011 if (m->net == NULL) {
1861 free(m); 2012 free(m);
1862 return NULL; 2013 return NULL;
@@ -1934,6 +2085,7 @@ void kill_messenger(Messenger *m)
1934 free(m->friendlist[i].statusmessage); 2085 free(m->friendlist[i].statusmessage);
1935 } 2086 }
1936 2087
2088 free(m->avatar_data);
1937 free(m->friendlist); 2089 free(m->friendlist);
1938 free(m); 2090 free(m);
1939} 2091}
@@ -1973,11 +2125,287 @@ static int handle_status(void *object, int i, uint8_t status)
1973 if (m->friendlist[i].status == FRIEND_ONLINE) { 2125 if (m->friendlist[i].status == FRIEND_ONLINE) {
1974 set_friend_status(m, i, FRIEND_CONFIRMED); 2126 set_friend_status(m, i, FRIEND_CONFIRMED);
1975 } 2127 }
2128
2129 /* Clear avatar transfer state */
2130 if (m->friendlist[i].avatar_recv_data) {
2131 free(m->friendlist[i].avatar_recv_data);
2132 m->friendlist[i].avatar_recv_data = NULL;
2133 }
2134 }
2135
2136 return 0;
2137}
2138
2139
2140/* Sends an avatar data control packet to the peer. Usually to return status
2141 * values or request data.
2142 */
2143static int send_avatar_data_control(const Messenger *m, const uint32_t friendnumber,
2144 uint8_t op)
2145{
2146 int ret = write_cryptpacket_id(m, friendnumber, PACKET_ID_AVATAR_DATA_CONTROL,
2147 &op, sizeof(op), 0);
2148 LOGGER_DEBUG("friendnumber = %u, op = %u, ret = %d",
2149 friendnumber, op, ret);
2150 return (ret >= 0) ? 0 : -1;
2151}
2152
2153
2154static int handle_avatar_data_control(Messenger *m, uint32_t friendnumber,
2155 uint8_t *data, uint32_t data_length)
2156{
2157 if (data_length != 1) {
2158 LOGGER_DEBUG("Error: PACKET_ID_AVATAR_DATA_CONTROL with bad "
2159 "data_length = %u, friendnumber = %u",
2160 data_length, friendnumber);
2161 send_avatar_data_control(m, friendnumber, AVATARDATACONTROL_ERROR);
2162 return -1; /* Error */
2163 }
2164
2165 LOGGER_DEBUG("friendnumber = %u, op = %u", friendnumber, data[0]);
2166
2167 switch (data[0]) {
2168 case AVATARDATACONTROL_REQ: {
2169
2170 /* Check data transfer limits for this friend */
2171 AVATARSENDDATA *const avsd = &(m->friendlist[friendnumber].avatar_send_data);
2172
2173 if (avsd->bytes_sent >= AVATAR_DATA_TRANSFER_LIMIT) {
2174 /* User reached data limit. Check timeout */
2175 uint64_t now = unix_time();
2176
2177 if (avsd->last_reset > 0
2178 && (avsd->last_reset + AVATAR_DATA_TRANSFER_TIMEOUT < now)) {
2179 avsd->bytes_sent = 0;
2180 avsd->last_reset = now;
2181 } else {
2182 /* Friend still rate-limitted. Send an error and stops. */
2183 LOGGER_DEBUG("Avatar data transfer limit reached. "
2184 "friendnumber = %u", friendnumber);
2185 send_avatar_data_control(m, friendnumber, AVATARDATACONTROL_ERROR);
2186 return 0;
2187 }
2188 }
2189
2190 /* Start the transmission with a DATA_START message. Format:
2191 * uint8_t format
2192 * uint8_t hash[AVATAR_HASH_LENGTH]
2193 * uint32_t total_length
2194 */
2195 LOGGER_DEBUG("Sending start msg to friend number %u. "
2196 "m->avatar_format = %u, m->avatar_data_length = %u",
2197 friendnumber, m->avatar_format, m->avatar_data_length);
2198 uint8_t start_data[1 + AVATAR_HASH_LENGTH + sizeof(uint32_t)];
2199 uint32_t avatar_len = htonl(m->avatar_data_length);
2200
2201 start_data[0] = m->avatar_format;
2202 memcpy(start_data + 1, m->avatar_hash, AVATAR_HASH_LENGTH);
2203 memcpy(start_data + 1 + AVATAR_HASH_LENGTH, &avatar_len, sizeof(uint32_t));
2204
2205 avsd->bytes_sent += sizeof(start_data); /* For rate limit */
2206
2207 int ret = write_cryptpacket_id(m, friendnumber, PACKET_ID_AVATAR_DATA_START,
2208 start_data, sizeof(start_data), 0);
2209
2210 if (ret < 0) {
2211 /* Something went wrong, try to signal the error so the friend
2212 * can clear up the state. */
2213 send_avatar_data_control(m, friendnumber, AVATARDATACONTROL_ERROR);
2214 return 0;
2215 }
2216
2217 /* User have no avatar data, nothing more to do. */
2218 if (m->avatar_format == AVATARFORMAT_NONE)
2219 return 0;
2220
2221 /* Send the actual avatar data. */
2222 uint32_t offset = 0;
2223
2224 while (offset < m->avatar_data_length) {
2225 uint32_t chunk_len = m->avatar_data_length - offset;
2226
2227 if (chunk_len > AVATAR_DATA_MAX_CHUNK_SIZE)
2228 chunk_len = AVATAR_DATA_MAX_CHUNK_SIZE;
2229
2230 uint8_t chunk[AVATAR_DATA_MAX_CHUNK_SIZE];
2231 memcpy(chunk, m->avatar_data + offset, chunk_len);
2232 offset += chunk_len;
2233 avsd->bytes_sent += chunk_len; /* For rate limit */
2234
2235 int ret = write_cryptpacket_id(m, friendnumber,
2236 PACKET_ID_AVATAR_DATA_PUSH,
2237 chunk, chunk_len, 0);
2238
2239 if (ret < 0) {
2240 LOGGER_DEBUG("write_cryptpacket_id failed. ret = %d, "
2241 "friendnumber = %u, offset = %u",
2242 ret, friendnumber, offset);
2243 send_avatar_data_control(m, friendnumber, AVATARDATACONTROL_ERROR);
2244 return -1;
2245 }
2246 }
2247
2248 return 0;
2249 }
2250
2251 case AVATARDATACONTROL_ERROR: {
2252 if (m->friendlist[friendnumber].avatar_recv_data) {
2253 /* We were receiving the data, sender detected an error
2254 (eg. changing avatar) and asked us to stop. */
2255 free(m->friendlist[friendnumber].avatar_recv_data);
2256 m->friendlist[friendnumber].avatar_recv_data = NULL;
2257 }
2258
2259 return 0;
2260 }
2261 }
2262
2263 return -1;
2264}
2265
2266
2267static int handle_avatar_data_start(Messenger *m, uint32_t friendnumber,
2268 uint8_t *data, uint32_t data_length)
2269{
2270 LOGGER_DEBUG("data_length = %u, friendnumber = %u", data_length, friendnumber);
2271
2272 if (data_length != 1 + AVATAR_HASH_LENGTH + sizeof(uint32_t)) {
2273 LOGGER_DEBUG("Invalid msg length = %u, friendnumber = %u",
2274 data_length, friendnumber);
2275 return -1;
2276 }
2277
2278 AVATARRECEIVEDATA *avrd = m->friendlist[friendnumber].avatar_recv_data;
2279
2280 if (avrd == NULL) {
2281 LOGGER_DEBUG("Received an unrequested DATA_START, friendnumber = %u",
2282 friendnumber);
2283 return -1;
2284 }
2285
2286 if (avrd->started) {
2287 /* Already receiving data from this friend. Must be an error
2288 * or an malicious request, because we zeroed the started bit
2289 * when we requested the data. */
2290 LOGGER_DEBUG("Received an unrequested duplicated DATA_START, "
2291 "friendnumber = %u", friendnumber);
2292 return -1;
2293 }
2294
2295 /* Copy data from message to our control structure */
2296 avrd->started = 1;
2297 avrd->format = data[0];
2298 memcpy(avrd->hash, data + 1, AVATAR_HASH_LENGTH);
2299 uint32_t tmp_len;
2300 memcpy(&tmp_len, data + 1 + AVATAR_HASH_LENGTH, sizeof(uint32_t));
2301 avrd->total_length = ntohl(tmp_len);
2302 avrd->bytes_received = 0;
2303
2304 LOGGER_DEBUG("friendnumber = %u, avrd->format = %u, "
2305 "avrd->total_length = %u, avrd->bytes_received = %u",
2306 friendnumber, avrd->format, avrd->total_length,
2307 avrd->bytes_received);
2308
2309 if (avrd->total_length > MAX_AVATAR_DATA_LENGTH) {
2310 /* Invalid data length. Stops. */
2311 LOGGER_DEBUG("Error: total_length > MAX_AVATAR_DATA_LENGTH, "
2312 "friendnumber = %u", friendnumber);
2313 free(avrd);
2314 avrd = NULL;
2315 m->friendlist[friendnumber].avatar_recv_data = NULL;
2316 return 0;
2317 }
2318
2319 if (avrd->format == AVATARFORMAT_NONE || avrd->total_length == 0) {
2320 /* No real data to receive. Run callback function and finish. */
2321 LOGGER_DEBUG("format == NONE, friendnumber = %u", friendnumber);
2322
2323 if (m->avatar_data_recv) {
2324 memset(avrd->hash, 0, AVATAR_HASH_LENGTH);
2325 (m->avatar_data_recv)(m, friendnumber, avrd->format, avrd->hash,
2326 NULL, 0, m->avatar_data_recv_userdata);
2327 }
2328
2329 free(avrd);
2330 avrd = NULL;
2331 m->friendlist[friendnumber].avatar_recv_data = NULL;
2332 return 0;
2333 }
2334
2335 /* Waits for more data to be received */
2336 return 0;
2337}
2338
2339
2340static int handle_avatar_data_push(Messenger *m, uint32_t friendnumber,
2341 uint8_t *data, uint32_t data_length)
2342{
2343 LOGGER_DEBUG("friendnumber = %u, data_length = %u", friendnumber, data_length);
2344
2345 AVATARRECEIVEDATA *avrd = m->friendlist[friendnumber].avatar_recv_data;
2346
2347 if (avrd == NULL) {
2348 /* No active transfer. It must be an error or a malicious request,
2349 * because we set the avatar_recv_data on the first DATA_START. */
2350 LOGGER_DEBUG("Error: avrd == NULL, friendnumber = %u", friendnumber);
2351 return -1; /* Error */
2352 }
2353
2354 if (avrd->started == 0) {
2355 /* Receiving data for a non-started request. Must be an error
2356 * or an malicious request. */
2357 LOGGER_DEBUG("Received an data push for a yet non started data "
2358 "request. friendnumber = %u", friendnumber);
2359 return -1; /* Error */
2360 }
2361
2362 uint32_t new_length = avrd->bytes_received + data_length;
2363
2364 if (new_length > avrd->total_length
2365 || new_length >= MAX_AVATAR_DATA_LENGTH) {
2366 /* Invalid data length due to error or malice. Stops. */
2367 LOGGER_DEBUG("Invalid data length. friendnumber = %u, "
2368 "new_length = %u, avrd->total_length = %u",
2369 friendnumber, new_length, avrd->total_length);
2370 free(avrd);
2371 m->friendlist[friendnumber].avatar_recv_data = NULL;
2372 return 0;
2373 }
2374
2375 memcpy(avrd->data + avrd->bytes_received, data, data_length);
2376 avrd->bytes_received += data_length;
2377
2378 if (avrd->bytes_received == avrd->total_length) {
2379 LOGGER_DEBUG("All data received. friendnumber = %u", friendnumber);
2380
2381 /* All data was received. Check if the hashes match. It the
2382 * requester's responsability to do this. The sender may have done
2383 * anything with its avatar data between the DATA_START and now.
2384 */
2385 uint8_t cur_hash[AVATAR_HASH_LENGTH];
2386 m_avatar_hash(cur_hash, avrd->data, avrd->bytes_received);
2387
2388 if (memcmp(cur_hash, avrd->hash, AVATAR_HASH_LENGTH) == 0) {
2389 /* Avatar successfuly received! */
2390 if (m->avatar_data_recv) {
2391 (m->avatar_data_recv)(m, friendnumber, avrd->format, cur_hash,
2392 avrd->data, avrd->bytes_received, m->avatar_data_recv_userdata);
2393 }
2394 } else {
2395 LOGGER_DEBUG("Avatar hash error. friendnumber = %u", friendnumber);
2396 }
2397
2398 free(avrd);
2399 m->friendlist[friendnumber].avatar_recv_data = NULL;
2400 return 0;
1976 } 2401 }
1977 2402
2403 /* Waits for more data to be received */
1978 return 0; 2404 return 0;
1979} 2405}
1980 2406
2407
2408
1981static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len) 2409static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len)
1982{ 2410{
1983 if (len == 0) 2411 if (len == 0)
@@ -2115,6 +2543,42 @@ static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len)
2115 break; 2543 break;
2116 } 2544 }
2117 2545
2546 case PACKET_ID_AVATAR_INFO_REQ: {
2547 /* Send our avatar information */
2548 m_send_avatar_info(m, i);
2549 break;
2550 }
2551
2552 case PACKET_ID_AVATAR_INFO: {
2553 if (m->avatar_info_recv) {
2554 /*
2555 * A malicious user may send an incomplete avatar info message.
2556 * Check if it have the correct size for the format:
2557 * [1 uint8_t: avatar format] [32 uint8_t: hash]
2558 */
2559 if (data_length == AVATAR_HASH_LENGTH + 1) {
2560 (m->avatar_info_recv)(m, i, data[0], data + 1, m->avatar_info_recv_userdata);
2561 }
2562 }
2563
2564 break;
2565 }
2566
2567 case PACKET_ID_AVATAR_DATA_CONTROL: {
2568 handle_avatar_data_control(m, i, data, data_length);
2569 break;
2570 }
2571
2572 case PACKET_ID_AVATAR_DATA_START: {
2573 handle_avatar_data_start(m, i, data, data_length);
2574 break;
2575 }
2576
2577 case PACKET_ID_AVATAR_DATA_PUSH: {
2578 handle_avatar_data_push(m, i, data, data_length);
2579 break;
2580 }
2581
2118 case PACKET_ID_RECEIPT: { 2582 case PACKET_ID_RECEIPT: {
2119 uint32_t msgid; 2583 uint32_t msgid;
2120 2584
@@ -2343,6 +2807,11 @@ void do_friends(Messenger *m)
2343 m->friendlist[i].userstatus_sent = 1; 2807 m->friendlist[i].userstatus_sent = 1;
2344 } 2808 }
2345 2809
2810 if (m->friendlist[i].avatar_info_sent == 0) {
2811 if (m_send_avatar_info(m, i) == 0)
2812 m->friendlist[i].avatar_info_sent = 1;
2813 }
2814
2346 if (m->friendlist[i].user_istyping_sent == 0) { 2815 if (m->friendlist[i].user_istyping_sent == 0) {
2347 if (send_user_istyping(m, i, m->friendlist[i].user_istyping)) 2816 if (send_user_istyping(m, i, m->friendlist[i].user_istyping))
2348 m->friendlist[i].user_istyping_sent = 1; 2817 m->friendlist[i].user_istyping_sent = 1;
diff --git a/toxcore/Messenger.h b/toxcore/Messenger.h
index 6c641a9a..01632126 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 (20*60)
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}
123USERSTATUS; 138USERSTATUS;
124 139
140/* AVATARFORMAT -
141 * Data formats for user avatar images
142 */
143typedef enum {
144 AVATARFORMAT_NONE,
145 AVATARFORMAT_PNG
146}
147AVATARFORMAT;
148
149/* AVATARDATACONTROL
150 * To control avatar data requests (PACKET_ID_AVATAR_DATA_CONTROL)
151 */
152typedef enum {
153 AVATARDATACONTROL_REQ,
154 AVATARDATACONTROL_ERROR
155}
156AVATARDATACONTROL;
157
158typedef 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}
166AVATARRECEIVEDATA;
167
168typedef 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}
173AVATARSENDDATA;
174
175
125struct File_Transfers { 176struct 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)
437uint8_t m_get_userstatus(const Messenger *m, int32_t friendnumber); 501uint8_t m_get_userstatus(const Messenger *m, int32_t friendnumber);
438uint8_t m_get_self_userstatus(const Messenger *m); 502uint8_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 */
519int 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 */
541int 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 */
557int 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 */
567int 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 */
579int 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 */
589int 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
533void m_callback_connectionstatus_internal_av(Messenger *m, void (*function)(Messenger *m, int32_t, uint8_t, void *), 685void 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 */
702void 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 */
726void 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..c5bea846 100644
--- a/toxcore/tox.c
+++ b/toxcore/tox.c
@@ -275,6 +275,42 @@ uint8_t tox_get_self_user_status(const Tox *tox)
275 return m_get_self_userstatus(m); 275 return m_get_self_userstatus(m);
276} 276}
277 277
278int tox_set_avatar(Tox *tox, uint8_t format, const uint8_t *data, uint32_t length)
279{
280 Messenger *m = tox;
281 return m_set_avatar(m, format, data, length);
282}
283
284int tox_get_self_avatar(const Tox *tox, uint8_t *format, uint8_t *buf, uint32_t *length, uint32_t maxlen, uint8_t *hash)
285{
286 const Messenger *m = tox;
287 return m_get_self_avatar(m, format, buf, length, maxlen, hash);
288}
289
290int tox_avatar_hash(const Tox *tox, uint8_t *hash, const uint8_t *data, const uint32_t datalen)
291{
292 return m_avatar_hash(hash, data, datalen);
293}
294
295int tox_request_avatar_info(const Tox *tox, const int32_t friendnumber)
296{
297 const Messenger *m = tox;
298 return m_request_avatar_info(m, friendnumber);
299}
300
301int tox_send_avatar_info(Tox *tox, const int32_t friendnumber)
302{
303 const Messenger *m = tox;
304 return m_send_avatar_info(m, friendnumber);
305}
306
307int tox_request_avatar_data(const Tox *tox, const int32_t friendnumber)
308{
309 const Messenger *m = tox;
310 return m_request_avatar_data(m, friendnumber);
311}
312
313
278/* returns timestamp of last time friendnumber was seen online, or 0 if never seen. 314/* returns timestamp of last time friendnumber was seen online, or 0 if never seen.
279 * returns -1 on error. 315 * returns -1 on error.
280 */ 316 */
@@ -439,6 +475,24 @@ void tox_callback_connection_status(Tox *tox, void (*function)(Messenger *tox, i
439 m_callback_connectionstatus(m, function, userdata); 475 m_callback_connectionstatus(m, function, userdata);
440} 476}
441 477
478void tox_callback_avatar_info(Tox *tox, void (*function)(Tox *tox, int32_t, uint8_t, uint8_t *, void *), void *userdata)
479{
480 Messenger *m = tox;
481 m_callback_avatar_info(m, function, userdata);
482}
483
484
485void tox_callback_avatar_data(Tox *tox, void (*function)(Tox *tox, int32_t, uint8_t, uint8_t *, uint8_t *, uint32_t,
486 void *), void *userdata)
487{
488 Messenger *m = tox;
489 m_callback_avatar_data(m, function, userdata);
490}
491
492
493
494
495
442/**********ADVANCED FUNCTIONS (If you don't know what they do you can safely ignore them.) ************/ 496/**********ADVANCED FUNCTIONS (If you don't know what they do you can safely ignore them.) ************/
443 497
444/* Functions to get/set the nospam part of the id. 498/* Functions to get/set the nospam part of the id.
diff --git a/toxcore/tox.h b/toxcore/tox.h
index 278a19cd..8f54697f 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}
71TOX_USERSTATUS; 73TOX_USERSTATUS;
72 74
75
76/* AVATARFORMAT -
77 * Data formats for user avatar images
78 */
79typedef enum {
80 TOX_AVATARFORMAT_NONE,
81 TOX_AVATARFORMAT_PNG
82}
83TOX_AVATARFORMAT;
84
73#ifndef __TOX_DEFINED__ 85#ifndef __TOX_DEFINED__
74#define __TOX_DEFINED__ 86#define __TOX_DEFINED__
75typedef struct Tox Tox; 87typedef struct Tox Tox;
@@ -243,6 +255,97 @@ uint8_t tox_get_user_status(const Tox *tox, int32_t friendnumber);
243uint8_t tox_get_self_user_status(const Tox *tox); 255uint8_t tox_get_self_user_status(const Tox *tox);
244 256
245 257
258/* Set the user avatar image data.
259 * This should be made before connecting, so we will not announce that the user have no avatar
260 * before setting and announcing a new one, forcing the peers to re-download it.
261 *
262 * Notice that the library treats the image as raw data and does not interpret it by any way.
263 *
264 * Arguments:
265 * format - Avatar image format or NONE for user with no avatar (see TOX_AVATARFORMAT);
266 * data - pointer to the avatar data (may be NULL it the format is NONE);
267 * length - length of image data. Must be <= TOX_MAX_AVATAR_DATA_LENGTH.
268 *
269 * returns 0 on success
270 * returns -1 on failure.
271 */
272int tox_set_avatar(Tox *tox, uint8_t format, const uint8_t *data, uint32_t length);
273
274
275/* Get avatar data from the current user.
276 * Copies the current user avatar data to the destination buffer and sets the image format
277 * accordingly.
278 *
279 * If the avatar format is NONE, the buffer 'buf' isleft uninitialized, 'hash' is zeroed, and
280 * 'length' is set to zero.
281 *
282 * If any of the pointers format, buf, length, and hash are NULL, that particular field will be ignored.
283 *
284 * Arguments:
285 * format - destination pointer to the avatar image format (see TOX_AVATARFORMAT);
286 * buf - destination buffer to the image data. Must have at least 'maxlen' bytes;
287 * length - destination pointer to the image data length;
288 * maxlen - length of the destination buffer 'buf';
289 * hash - destination pointer to the avatar hash (it must be exactly TOX_AVATAR_HASH_LENGTH bytes long).
290 *
291 * returns 0 on success;
292 * returns -1 on failure.
293 *
294 */
295int tox_get_self_avatar(const Tox *tox, uint8_t *format, uint8_t *buf, uint32_t *length, uint32_t maxlen,
296 uint8_t *hash);
297
298
299/* Generates a cryptographic hash of the given avatar data.
300 * This function is a wrapper to internal message-digest functions and specifically provided
301 * to generate hashes from user avatars that may be memcmp()ed with the values returned by the
302 * other avatar functions. It is specially important to validate cached avatars.
303 *
304 * Arguments:
305 * hash - destination buffer for the hash data, it must be exactly TOX_AVATAR_HASH_LENGTH bytes long.
306 * data - avatar image data;
307 * datalen - length of the avatar image data; it must be <= TOX_MAX_AVATAR_DATA_LENGTH.
308 *
309 * returns 0 on success
310 * returns -1 on failure.
311 */
312int tox_avatar_hash(const Tox *tox, uint8_t *hash, const uint8_t *data, const uint32_t datalen);
313
314
315/* Request avatar information from a friend.
316 * Asks a friend to provide their avatar information (image format and hash). The friend may
317 * or may not answer this request and, if answered, the information will be provided through
318 * the callback 'avatar_info'.
319 *
320 * returns 0 on success
321 * returns -1 on failure.
322 */
323int tox_request_avatar_info(const Tox *tox, const int32_t friendnumber);
324
325
326/* Send an unrequested avatar information to a friend.
327 * Sends our avatar format and hash to a friend; he/she can use this information to validate
328 * an avatar from the cache and may (or not) reply with an avatar data request.
329 *
330 * Notice: it is NOT necessary to send these notification after changing the avatar or
331 * connecting. The library already does this.
332 *
333 * returns 0 on success
334 * returns -1 on failure.
335 */
336int tox_send_avatar_info(Tox *tox, const int32_t friendnumber);
337
338
339/* Request the avatar data from a friend.
340 * Ask a friend to send their avatar data. The friend may or may not answer this request and,
341 * if answered, the information will be provided in callback 'avatar_data'.
342 *
343 * returns 0 on sucess
344 * returns -1 on failure.
345 */
346int tox_request_avatar_data(const Tox *tox, const int32_t friendnumber);
347
348
246/* returns timestamp of last time friendnumber was seen online, or 0 if never seen. 349/* returns timestamp of last time friendnumber was seen online, or 0 if never seen.
247 * returns -1 on error. 350 * returns -1 on error.
248 */ 351 */
@@ -344,6 +447,48 @@ void tox_callback_read_receipt(Tox *tox, void (*function)(Tox *tox, int32_t, uin
344 */ 447 */
345void tox_callback_connection_status(Tox *tox, void (*function)(Tox *tox, int32_t, uint8_t, void *), void *userdata); 448void tox_callback_connection_status(Tox *tox, void (*function)(Tox *tox, int32_t, uint8_t, void *), void *userdata);
346 449
450/* Set the callback function for avatar information.
451 * This callback will be called when avatar information are received from friends. These events
452 * can arrive at anytime, but are usually received uppon connection and in reply of avatar
453 * information requests.
454 *
455 * Function format is:
456 * function(Tox *tox, int32_t friendnumber, uint8_t format, uint8_t *hash, void *userdata)
457 *
458 * where 'format' is the avatar image format (see TOX_AVATARFORMAT) and 'hash' is the hash of
459 * the avatar data for caching purposes and it is exactly TOX_AVATAR_HASH_LENGTH long. If the
460 * image format is NONE, the hash is zeroed.
461 *
462 */
463void tox_callback_avatar_info(Tox *tox, void (*function)(Tox *tox, int32_t, uint8_t, uint8_t *, void *),
464 void *userdata);
465
466
467/* Set the callback function for avatar data.
468 * This callback will be called when the complete avatar data was correctly received from a
469 * friend. This only happens in reply of a avatar data request (see tox_request_avatar_data);
470 *
471 * Function format is:
472 * function(Tox *tox, int32_t friendnumber, uint8_t format, uint8_t *hash, uint8_t *data, uint32_t datalen, void *userdata)
473 *
474 * where 'format' is the avatar image format (see TOX_AVATARFORMAT); 'hash' is the
475 * locally-calculated cryptographic hash of the avatar data and it is exactly
476 * TOX_AVATAR_HASH_LENGTH long; 'data' is the avatar image data and 'datalen' is the length
477 * of such data.
478 *
479 * If format is NONE, 'data' is NULL, 'datalen' is zero, and the hash is zeroed. The hash is
480 * always validated locally with the function tox_avatar_hash and ensured to match the image
481 * data, so this value can be safely used to compare with cached avatars.
482 *
483 * WARNING: users MUST treat all avatar image data received from another peer as untrusted and
484 * potentially malicious. The library only ensures that the data which arrived is the same the
485 * other user sent, and does not interpret or validate any image data.
486 */
487void tox_callback_avatar_data(Tox *tox, void (*function)(Tox *tox, int32_t, uint8_t, uint8_t *, uint8_t *, uint32_t,
488 void *), void *userdata);
489
490
491
347 492
348/**********ADVANCED FUNCTIONS (If you don't know what they do you can safely ignore them.) ************/ 493/**********ADVANCED FUNCTIONS (If you don't know what they do you can safely ignore them.) ************/
349 494