diff options
-rw-r--r-- | docs/Avatars.md | 35 | ||||
-rw-r--r-- | testing/test_avatars.c | 114 |
2 files changed, 67 insertions, 82 deletions
diff --git a/docs/Avatars.md b/docs/Avatars.md index 75517004..74308f02 100644 --- a/docs/Avatars.md +++ b/docs/Avatars.md | |||
@@ -241,18 +241,22 @@ already downloaded by other clients can be reused. | |||
241 | 241 | ||
242 | Given the Tox data directory described in STS Draft v0.1.0: | 242 | Given the Tox data directory described in STS Draft v0.1.0: |
243 | 243 | ||
244 | - The user avatar is stored in a file named "avatar.png". As more formats | 244 | - Avatars are stored in a directory called "avatars" and named |
245 | may be used in the future, another extensions are reserved and clients | 245 | as "xxxxx.png", where "xxxxx" is the complete client id (but not friend |
246 | should keep just one file named "avatar.*", with the data of the last | 246 | address!) encoded as an uppercase hexadecimal string and "png" is the |
247 | avatar set by the user. If the user have no avatar, no such files should | 247 | extension for the PNG avatar. As new image formats may be used in the |
248 | be kept in the data directory; | 248 | future, clients should ensure no other file "xxxxx.*" exists. No file |
249 | 249 | should be kept for an user who have no avatar. | |
250 | - Friends avatars are stored in a directory called "avatars" and named | 250 | |
251 | as "xxxxx.png", where "xxxxx" is the complete client id encoded as an | 251 | - The client's own avatar is not special and is stored like any other. This |
252 | uppercase hexadecimal string and "png" is the extension for the PNG | 252 | is partially for simplicity, and partially in anticipation of profiles. |
253 | avatar. As new image formats may be used in the future, clients should | 253 | |
254 | ensure no other file "xxxxx.*" exists. No file should be kept for an user | 254 | - The avatar should be stored as its recieved, before any modifications by |
255 | who have no avatar. | 255 | the client for display purposes. |
256 | |||
257 | - The hash, as calculated by toxcore and passed in to the data callback, | ||
258 | should be saved in "avatars/xxxxx.hash" where "xxxxx" means the | ||
259 | same thing as for avatars. (The filename is longer than the file :) ) | ||
256 | 260 | ||
257 | **To be discussed:** User keys are usually presented in Tox clients as | 261 | **To be discussed:** User keys are usually presented in Tox clients as |
258 | upper case strings, but lower case file names are more usual. | 262 | upper case strings, but lower case file names are more usual. |
@@ -262,13 +266,14 @@ Example for Linux and other Unix systems, assuming an user called "gildor": | |||
262 | 266 | ||
263 | Tox data directory: /home/gildor/.config/tox/ | 267 | Tox data directory: /home/gildor/.config/tox/ |
264 | Tox data file: /home/gildor/.config/tox/data | 268 | 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/ | 269 | Avatar data dir: /home/gildor/.config/tox/avatars/ |
270 | Gildor's avatar: /home/gildor/.config/tox/avatars/E5809EEF5F11AB29B9BDF543C05B58DDF454AB9CA176C235C7699FDC2757DC33.png | ||
267 | Elrond's avatar: /home/gildor/.config/tox/avatars/43656C65627269616E20646F6E277420546F782E426164206D656D6F72696573.png | 271 | Elrond's avatar: /home/gildor/.config/tox/avatars/43656C65627269616E20646F6E277420546F782E426164206D656D6F72696573.png |
272 | Elrond's hash: /home/gildor/.config/tox/avatars/43656C65627269616E20646F6E277420546F782E426164206D656D6F72696573.hash | ||
268 | Elladan's avatar: /home/gildor/.config/tox/avatars/49486174655768656E48756D616E735468696E6B49416D4D7942726F74686572.png | 273 | Elladan's avatar: /home/gildor/.config/tox/avatars/49486174655768656E48756D616E735468696E6B49416D4D7942726F74686572.png |
274 | Elladan's hash: /home/gildor/.config/tox/avatars/49486174655768656E48756D616E735468696E6B49416D4D7942726F74686572.hash | ||
269 | Elrohir's avatar /home/gildor/.config/tox/avatars/726568746F7242794D6D41496B6E696854736E616D75486E6568576574614849.png | 275 | Elrohir's avatar /home/gildor/.config/tox/avatars/726568746F7242794D6D41496B6E696854736E616D75486E6568576574614849.png |
270 | Arwen's avatar: /home/gildor/.config/tox/avatars/53686520746F6F6B20476C6F7266696E64656C277320706C6163652068657265.png | 276 | Elrohir's hash: /home/gildor/.config/tox/avatars/726568746F7242794D6D41496B6E696854736E616D75486E6568576574614849.hash |
271 | Lindir's avatar: /home/gildor/.config/tox/avatars/417070735772697474656E42794D6F7274616C734C6F6F6B54686553616D652E.png | ||
272 | 277 | ||
273 | This recommendation is partially implemented by "testing/test_avatars.c". | 278 | This recommendation is partially implemented by "testing/test_avatars.c". |
274 | 279 | ||
diff --git a/testing/test_avatars.c b/testing/test_avatars.c index 55beea67..6afbac1d 100644 --- a/testing/test_avatars.c +++ b/testing/test_avatars.c | |||
@@ -17,17 +17,15 @@ | |||
17 | * | 17 | * |
18 | * Data dir MAY have: | 18 | * Data dir MAY have: |
19 | * | 19 | * |
20 | * - A file named avatar.png. If given, the bot will publish it. Otherwise, | 20 | * - A directory named "avatars" with the user's avatar and cached avatars. |
21 | * no avatar will be set. | 21 | * The user avatar must be named in the format: "<uppercase user id>.png" |
22 | * | ||
23 | * - A directory named "avatars" with the currently cached avatars. | ||
24 | * | 22 | * |
25 | * | 23 | * |
26 | * The bot will answer to these commands: | 24 | * The bot will answer to these commands: |
27 | * | 25 | * |
28 | * !debug-on - Enable extended debug messages | 26 | * !debug-on - Enable extended debug messages |
29 | * !debug-off - Disenable extended debug messages | 27 | * !debug-off - Disenable extended debug messages |
30 | * !set-avatar - Set our avatar to the contents of the file avatar.* | 28 | * !set-avatar - Set our avatar from "avatars/<USERID>.png" |
31 | * !remove-avatar - Remove our avatar | 29 | * !remove-avatar - Remove our avatar |
32 | * | 30 | * |
33 | */ | 31 | */ |
@@ -81,13 +79,12 @@ static void debug_printf(const char *fmt, ...) | |||
81 | typedef struct { | 79 | typedef struct { |
82 | uint8_t format; | 80 | uint8_t format; |
83 | char *suffix; | 81 | char *suffix; |
84 | char *file_name; | 82 | } avatar_format_data_t; |
85 | } def_avatar_name_t; | ||
86 | 83 | ||
87 | static const def_avatar_name_t def_avatar_names[] = { | 84 | static const avatar_format_data_t avatar_formats[] = { |
88 | /* In order of preference */ | 85 | /* In order of preference */ |
89 | { TOX_AVATAR_FORMAT_PNG, "png", "avatar.png" }, | 86 | { TOX_AVATAR_FORMAT_PNG, "png" }, |
90 | { TOX_AVATAR_FORMAT_NONE, NULL, NULL }, /* Must be the last one */ | 87 | { TOX_AVATAR_FORMAT_NONE, NULL }, /* Must be the last one */ |
91 | }; | 88 | }; |
92 | 89 | ||
93 | 90 | ||
@@ -99,9 +96,9 @@ static char *get_avatar_suffix_from_format(uint8_t format) | |||
99 | { | 96 | { |
100 | int i; | 97 | int i; |
101 | 98 | ||
102 | for (i = 0; def_avatar_names[i].format != TOX_AVATAR_FORMAT_NONE; i++) | 99 | for (i = 0; avatar_formats[i].format != TOX_AVATAR_FORMAT_NONE; i++) |
103 | if (def_avatar_names[i].format == format) | 100 | if (avatar_formats[i].format == format) |
104 | return def_avatar_names[i].suffix; | 101 | return avatar_formats[i].suffix; |
105 | 102 | ||
106 | return NULL; | 103 | return NULL; |
107 | } | 104 | } |
@@ -169,8 +166,8 @@ static void byte_to_hex_str(const uint8_t *buf, const size_t buflen, char *dst) | |||
169 | /* Make the cache file name for a avatar of the given format for the given | 166 | /* Make the cache file name for a avatar of the given format for the given |
170 | * client id. | 167 | * client id. |
171 | */ | 168 | */ |
172 | static int make_avatar_file_name(char *dst, size_t dst_len, | 169 | static int make_avatar_file_name(char *dst, size_t dst_len, const char *base_dir, |
173 | char *base_dir, uint8_t format, uint8_t *client_id) | 170 | const uint8_t format, uint8_t *client_id) |
174 | { | 171 | { |
175 | char client_id_str[2 * TOX_CLIENT_ID_SIZE + 1]; | 172 | char client_id_str[2 * TOX_CLIENT_ID_SIZE + 1]; |
176 | byte_to_hex_str(client_id, TOX_CLIENT_ID_SIZE, client_id_str); | 173 | byte_to_hex_str(client_id, TOX_CLIENT_ID_SIZE, client_id_str); |
@@ -267,13 +264,13 @@ static int delete_user_avatar(Tox *tox, char *base_dir, int friendnum) | |||
267 | /* This iteration is dumb and inefficient */ | 264 | /* This iteration is dumb and inefficient */ |
268 | int i; | 265 | int i; |
269 | 266 | ||
270 | for (i = 0; def_avatar_names[i].format != TOX_AVATAR_FORMAT_NONE; i++) { | 267 | for (i = 0; avatar_formats[i].format != TOX_AVATAR_FORMAT_NONE; i++) { |
271 | int ret = make_avatar_file_name(path, sizeof(path), base_dir, | 268 | int ret = make_avatar_file_name(path, sizeof(path), base_dir, |
272 | def_avatar_names[i].format, addr); | 269 | avatar_formats[i].format, addr); |
273 | 270 | ||
274 | if (ret != 0) { | 271 | if (ret != 0) { |
275 | DEBUG("Failed to create avatar path for friend #%d, format %d\n", | 272 | DEBUG("Failed to create avatar path for friend #%d, format %d\n", |
276 | friendnum, def_avatar_names[i].format); | 273 | friendnum, avatar_formats[i].format); |
277 | continue; | 274 | continue; |
278 | } | 275 | } |
279 | 276 | ||
@@ -438,66 +435,49 @@ static void friend_request_cb(Tox *tox, const uint8_t *public_key, | |||
438 | } | 435 | } |
439 | 436 | ||
440 | 437 | ||
441 | static int try_avatar_file(Tox *tox, const char *base_dir, const def_avatar_name_t *an) | 438 | static void set_avatar(Tox *tox, const char *base_dir) |
442 | { | 439 | { |
440 | uint8_t addr[TOX_FRIEND_ADDRESS_SIZE]; | ||
443 | char path[PATH_MAX]; | 441 | char path[PATH_MAX]; |
444 | int n = snprintf(path, sizeof(path), "%s/%s", base_dir, an->file_name); | 442 | uint8_t buf[2 * TOX_AVATAR_MAX_DATA_LENGTH]; |
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_AVATAR_MAX_DATA_LENGTH]; | ||
457 | int len = fread(buf, 1, sizeof(buf), fp); | ||
458 | |||
459 | if (len >= 0 && len <= TOX_AVATAR_MAX_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_AVATAR_MAX_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 | 443 | ||
444 | tox_get_address(tox, addr); | ||
483 | 445 | ||
484 | static void set_avatar(Tox *tox, const char *base_dir) | ||
485 | { | ||
486 | int i; | 446 | int i; |
487 | 447 | for (i = 0; ; i++) { | |
488 | for (i = 0; i < 4; i++) { | 448 | if (avatar_formats[i].format == TOX_AVATAR_FORMAT_NONE) { |
489 | if (def_avatar_names[i].format == TOX_AVATAR_FORMAT_NONE) { | ||
490 | tox_set_avatar(tox, TOX_AVATAR_FORMAT_NONE, NULL, 0); | 449 | tox_set_avatar(tox, TOX_AVATAR_FORMAT_NONE, NULL, 0); |
491 | printf("No avatar file found, setting to NONE.\n"); | 450 | printf("No avatar file found, setting to NONE.\n"); |
492 | return; | 451 | break; |
493 | } else { | 452 | } else { |
494 | if (try_avatar_file(tox, base_dir, &def_avatar_names[i]) == 0) | 453 | int ret = make_avatar_file_name(path, sizeof(path), base_dir, |
454 | avatar_formats[i].format, addr); | ||
455 | if (ret < 0) { | ||
456 | printf("Failed to generate avatar file name.\n"); | ||
457 | return; | ||
458 | } | ||
459 | |||
460 | int len = load_avatar_data(path, buf); | ||
461 | if (len < 0) { | ||
462 | printf("Failed to load avatar data from file: %s\n", path); | ||
463 | continue; | ||
464 | } | ||
465 | |||
466 | if (len > TOX_AVATAR_MAX_DATA_LENGTH) { | ||
467 | printf("Avatar file %s is too big (more than %d bytes)", | ||
468 | path, TOX_AVATAR_MAX_DATA_LENGTH); | ||
495 | return; | 469 | return; |
470 | } | ||
471 | |||
472 | ret = tox_set_avatar(tox, avatar_formats[i].format, buf, len); | ||
473 | DEBUG("tox_set_avatar returned=%d", ret); | ||
474 | if (ret == 0) | ||
475 | printf("Setting avatar from %s (%d bytes).\n", path, len); | ||
476 | else | ||
477 | printf("Error setting avatar from %s.\n", path); | ||
478 | return; | ||
496 | } | 479 | } |
497 | } | 480 | } |
498 | |||
499 | /* Should be unreachable */ | ||
500 | printf("UNEXPECTED CODE PATH\n"); | ||
501 | } | 481 | } |
502 | 482 | ||
503 | 483 | ||