contact.c (9874B)
1 /* 2 This file is part of GNUnet. 3 Copyright (C) 2021--2024 GNUnet e.V. 4 5 GNUnet is free software: you can redistribute it and/or modify it 6 under the terms of the GNU Affero General Public License as published 7 by the Free Software Foundation, either version 3 of the License, 8 or (at your option) any later version. 9 10 GNUnet is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Affero General Public License for more details. 14 15 You should have received a copy of the GNU Affero General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 SPDX-License-Identifier: AGPL3.0-or-later 19 */ 20 /* 21 * @author Tobias Frisch 22 * @file contact.c 23 */ 24 25 #include "contact.h" 26 27 #include "ui.h" 28 29 #include <gnunet/gnunet_chat_lib.h> 30 #include <gnunet/gnunet_common.h> 31 #include <string.h> 32 33 enum GNUNET_GenericReturnValue 34 contact_create_info(struct GNUNET_CHAT_Contact *contact) 35 { 36 if ((!contact) || (GNUNET_CHAT_contact_get_user_pointer(contact))) 37 return GNUNET_NO; 38 39 MESSENGER_ContactInfo* info = g_malloc(sizeof(MESSENGER_ContactInfo)); 40 41 info->last_message = NULL; 42 info->icon_file = NULL; 43 info->icon = NULL; 44 info->task = 0; 45 46 info->name_labels = NULL; 47 info->name_avatars = NULL; 48 info->visible_widgets = NULL; 49 50 GNUNET_CHAT_contact_set_user_pointer(contact, info); 51 return GNUNET_YES; 52 } 53 54 void 55 contact_destroy_info(struct GNUNET_CHAT_Contact *contact) 56 { 57 g_assert(contact); 58 59 MESSENGER_ContactInfo* info = GNUNET_CHAT_contact_get_user_pointer(contact); 60 61 if (!info) 62 return; 63 64 if (info->name_labels) 65 g_list_free(info->name_labels); 66 67 if (info->name_avatars) 68 g_list_free(info->name_avatars); 69 70 if (info->visible_widgets) 71 g_list_free(info->visible_widgets); 72 73 if (info->task) 74 util_source_remove(info->task); 75 76 if (info->icon) 77 g_object_unref(info->icon); 78 79 if (info->icon_file) 80 g_object_unref(info->icon_file); 81 82 g_free(info); 83 84 GNUNET_CHAT_contact_set_user_pointer(contact, NULL); 85 } 86 87 void 88 contact_set_last_message_to_info(const struct GNUNET_CHAT_Contact *contact, 89 void *message) 90 { 91 MESSENGER_ContactInfo* info = GNUNET_CHAT_contact_get_user_pointer(contact); 92 93 if (!info) 94 return; 95 96 info->last_message = message; 97 } 98 99 void* 100 contact_get_last_message_from_info(const struct GNUNET_CHAT_Contact *contact) 101 { 102 MESSENGER_ContactInfo* info = GNUNET_CHAT_contact_get_user_pointer(contact); 103 104 if (!info) 105 return NULL; 106 107 return info->last_message; 108 } 109 110 void 111 contact_add_name_label_to_info(const struct GNUNET_CHAT_Contact *contact, 112 GtkLabel *label) 113 { 114 g_assert(label); 115 116 MESSENGER_ContactInfo* info = GNUNET_CHAT_contact_get_user_pointer(contact); 117 118 if (!info) 119 return; 120 121 const char *name = GNUNET_CHAT_contact_get_name(contact); 122 123 ui_label_set_text(label, name); 124 125 info->name_labels = g_list_append(info->name_labels, label); 126 } 127 128 void 129 contact_remove_name_label_from_info(const struct GNUNET_CHAT_Contact *contact, 130 GtkLabel *label) 131 { 132 g_assert(label); 133 134 MESSENGER_ContactInfo* info = GNUNET_CHAT_contact_get_user_pointer(contact); 135 136 if (!info) 137 return; 138 139 if (info->name_labels) 140 info->name_labels = g_list_remove(info->name_labels, label); 141 } 142 143 void 144 contact_add_name_avatar_to_info(const struct GNUNET_CHAT_Contact *contact, 145 HdyAvatar *avatar) 146 { 147 g_assert(avatar); 148 149 MESSENGER_ContactInfo* info = GNUNET_CHAT_contact_get_user_pointer(contact); 150 151 if (!info) 152 return; 153 154 const char *name = GNUNET_CHAT_contact_get_name(contact); 155 156 ui_avatar_set_text(avatar, name); 157 ui_avatar_set_icon(avatar, info->icon); 158 159 info->name_avatars = g_list_append(info->name_avatars, avatar); 160 } 161 162 void 163 contact_remove_name_avatar_from_info(const struct GNUNET_CHAT_Contact *contact, 164 HdyAvatar *avatar) 165 { 166 g_assert(avatar); 167 168 MESSENGER_ContactInfo* info = GNUNET_CHAT_contact_get_user_pointer(contact); 169 170 if (!info) 171 return; 172 173 if (info->name_avatars) 174 info->name_avatars = g_list_remove(info->name_avatars, avatar); 175 } 176 177 void 178 contact_add_visible_widget_to_info(const struct GNUNET_CHAT_Contact *contact, 179 GtkWidget *widget) 180 { 181 g_assert(widget); 182 183 MESSENGER_ContactInfo* info = GNUNET_CHAT_contact_get_user_pointer(contact); 184 185 if (!info) 186 return; 187 188 gboolean visible = (GNUNET_YES != GNUNET_CHAT_contact_is_blocked(contact)); 189 190 gtk_widget_set_visible(widget, visible); 191 192 info->visible_widgets = g_list_append(info->visible_widgets, widget); 193 } 194 195 void 196 contact_remove_visible_widget_to_info(const struct GNUNET_CHAT_Contact *contact, 197 GtkWidget *widget) 198 { 199 g_assert(widget); 200 201 MESSENGER_ContactInfo* info = GNUNET_CHAT_contact_get_user_pointer(contact); 202 203 if (!info) 204 return; 205 206 if (info->visible_widgets) 207 info->visible_widgets = g_list_remove(info->visible_widgets, widget); 208 } 209 210 void 211 contact_update_info(const struct GNUNET_CHAT_Contact *contact) 212 { 213 MESSENGER_ContactInfo* info = GNUNET_CHAT_contact_get_user_pointer(contact); 214 215 if (!info) 216 return; 217 218 GList* list; 219 const char *name = GNUNET_CHAT_contact_get_name(contact); 220 221 gboolean visible = (GNUNET_YES != GNUNET_CHAT_contact_is_blocked(contact)); 222 223 for (list = info->name_labels; list; list = list->next) 224 ui_label_set_text(GTK_LABEL(list->data), name); 225 226 for (list = info->name_avatars; list; list = list->next) 227 ui_avatar_set_text(HDY_AVATAR(list->data), name); 228 229 for (list = info->name_avatars; list; list = list->next) 230 ui_avatar_set_icon(HDY_AVATAR(list->data), info->icon); 231 232 for (list = info->visible_widgets; list; list = list->next) 233 gtk_widget_set_visible(GTK_WIDGET(list->data), visible); 234 } 235 236 static gboolean 237 _task_update_avatars(gpointer data) 238 { 239 g_assert(data); 240 241 MESSENGER_ContactInfo *info = (MESSENGER_ContactInfo*) data; 242 243 info->task = 0; 244 245 GList* list; 246 for (list = info->name_avatars; list; list = list->next) 247 ui_avatar_set_icon(HDY_AVATAR(list->data), info->icon); 248 249 return FALSE; 250 } 251 252 static void 253 _info_profile_downloaded(void *cls, 254 struct GNUNET_CHAT_File *file, 255 uint64_t completed, 256 uint64_t size) 257 { 258 g_assert((cls) && (file)); 259 260 MESSENGER_ContactInfo* info = (MESSENGER_ContactInfo*) cls; 261 262 if (completed < size) 263 return; 264 265 const char *preview = GNUNET_CHAT_file_open_preview(file); 266 267 if (!preview) 268 return; 269 270 GFile *file_object = g_file_new_for_path(preview); 271 272 if (!file_object) 273 return; 274 275 if (!(info->icon_file)) 276 goto skip_comparison; 277 278 if (g_file_equal(info->icon_file, file_object)) 279 { 280 g_object_unref(file_object); 281 return; 282 } 283 284 g_object_unref(info->icon_file); 285 286 skip_comparison: 287 info->icon_file = file_object; 288 289 if (info->icon) 290 g_object_unref(info->icon); 291 292 info->icon = g_file_icon_new(file_object); 293 294 if (!(info->task)) 295 info->task = util_idle_add(G_SOURCE_FUNC(_task_update_avatars), info); 296 } 297 298 static enum GNUNET_GenericReturnValue 299 _info_iterate_attribute(MESSENGER_ContactInfo* info, 300 struct GNUNET_CHAT_Handle *handle, 301 struct GNUNET_CHAT_Contact *contact, 302 const char *name, 303 const char *value) 304 { 305 g_assert((info) && (handle) && (contact) && (name)); 306 307 if ((0 != strcmp(name, GNUNET_CHAT_ATTRIBUTE_AVATAR)) || (!value)) 308 return GNUNET_YES; 309 310 struct GNUNET_CHAT_Uri *uri = GNUNET_CHAT_uri_parse(value, NULL); 311 312 if (!uri) 313 return GNUNET_YES; 314 315 struct GNUNET_CHAT_File *file = GNUNET_CHAT_request_file(handle, uri); 316 317 if (!file) 318 goto skip_file; 319 320 if (GNUNET_YES == GNUNET_CHAT_file_is_ready(file)) 321 _info_profile_downloaded( 322 info, 323 file, 324 GNUNET_CHAT_file_get_local_size(file), 325 GNUNET_CHAT_file_get_size(file) 326 ); 327 else if (GNUNET_YES != GNUNET_CHAT_file_is_downloading(file)) 328 GNUNET_CHAT_file_start_download( 329 file, 330 _info_profile_downloaded, 331 info 332 ); 333 334 skip_file: 335 GNUNET_CHAT_uri_destroy(uri); 336 return GNUNET_YES; 337 } 338 339 static enum GNUNET_GenericReturnValue 340 _handle_iterate_attribute(void *cls, 341 struct GNUNET_CHAT_Handle *handle, 342 const char *name, 343 const char *value) 344 { 345 g_assert((cls) && (handle) && (name)); 346 347 struct GNUNET_CHAT_Contact *contact = (struct GNUNET_CHAT_Contact*) cls; 348 349 MESSENGER_ContactInfo* info = GNUNET_CHAT_contact_get_user_pointer(contact); 350 351 if (!info) 352 return GNUNET_NO; 353 354 return _info_iterate_attribute( 355 info, 356 handle, 357 contact, 358 name, 359 value 360 ); 361 } 362 363 static enum GNUNET_GenericReturnValue 364 _contact_iterate_attribute(void *cls, 365 struct GNUNET_CHAT_Contact *contact, 366 const char *name, 367 const char *value) 368 { 369 g_assert((cls) && (contact) && (name)); 370 371 struct GNUNET_CHAT_Handle *handle = (struct GNUNET_CHAT_Handle*) cls; 372 373 MESSENGER_ContactInfo* info = GNUNET_CHAT_contact_get_user_pointer(contact); 374 375 if (!info) 376 return GNUNET_NO; 377 378 return _info_iterate_attribute( 379 info, 380 handle, 381 contact, 382 name, 383 value 384 ); 385 } 386 387 void 388 contact_update_attributes(struct GNUNET_CHAT_Contact *contact, 389 MESSENGER_Application *app) 390 { 391 g_assert(app); 392 393 MESSENGER_ContactInfo* info = GNUNET_CHAT_contact_get_user_pointer(contact); 394 395 if (!info) 396 return; 397 398 if (GNUNET_YES == GNUNET_CHAT_contact_is_owned(contact)) 399 GNUNET_CHAT_get_attributes( 400 app->chat.messenger.handle, 401 _handle_iterate_attribute, 402 contact 403 ); 404 else 405 GNUNET_CHAT_contact_get_attributes( 406 contact, 407 _contact_iterate_attribute, 408 app->chat.messenger.handle 409 ); 410 }