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