1 #include "avatar.h"
2
3 #include "debug.h"
4 #include "file_transfers.h"
5 #include "filesys.h"
6 #include "self.h"
7 #include "tox.h"
8
9 #include "native/image.h"
10
11 #include <stdlib.h>
12 #include <string.h>
13
14 /* frees the image of an avatar, does nothing if image is NULL */
avatar_free_image(AVATAR * avatar)15 static void avatar_free_image(AVATAR *avatar) {
16 if (avatar) {
17 image_free(avatar->img);
18 avatar->img = NULL;
19 avatar->size = 0;
20 }
21 }
22
avatar_save(char hexid[TOX_PUBLIC_KEY_SIZE * 2],const uint8_t * data,size_t length)23 bool avatar_save(char hexid[TOX_PUBLIC_KEY_SIZE * 2], const uint8_t *data, size_t length) {
24 char name[sizeof("avatars/") + TOX_PUBLIC_KEY_SIZE * 2 + sizeof(".png")] = { 0 };
25 FILE *fp;
26
27 snprintf(name, sizeof("avatars/") + TOX_PUBLIC_KEY_SIZE * 2 + sizeof(".png"), "avatars/%.*s.png",
28 TOX_PUBLIC_KEY_SIZE * 2, hexid);
29
30 fp = utox_get_file(name, NULL, UTOX_FILE_OPTS_WRITE | UTOX_FILE_OPTS_MKDIR);
31
32 if (!fp) {
33 LOG_WARN("Avatar", "Could not save avatar for: %.*s", TOX_PUBLIC_KEY_SIZE * 2, hexid);
34 return false;
35 }
36
37 fwrite(data, length, 1, fp);
38 fclose(fp);
39 return true;
40 }
41
load_img_data(char hexid[TOX_PUBLIC_KEY_SIZE * 2],size_t * out_size)42 static uint8_t *load_img_data(char hexid[TOX_PUBLIC_KEY_SIZE * 2], size_t *out_size) {
43 char name[sizeof("avatars/") + TOX_PUBLIC_KEY_SIZE * 2 + sizeof(".png")] = { 0 };
44
45 snprintf(name, sizeof("avatars/") + TOX_PUBLIC_KEY_SIZE * 2 + sizeof(".png"), "avatars/%.*s.png",
46 TOX_PUBLIC_KEY_SIZE * 2, hexid);
47
48 size_t size = 0;
49 FILE *fp = utox_get_file(name, &size, UTOX_FILE_OPTS_READ);
50 if (fp == NULL) {
51 LOG_TRACE("Avatar", "Could not read: %s", name);
52 return NULL;
53 }
54
55 uint8_t *data = calloc(1, size);
56 if (data == NULL) {
57 LOG_ERR("Avatar", "Could not allocate memory for file of size %zu.", size);
58 fclose(fp);
59 return NULL;
60 }
61
62 if (fread(data, size, 1, fp) != 1) {
63 LOG_WARN("Avatar", "Could not read from open file: %s", name);
64 fclose(fp);
65 free(data);
66 return NULL;
67 }
68
69 fclose(fp);
70 if (out_size) {
71 *out_size = size;
72 }
73 return data;
74 }
75
avatar_delete(char hexid[TOX_PUBLIC_KEY_SIZE * 2])76 bool avatar_delete(char hexid[TOX_PUBLIC_KEY_SIZE * 2]) {
77 char name[sizeof("avatars/") + TOX_PUBLIC_KEY_SIZE * 2 + sizeof(".png")] = { 0 };
78
79 snprintf(name, sizeof(name), "avatars/%.*s.png", TOX_PUBLIC_KEY_SIZE * 2, hexid);
80 int name_len = strnlen(name, sizeof(name) - 1);
81
82 return utox_remove_file((uint8_t *)name, name_len);
83 }
84
avatar_load(char hexid[TOX_PUBLIC_KEY_SIZE * 2],AVATAR * avatar,size_t * size_out)85 static bool avatar_load(char hexid[TOX_PUBLIC_KEY_SIZE * 2], AVATAR *avatar, size_t *size_out) {
86 size_t size = 0;
87
88 uint8_t *img = load_img_data(hexid, &size);
89 if (!img) {
90 LOG_DEBUG("Avatar", "Unable to get saved avatar from disk for friend %.*s" ,
91 TOX_PUBLIC_KEY_SIZE * 2, hexid);
92 return false;
93 }
94
95 if (size > UTOX_AVATAR_MAX_DATA_LENGTH) {
96 free(img);
97 LOG_WARN("Avatar", "Saved avatar file for friend (%.*s) too large for tox" ,
98 TOX_PUBLIC_KEY_SIZE * 2, hexid);
99 return false;
100 }
101
102 avatar->img = utox_image_to_native(img, size, &avatar->width, &avatar->height, true);
103 if (avatar->img) {
104 avatar->format = UTOX_AVATAR_FORMAT_PNG;
105 avatar->size = size;
106 tox_hash(avatar->hash, img, size);
107 if (size_out) {
108 *size_out = size;
109 }
110 if (avatar == self.avatar) {
111 // We need to save our avatar in PNG format so we can send it to friends!
112 self.png_data = img;
113 self.png_size = size;
114 } else {
115 free(img);
116 }
117 return true;
118 }
119
120 free(img);
121 return false;
122 }
123
avatar_set(AVATAR * avatar,const uint8_t * data,size_t size)124 bool avatar_set(AVATAR *avatar, const uint8_t *data, size_t size) {
125 if (avatar == NULL) {
126 LOG_DEBUG("Avatar", "avatar is null.");
127 return false;
128 }
129
130 if (size > UTOX_AVATAR_MAX_DATA_LENGTH) {
131 LOG_ERR("Avatar", " avatar too large");
132 return false;
133 }
134
135 avatar_free_image(avatar);
136 NATIVE_IMAGE *image = utox_image_to_native((UTOX_IMAGE)data, size, &avatar->width, &avatar->height, true);
137 if (!NATIVE_IMAGE_IS_VALID(image)) {
138 LOG_DEBUG("Avatar", "avatar is invalid");
139 return false;
140 }
141
142 avatar->img = image;
143 avatar->format = UTOX_AVATAR_FORMAT_PNG;
144 avatar->size = size;
145 tox_hash(avatar->hash, data, size);
146
147 return true;
148 }
149
150 /* sets self avatar, see self_set_and_save_avatar */
avatar_set_self(const uint8_t * data,size_t size)151 bool avatar_set_self(const uint8_t *data, size_t size) {
152 return avatar_set(self.avatar, data, size);
153 }
154
avatar_unset(AVATAR * avatar)155 void avatar_unset(AVATAR *avatar) {
156 if (avatar == NULL) {
157 LOG_TRACE("Avatar", " avatar is null" );
158 return;
159 }
160
161 avatar->format = UTOX_AVATAR_FORMAT_NONE;
162 avatar_free_image(avatar);
163 }
164
avatar_unset_self(void)165 void avatar_unset_self(void) {
166 avatar_unset(self.avatar);
167 }
168
avatar_init(char hexid[TOX_PUBLIC_KEY_SIZE * 2],AVATAR * avatar)169 bool avatar_init(char hexid[TOX_PUBLIC_KEY_SIZE * 2], AVATAR *avatar) {
170 avatar_unset(avatar);
171 return avatar_load(hexid, avatar, NULL);
172 }
173
avatar_init_self(void)174 bool avatar_init_self(void) {
175 self.avatar = calloc(1, sizeof(AVATAR));
176 if (self.avatar == NULL) {
177 return false;
178 }
179
180 return avatar_load(self.id_str, self.avatar, NULL);
181 }
182
self_set_and_save_avatar(const uint8_t * data,uint32_t size)183 bool self_set_and_save_avatar(const uint8_t *data, uint32_t size) {
184 if (avatar_set_self(data, size)) {
185 avatar_save(self.id_str, data, size);
186 return true;
187 }
188 return false;
189 }
190
avatar_delete_self(void)191 void avatar_delete_self(void) {
192 avatar_unset(self.avatar);
193 avatar_delete(self.id_str);
194 postmessage_toxcore(TOX_AVATAR_UNSET, 0, 0, NULL);
195 }
196
avatar_on_friend_online(Tox * tox,uint32_t friend_number)197 bool avatar_on_friend_online(Tox *tox, uint32_t friend_number) {
198 if (!self.png_data) {
199 uint8_t *avatar_data = load_img_data(self.id_str, &self.png_size);
200 if (!avatar_data) {
201 LOG_WARN("Avatar", "Unable to get out avatar data to send to friend.");
202 self.png_data = NULL;
203 self.png_size = 0;
204 return false;
205 }
206 self.png_data = avatar_data;
207 }
208
209 ft_send_avatar(tox, friend_number);
210 return true;
211 }
212
avatar_move(const uint8_t * source,const uint8_t * dest)213 bool avatar_move(const uint8_t *source, const uint8_t *dest) {
214 uint8_t current_name[sizeof("avatars/") + TOX_PUBLIC_KEY_SIZE * 2 + sizeof(".png")] = { 0 };
215 uint8_t new_name[sizeof("avatars/") + TOX_PUBLIC_KEY_SIZE * 2 + sizeof(".png")] = { 0 };
216
217 snprintf((char *)current_name, sizeof("avatars/") + TOX_PUBLIC_KEY_SIZE * 2 + sizeof(".png"), "avatars/%.*s.png",
218 TOX_PUBLIC_KEY_SIZE * 2, source);
219 snprintf((char *)new_name, sizeof("avatars/") + TOX_PUBLIC_KEY_SIZE * 2 + sizeof(".png"), "avatars/%.*s.png",
220 TOX_PUBLIC_KEY_SIZE * 2, dest);
221
222 return utox_move_file(current_name, new_name);
223 }
224