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.c474
-rw-r--r--toxcore/Messenger.h195
-rw-r--r--toxcore/tox.c51
-rw-r--r--toxcore/tox.h144
7 files changed, 2257 insertions, 3 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..3d96f6ca 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;
@@ -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
577int 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
613int 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
635int 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
645int 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
656int 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
671int 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
944void 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
951void 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
809static void break_files(const Messenger *m, int32_t friendnumber); 958static void break_files(const Messenger *m, int32_t friendnumber);
810static void check_friend_connectionstatus(Messenger *m, int32_t friendnumber, uint8_t status) 959static 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 */
2144static 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
2155static 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
2268static 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
2341static 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
1981static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len) 2410static 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}
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..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
804void 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
810void 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
817int 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
823int 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
829int 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
834int 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
840int 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
846int 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}
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;
@@ -242,7 +254,6 @@ int tox_get_self_status_message(const Tox *tox, uint8_t *buf, uint32_t maxlen);
242uint8_t tox_get_user_status(const Tox *tox, int32_t friendnumber); 254uint8_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
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. */
518uint32_t tox_get_chatlist(const Tox *tox, int *out_list, uint32_t list_size); 529uint32_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 */
546void 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 */
570void 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 */
587int 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 */
610int 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 */
627int 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 */
638int 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 */
651int 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 */
661int 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.