diff options
-rw-r--r-- | docs/Avatars.md | 40 | ||||
-rw-r--r-- | testing/test_avatars.c | 50 |
2 files changed, 52 insertions, 38 deletions
diff --git a/docs/Avatars.md b/docs/Avatars.md index 1dcbba96..31138af7 100644 --- a/docs/Avatars.md +++ b/docs/Avatars.md | |||
@@ -11,8 +11,8 @@ way for one user to identify another in the friend list. | |||
11 | This document describes the implementation of avatars in the Tox protocol, | 11 | This document describes the implementation of avatars in the Tox protocol, |
12 | according to the following design considerations: | 12 | according to the following design considerations: |
13 | 13 | ||
14 | - Avatars are handled as private information, i.e., they are only exchanged over | 14 | - Avatars are handled as private information, i.e., they are only exchanged |
15 | Tox encrypted channels among previously authenticated friends; | 15 | over Tox encrypted channels among previously authenticated friends; |
16 | 16 | ||
17 | - The library treats all images as blobs and does not interpret or | 17 | - The library treats all images as blobs and does not interpret or |
18 | understand image formats. It only ensures that the avatar data sent by | 18 | understand image formats. It only ensures that the avatar data sent by |
@@ -85,14 +85,15 @@ Tox protocol: | |||
85 | connects to the network, changes his avatar, or in reply to an **avatar | 85 | connects to the network, changes his avatar, or in reply to an **avatar |
86 | information request**. They are delivered by a very lightweight message | 86 | information request**. They are delivered by a very lightweight message |
87 | but with information enough to allow a user to validate or discard an | 87 | but with information enough to allow a user to validate or discard an |
88 | avatar from the local cache and to decide if it is interesting to request the | 88 | avatar from the local cache and to decide if it is interesting to request |
89 | avatar data from the peer. | 89 | the avatar data from the peer. |
90 | 90 | ||
91 | This event contains two data fields: (1) the image format, and (2) the | 91 | This event contains two data fields: (1) the image format, and (2) the |
92 | cryptographic hash of the actual image data. The image format may be NONE | 92 | cryptographic hash of the current image data. The image format may be |
93 | (for users who have no avatar or removed their avatars) or PNG. The | 93 | NONE (for users who have no avatar or removed their avatars) or PNG. The |
94 | cryptographic hash is intended to be compared with the hash of the | 94 | cryptographic hash is intended to be compared with the hash of the |
95 | currently cached avatar (if any) in order to check if it is still up to date. | 95 | currently cached avatar (if any) in order to check if it is still up to |
96 | date. | ||
96 | 97 | ||
97 | - **Avatar Information Requests** are very lightweight messages sent by a | 98 | - **Avatar Information Requests** are very lightweight messages sent by a |
98 | user asking for an **avatar information notification**. They may be sent | 99 | user asking for an **avatar information notification**. They may be sent |
@@ -145,6 +146,9 @@ TOX_AVATAR_FORMAT; | |||
145 | /* Set the user avatar image data. */ | 146 | /* Set the user avatar image data. */ |
146 | int tox_set_avatar(Tox *tox, uint8_t format, const uint8_t *data, uint32_t length); | 147 | int tox_set_avatar(Tox *tox, uint8_t format, const uint8_t *data, uint32_t length); |
147 | 148 | ||
149 | /* Removes the user avatar image data. */ | ||
150 | int tox_unset_avatar(Tox *tox); | ||
151 | |||
148 | /* Get avatar data from the current user. */ | 152 | /* Get avatar data from the current user. */ |
149 | int tox_get_self_avatar(const Tox *tox, uint8_t *format, uint8_t *buf, uint32_t *length, uint32_t maxlen, uint8_t *hash); | 153 | int tox_get_self_avatar(const Tox *tox, uint8_t *format, uint8_t *buf, uint32_t *length, uint32_t maxlen, uint8_t *hash); |
150 | 154 | ||
@@ -201,6 +205,11 @@ void tox_callback_avatar_data(Tox *tox, void (*function)(Tox *tox, int32_t, uint | |||
201 | * A client may not understand a particular image format and ignore | 205 | * A client may not understand a particular image format and ignore |
202 | avatars using it, but request and handle other formats; | 206 | avatars using it, but request and handle other formats; |
203 | 207 | ||
208 | * A client on a slow mobile network may ask for avatar information to | ||
209 | ensure its cached avatars are still valid, but do not request avatar | ||
210 | data. The same client may start asking for avatar data once it | ||
211 | connects through a fast network. | ||
212 | |||
204 | - Clients SHOULD implement a local cache of avatars and do not request | 213 | - Clients SHOULD implement a local cache of avatars and do not request |
205 | avatar data from other peers unless necessary; | 214 | avatar data from other peers unless necessary; |
206 | 215 | ||
@@ -244,7 +253,7 @@ already downloaded by other clients can be reused. | |||
244 | Given the Tox data directory described in STS Draft v0.1.0: | 253 | Given the Tox data directory described in STS Draft v0.1.0: |
245 | 254 | ||
246 | - Avatars are stored in a directory called "avatars" and named | 255 | - Avatars are stored in a directory called "avatars" and named |
247 | as "xxxxx.png", where "xxxxx" is the complete client id (but not friend | 256 | as "xxxxx.png", where "xxxxx" is the complete public key (but not friend |
248 | address!) encoded as an uppercase hexadecimal string and "png" is the | 257 | address!) encoded as an uppercase hexadecimal string and "png" is the |
249 | extension for the PNG avatar. As new image formats may be used in the | 258 | extension for the PNG avatar. As new image formats may be used in the |
250 | future, clients should ensure no other file "xxxxx.*" exists. No file | 259 | future, clients should ensure no other file "xxxxx.*" exists. No file |
@@ -253,7 +262,7 @@ Given the Tox data directory described in STS Draft v0.1.0: | |||
253 | - The client's own avatar is not special and is stored like any other. This | 262 | - The client's own avatar is not special and is stored like any other. This |
254 | is partially for simplicity, and partially in anticipation of profiles. | 263 | is partially for simplicity, and partially in anticipation of profiles. |
255 | 264 | ||
256 | - The avatar should be stored as its recieved, before any modifications by | 265 | - The avatar should be stored as its received, before any modifications by |
257 | the client for display purposes. | 266 | the client for display purposes. |
258 | 267 | ||
259 | - The hash, as calculated by toxcore and passed in to the data callback, | 268 | - The hash, as calculated by toxcore and passed in to the data callback, |
@@ -269,7 +278,7 @@ Example for Linux and other Unix systems, assuming an user called "gildor": | |||
269 | Tox data directory: /home/gildor/.config/tox/ | 278 | Tox data directory: /home/gildor/.config/tox/ |
270 | Tox data file: /home/gildor/.config/tox/data | 279 | Tox data file: /home/gildor/.config/tox/data |
271 | Avatar data dir: /home/gildor/.config/tox/avatars/ | 280 | Avatar data dir: /home/gildor/.config/tox/avatars/ |
272 | Gildor's avatar: /home/gildor/.config/tox/avatars/E5809EEF5F11AB29B9BDF543C05B58DDF454AB9CA176C235C7699FDC2757DC33.png | 281 | Gildor's avatar: /home/gildor/.config/tox/avatars/446F4E6F744D6564646C65496E546865416666616972734F6657697A61726473.png |
273 | Elrond's avatar: /home/gildor/.config/tox/avatars/43656C65627269616E20646F6E277420546F782E426164206D656D6F72696573.png | 282 | Elrond's avatar: /home/gildor/.config/tox/avatars/43656C65627269616E20646F6E277420546F782E426164206D656D6F72696573.png |
274 | Elrond's hash: /home/gildor/.config/tox/avatars/43656C65627269616E20646F6E277420546F782E426164206D656D6F72696573.hash | 283 | Elrond's hash: /home/gildor/.config/tox/avatars/43656C65627269616E20646F6E277420546F782E426164206D656D6F72696573.hash |
275 | Elladan's avatar: /home/gildor/.config/tox/avatars/49486174655768656E48756D616E735468696E6B49416D4D7942726F74686572.png | 284 | Elladan's avatar: /home/gildor/.config/tox/avatars/49486174655768656E48756D616E735468696E6B49416D4D7942726F74686572.png |
@@ -313,7 +322,12 @@ notifications and unnecessary data transfers. | |||
313 | 322 | ||
314 | #### Removing the avatar from the current user | 323 | #### Removing the avatar from the current user |
315 | 324 | ||
316 | To remove an avatar, an application must set it to `TOX_AVATAR_FORMAT_NONE`. | 325 | To remove the current avatar, an application must call |
326 | |||
327 | tox_unset_avatar(tox); | ||
328 | |||
329 | the effect is the same as setting the avatar format to `TOX_AVATAR_FORMAT_NONE` | ||
330 | and with no data: | ||
317 | 331 | ||
318 | tox_set_avatar(tox, TOX_AVATAR_FORMAT_NONE, NULL, 0); | 332 | tox_set_avatar(tox, TOX_AVATAR_FORMAT_NONE, NULL, 0); |
319 | 333 | ||
@@ -408,7 +422,7 @@ calls: | |||
408 | 422 | ||
409 | In the previous examples, implementation of the functions to check, store | 423 | In the previous examples, implementation of the functions to check, store |
410 | and retrieve data from the cache were omitted for brevity. These functions | 424 | and retrieve data from the cache were omitted for brevity. These functions |
411 | will also need to get the friend client ID (public key) from they friend | 425 | will also need to get the friend public key (client id) from they friend |
412 | number and, usually, convert it from a byte string to a hexadecimal | 426 | number and, usually, convert it from a byte string to a hexadecimal |
413 | string. A complete, yet more complex, example is available in the file | 427 | string. A complete, yet more complex, example is available in the file |
414 | `testing/test_avatars.c`. | 428 | `testing/test_avatars.c`. |
@@ -555,7 +569,7 @@ from a client "B": | |||
555 | If valid, "A" updates the 'bytes_received' counter and concatenates the | 569 | If valid, "A" updates the 'bytes_received' counter and concatenates the |
556 | newly arrived data to the buffer. | 570 | newly arrived data to the buffer. |
557 | 571 | ||
558 | The "A" checks if all the data was already received by comparing the | 572 | Then "A" checks if all the data was already received by comparing the |
559 | counter 'bytes_received' with the field 'total_length'. If they are | 573 | counter 'bytes_received' with the field 'total_length'. If they are |
560 | equal, "A" takes a SHA-256 hash of the data and compares it with the | 574 | equal, "A" takes a SHA-256 hash of the data and compares it with the |
561 | hash stored in the field 'hash' received from the first | 575 | hash stored in the field 'hash' received from the first |
diff --git a/testing/test_avatars.c b/testing/test_avatars.c index 71a0330e..b4adc48f 100644 --- a/testing/test_avatars.c +++ b/testing/test_avatars.c | |||
@@ -18,7 +18,7 @@ | |||
18 | * Data dir MAY have: | 18 | * Data dir MAY have: |
19 | * | 19 | * |
20 | * - A directory named "avatars" with the user's avatar and cached avatars. | 20 | * - A directory named "avatars" with the user's avatar and cached avatars. |
21 | * The user avatar must be named in the format: "<uppercase user id>.png" | 21 | * The user avatar must be named in the format: "<uppercase pub key>.png" |
22 | * | 22 | * |
23 | * | 23 | * |
24 | * The bot will answer to these commands: | 24 | * The bot will answer to these commands: |
@@ -163,14 +163,14 @@ static void byte_to_hex_str(const uint8_t *buf, const size_t buflen, char *dst) | |||
163 | dst[j++] = '\0'; | 163 | dst[j++] = '\0'; |
164 | } | 164 | } |
165 | 165 | ||
166 | /* Make the cache file name for a avatar of the given format for the given | 166 | /* Make the cache file name for an avatar of the given format for the given |
167 | * client id. | 167 | * public key. |
168 | */ | 168 | */ |
169 | static int make_avatar_file_name(char *dst, size_t dst_len, const char *base_dir, | 169 | static int make_avatar_file_name(char *dst, size_t dst_len, const char *base_dir, |
170 | const uint8_t format, uint8_t *client_id) | 170 | const uint8_t format, uint8_t *public_key) |
171 | { | 171 | { |
172 | char client_id_str[2 * TOX_CLIENT_ID_SIZE + 1]; | 172 | char public_key_str[2 * TOX_PUBLIC_KEY_SIZE + 1]; |
173 | byte_to_hex_str(client_id, TOX_CLIENT_ID_SIZE, client_id_str); | 173 | byte_to_hex_str(public_key, TOX_PUBLIC_KEY_SIZE, public_key_str); |
174 | 174 | ||
175 | const char *suffix = get_avatar_suffix_from_format(format); | 175 | const char *suffix = get_avatar_suffix_from_format(format); |
176 | 176 | ||
@@ -178,7 +178,7 @@ static int make_avatar_file_name(char *dst, size_t dst_len, const char *base_dir | |||
178 | return -1; /* Error */ | 178 | return -1; /* Error */ |
179 | 179 | ||
180 | int n = snprintf(dst, dst_len, "%s/%s/%s.%s", base_dir, AVATAR_DIR_NAME, | 180 | int n = snprintf(dst, dst_len, "%s/%s/%s.%s", base_dir, AVATAR_DIR_NAME, |
181 | client_id_str, suffix); | 181 | public_key_str, suffix); |
182 | dst[dst_len - 1] = '\0'; | 182 | dst[dst_len - 1] = '\0'; |
183 | 183 | ||
184 | if (n >= dst_len) | 184 | if (n >= dst_len) |
@@ -196,7 +196,7 @@ static int make_avatar_file_name(char *dst, size_t dst_len, const char *base_dir | |||
196 | static int load_user_avatar(Tox *tox, char *base_dir, int friendnum, | 196 | static int load_user_avatar(Tox *tox, char *base_dir, int friendnum, |
197 | uint8_t format, uint8_t *hash, uint8_t *data, uint32_t *datalen) | 197 | uint8_t format, uint8_t *hash, uint8_t *data, uint32_t *datalen) |
198 | { | 198 | { |
199 | uint8_t addr[TOX_CLIENT_ID_SIZE]; | 199 | uint8_t addr[TOX_PUBLIC_KEY_SIZE]; |
200 | 200 | ||
201 | if (tox_get_client_id(tox, friendnum, addr) != 0) { | 201 | if (tox_get_client_id(tox, friendnum, addr) != 0) { |
202 | DEBUG("Bad client id, friendnumber=%d", friendnum); | 202 | DEBUG("Bad client id, friendnumber=%d", friendnum); |
@@ -224,14 +224,14 @@ static int load_user_avatar(Tox *tox, char *base_dir, int friendnum, | |||
224 | return 0; | 224 | return 0; |
225 | } | 225 | } |
226 | 226 | ||
227 | /* Save a user avatar into the cache. Gets the file name from client id and | 227 | /* Save a user avatar into the cache. Gets the file name from the public key |
228 | * the given data format. | 228 | * and the given data format. |
229 | * Returns 0 on success, or -1 on error. | 229 | * Returns 0 on success, or -1 on error. |
230 | */ | 230 | */ |
231 | static int save_user_avatar(Tox *tox, char *base_dir, int friendnum, | 231 | static int save_user_avatar(Tox *tox, char *base_dir, int friendnum, |
232 | uint8_t format, uint8_t *data, uint32_t datalen) | 232 | uint8_t format, uint8_t *data, uint32_t datalen) |
233 | { | 233 | { |
234 | uint8_t addr[TOX_CLIENT_ID_SIZE]; | 234 | uint8_t addr[TOX_PUBLIC_KEY_SIZE]; |
235 | 235 | ||
236 | if (tox_get_client_id(tox, friendnum, addr) != 0) { | 236 | if (tox_get_client_id(tox, friendnum, addr) != 0) { |
237 | DEBUG("Bad client id, friendnumber=%d", friendnum); | 237 | DEBUG("Bad client id, friendnumber=%d", friendnum); |
@@ -252,7 +252,7 @@ static int save_user_avatar(Tox *tox, char *base_dir, int friendnum, | |||
252 | /* Delete all cached avatars for a given user */ | 252 | /* Delete all cached avatars for a given user */ |
253 | static int delete_user_avatar(Tox *tox, char *base_dir, int friendnum) | 253 | static int delete_user_avatar(Tox *tox, char *base_dir, int friendnum) |
254 | { | 254 | { |
255 | uint8_t addr[TOX_CLIENT_ID_SIZE]; | 255 | uint8_t addr[TOX_PUBLIC_KEY_SIZE]; |
256 | 256 | ||
257 | if (tox_get_client_id(tox, friendnum, addr) != 0) { | 257 | if (tox_get_client_id(tox, friendnum, addr) != 0) { |
258 | DEBUG("Bad client id, friendnumber=%d", friendnum); | 258 | DEBUG("Bad client id, friendnumber=%d", friendnum); |
@@ -288,11 +288,11 @@ static int delete_user_avatar(Tox *tox, char *base_dir, int friendnum) | |||
288 | 288 | ||
289 | static void friend_status_cb(Tox *tox, int n, uint8_t status, void *ud) | 289 | static void friend_status_cb(Tox *tox, int n, uint8_t status, void *ud) |
290 | { | 290 | { |
291 | uint8_t addr[TOX_CLIENT_ID_SIZE]; | 291 | uint8_t addr[TOX_PUBLIC_KEY_SIZE]; |
292 | char addr_str[2 * TOX_CLIENT_ID_SIZE + 1]; | 292 | char addr_str[2 * TOX_PUBLIC_KEY_SIZE + 1]; |
293 | 293 | ||
294 | if (tox_get_client_id(tox, n, addr) == 0) { | 294 | if (tox_get_client_id(tox, n, addr) == 0) { |
295 | byte_to_hex_str(addr, TOX_CLIENT_ID_SIZE, addr_str); | 295 | byte_to_hex_str(addr, TOX_PUBLIC_KEY_SIZE, addr_str); |
296 | printf("Receiving status from %s: %u\n", addr_str, status); | 296 | printf("Receiving status from %s: %u\n", addr_str, status); |
297 | } | 297 | } |
298 | } | 298 | } |
@@ -300,12 +300,12 @@ static void friend_status_cb(Tox *tox, int n, uint8_t status, void *ud) | |||
300 | static void friend_avatar_info_cb(Tox *tox, int32_t n, uint8_t format, uint8_t *hash, void *ud) | 300 | static void friend_avatar_info_cb(Tox *tox, int32_t n, uint8_t format, uint8_t *hash, void *ud) |
301 | { | 301 | { |
302 | char *base_dir = (char *) ud; | 302 | char *base_dir = (char *) ud; |
303 | uint8_t addr[TOX_CLIENT_ID_SIZE]; | 303 | uint8_t addr[TOX_PUBLIC_KEY_SIZE]; |
304 | char addr_str[2 * TOX_CLIENT_ID_SIZE + 1]; | 304 | char addr_str[2 * TOX_PUBLIC_KEY_SIZE + 1]; |
305 | char hash_str[2 * TOX_HASH_LENGTH + 1]; | 305 | char hash_str[2 * TOX_HASH_LENGTH + 1]; |
306 | 306 | ||
307 | if (tox_get_client_id(tox, n, addr) == 0) { | 307 | if (tox_get_client_id(tox, n, addr) == 0) { |
308 | byte_to_hex_str(addr, TOX_CLIENT_ID_SIZE, addr_str); | 308 | byte_to_hex_str(addr, TOX_PUBLIC_KEY_SIZE, addr_str); |
309 | printf("Receiving avatar information from %s.\n", addr_str); | 309 | printf("Receiving avatar information from %s.\n", addr_str); |
310 | } else { | 310 | } else { |
311 | DEBUG("tox_get_client_id failed"); | 311 | DEBUG("tox_get_client_id failed"); |
@@ -350,12 +350,12 @@ static void friend_avatar_data_cb(Tox *tox, int32_t n, uint8_t format, | |||
350 | uint8_t *hash, uint8_t *data, uint32_t datalen, void *ud) | 350 | uint8_t *hash, uint8_t *data, uint32_t datalen, void *ud) |
351 | { | 351 | { |
352 | char *base_dir = (char *) ud; | 352 | char *base_dir = (char *) ud; |
353 | uint8_t addr[TOX_CLIENT_ID_SIZE]; | 353 | uint8_t addr[TOX_PUBLIC_KEY_SIZE]; |
354 | char addr_str[2 * TOX_CLIENT_ID_SIZE + 1]; | 354 | char addr_str[2 * TOX_PUBLIC_KEY_SIZE + 1]; |
355 | char hash_str[2 * TOX_HASH_LENGTH + 1]; | 355 | char hash_str[2 * TOX_HASH_LENGTH + 1]; |
356 | 356 | ||
357 | if (tox_get_client_id(tox, n, addr) == 0) { | 357 | if (tox_get_client_id(tox, n, addr) == 0) { |
358 | byte_to_hex_str(addr, TOX_CLIENT_ID_SIZE, addr_str); | 358 | byte_to_hex_str(addr, TOX_PUBLIC_KEY_SIZE, addr_str); |
359 | printf("Receiving avatar data from %s.\n", addr_str); | 359 | printf("Receiving avatar data from %s.\n", addr_str); |
360 | } else { | 360 | } else { |
361 | DEBUG("tox_get_client_id failed"); | 361 | DEBUG("tox_get_client_id failed"); |
@@ -382,8 +382,8 @@ static void friend_msg_cb(Tox *tox, int n, const uint8_t *msg, uint16_t len, voi | |||
382 | { | 382 | { |
383 | const char *base_dir = (char *) ud; | 383 | const char *base_dir = (char *) ud; |
384 | const char *msg_str = (char *) msg; | 384 | const char *msg_str = (char *) msg; |
385 | uint8_t addr[TOX_CLIENT_ID_SIZE]; | 385 | uint8_t addr[TOX_PUBLIC_KEY_SIZE]; |
386 | char addr_str[2 * TOX_CLIENT_ID_SIZE + 1]; | 386 | char addr_str[2 * TOX_PUBLIC_KEY_SIZE + 1]; |
387 | 387 | ||
388 | if (tox_get_client_id(tox, n, addr) == 0) { | 388 | if (tox_get_client_id(tox, n, addr) == 0) { |
389 | byte_to_hex_str(addr, TOX_FRIEND_ADDRESS_SIZE, addr_str); | 389 | byte_to_hex_str(addr, TOX_FRIEND_ADDRESS_SIZE, addr_str); |
@@ -428,8 +428,8 @@ static void friend_msg_cb(Tox *tox, int n, const uint8_t *msg, uint16_t len, voi | |||
428 | static void friend_request_cb(Tox *tox, const uint8_t *public_key, | 428 | static void friend_request_cb(Tox *tox, const uint8_t *public_key, |
429 | const uint8_t *data, uint16_t length, void *ud) | 429 | const uint8_t *data, uint16_t length, void *ud) |
430 | { | 430 | { |
431 | char addr_str[2 * TOX_CLIENT_ID_SIZE + 1]; | 431 | char addr_str[2 * TOX_PUBLIC_KEY_SIZE + 1]; |
432 | byte_to_hex_str(public_key, TOX_CLIENT_ID_SIZE, addr_str); | 432 | byte_to_hex_str(public_key, TOX_PUBLIC_KEY_SIZE, addr_str); |
433 | printf("Accepting friend request from %s.\n %s\n", addr_str, data); | 433 | printf("Accepting friend request from %s.\n %s\n", addr_str, data); |
434 | tox_add_friend_norequest(tox, public_key); | 434 | tox_add_friend_norequest(tox, public_key); |
435 | } | 435 | } |