diff options
Diffstat (limited to 'testing')
-rw-r--r-- | testing/Makefile.inc | 15 | ||||
-rw-r--r-- | testing/test_avatars.c | 770 |
2 files changed, 785 insertions, 0 deletions
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 | |||
109 | noinst_PROGRAMS += test_avatars | ||
110 | |||
111 | test_avatars_SOURCES = ../testing/test_avatars.c | ||
112 | |||
113 | test_avatars_CFLAGS = $(LIBSODIUM_CFLAGS) \ | ||
114 | $(NACL_CFLAGS) | ||
115 | |||
116 | test_avatars_LDADD = $(LIBSODIUM_LDFLAGS) \ | ||
117 | $(NACL_LDFLAGS) \ | ||
118 | libtoxcore.la \ | ||
119 | $(LIBSODIUM_LIBS) \ | ||
120 | $(NACL_OBJECTS) \ | ||
121 | $(NACL_LIBS) | ||
122 | |||
108 | endif | 123 | endif |
109 | 124 | ||
110 | EXTRA_DIST += $(top_srcdir)/testing/misc_tools.c | 125 | EXTRA_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 | |||
62 | static bool print_debug_msgs = true; | ||
63 | |||
64 | static 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 | |||
81 | typedef struct { | ||
82 | uint8_t format; | ||
83 | char *suffix; | ||
84 | char *file_name; | ||
85 | } def_avatar_name_t; | ||
86 | |||
87 | static 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 | |||
95 | static void set_avatar(Tox *tox, const char *base_dir); | ||
96 | |||
97 | |||
98 | static 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 | */ | ||
114 | static 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 */ | ||
135 | static 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 | |||
154 | static 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 | */ | ||
172 | static 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 | */ | ||
199 | static 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 | */ | ||
234 | static 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 */ | ||
256 | static 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 | |||
292 | static 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 | |||
303 | static 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 | |||
352 | static 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 | |||
384 | static 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 | |||
431 | static 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 | |||
441 | static 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 | |||
484 | static 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 | |||
504 | static 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 | */ | ||
525 | static 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 | |||
553 | static 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 | |||
616 | static 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 | |||
686 | int 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 | } | ||