event.c (25701B)
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 event.c 23 */ 24 25 #include "event.h" 26 27 #include "account.h" 28 #include "application.h" 29 #include "contact.h" 30 #include "discourse.h" 31 #include "file.h" 32 #include "ui.h" 33 34 #include "ui/chat_entry.h" 35 #include "ui/chat_title.h" 36 #include "ui/message.h" 37 38 #include <glib-2.0/glib.h> 39 #include <gnunet/gnunet_chat_lib.h> 40 #include <gnunet/gnunet_common.h> 41 #include <stdio.h> 42 43 static void 44 _close_notification(NotifyNotification* notification, 45 gpointer user_data) 46 { 47 g_assert((notification) && (user_data)); 48 49 MESSENGER_Application *app = (MESSENGER_Application*) user_data; 50 51 app->notifications = g_list_remove(app->notifications, notification); 52 53 notify_notification_clear_actions(notification); 54 notify_notification_clear_hints(notification); 55 56 g_object_unref(notification); 57 } 58 59 static void 60 _show_notification(MESSENGER_Application *app, 61 UNUSED struct GNUNET_CHAT_Context *context, 62 const struct GNUNET_CHAT_Contact *contact, 63 const char *text, 64 const char *icon, 65 const char *category) 66 { 67 g_assert( 68 (app) && 69 (text) && 70 (icon) && 71 (category) 72 ); 73 74 if (app->settings.disable_notifications) 75 return; 76 77 const char *sender = GNUNET_CHAT_contact_get_name(contact); 78 79 NotifyNotification *notification = notify_notification_new( 80 sender? sender : "(unknown)", text, icon 81 ); 82 83 if (!notification) 84 return; 85 86 app->notifications = g_list_append(app->notifications, notification); 87 88 notify_notification_set_category(notification, category); 89 90 g_signal_connect( 91 notification, 92 "closed", 93 G_CALLBACK(_close_notification), 94 app 95 ); 96 97 notify_notification_show(notification, NULL); 98 99 if (app->settings.play_notification_sounds) 100 { 101 fprintf(stdout, "\a"); 102 fflush(stdout); 103 } 104 } 105 106 void 107 event_handle_warning(MESSENGER_Application *app, 108 struct GNUNET_CHAT_Context *context, 109 struct GNUNET_CHAT_Message *msg) 110 { 111 g_assert((app) && (msg)); 112 113 const char *text = GNUNET_CHAT_message_get_text(msg); 114 115 const struct GNUNET_CHAT_Contact *contact = GNUNET_CHAT_message_get_sender( 116 msg 117 ); 118 119 g_printerr("ERROR: %s\n", text); 120 121 _show_notification( 122 app, 123 context, 124 contact, 125 text, 126 "dialog-warning-symbolic", 127 "im.error" 128 ); 129 } 130 131 static enum GNUNET_GenericReturnValue 132 _iterate_reload_account(void *cls, 133 UNUSED struct GNUNET_CHAT_Handle *handle, 134 struct GNUNET_CHAT_Account *account) 135 { 136 g_assert((cls) && (account)); 137 138 MESSENGER_Application *app = (MESSENGER_Application*) cls; 139 140 account_create_info(account); 141 account_update_attributes(account, app); 142 143 return GNUNET_YES; 144 } 145 146 void 147 event_refresh_accounts(MESSENGER_Application *app) 148 { 149 g_assert(app); 150 151 UI_MESSENGER_Handle *ui = &(app->ui.messenger); 152 CHAT_MESSENGER_Handle *chat = &(app->chat.messenger); 153 const struct GNUNET_CHAT_Account *account; 154 155 GNUNET_CHAT_iterate_accounts( 156 chat->handle, 157 _iterate_reload_account, 158 app 159 ); 160 161 switch (app->ui.state) 162 { 163 case MESSENGER_STATE_ACCOUNTS: 164 ui_accounts_dialog_refresh(app, &(app->ui.accounts)); 165 break; 166 case MESSENGER_STATE_MAIN_WINDOW: 167 ui_messenger_refresh(app, ui); 168 break; 169 default: 170 break; 171 } 172 173 account = GNUNET_CHAT_get_connected(chat->handle); 174 175 if (account) 176 application_show_window(app); 177 } 178 179 static gboolean 180 _select_chat_to_activate(gpointer user_data) 181 { 182 g_assert(user_data); 183 184 UI_CHAT_ENTRY_Handle *entry = (UI_CHAT_ENTRY_Handle*) user_data; 185 186 if ((!entry) || (!(entry->chat)) || (!(entry->entry_box))) 187 return FALSE; 188 189 MESSENGER_Application *app = entry->chat->app; 190 191 if (!app) 192 return FALSE; 193 194 GtkListBoxRow *row = GTK_LIST_BOX_ROW( 195 gtk_widget_get_parent(entry->entry_box) 196 ); 197 198 if (!row) 199 return FALSE; 200 201 UI_MESSENGER_Handle *ui = &(app->ui.messenger); 202 203 gtk_list_box_select_row(ui->chats_listbox, row); 204 gtk_list_box_invalidate_filter(ui->chats_listbox); 205 206 gtk_widget_activate(GTK_WIDGET(row)); 207 208 ui->chat_selection = 0; 209 return FALSE; 210 } 211 212 static gboolean 213 _idle_chat_entry_update(gpointer user_data) 214 { 215 g_assert(user_data); 216 217 UI_CHAT_ENTRY_Handle *entry = (UI_CHAT_ENTRY_Handle*) user_data; 218 219 if ((!(entry->chat)) || (!(entry->chat->app)) || 220 (!(entry->chat->send_text_view))) 221 goto update_exit; 222 223 ui_chat_entry_update(entry, entry->chat->app); 224 225 update_exit: 226 entry->update = 0; 227 return FALSE; 228 } 229 230 static void 231 enqueue_chat_entry_update(UI_CHAT_ENTRY_Handle *entry) 232 { 233 g_assert(entry); 234 235 if (entry->update) 236 util_source_remove(entry->update); 237 238 entry->update = util_idle_add( 239 G_SOURCE_FUNC(_idle_chat_entry_update), 240 entry 241 ); 242 } 243 244 static void 245 _add_new_chat_entry(MESSENGER_Application *app, 246 struct GNUNET_CHAT_Context *context) 247 { 248 g_assert((app) && (context)); 249 250 UI_MESSENGER_Handle *ui = &(app->ui.messenger); 251 UI_CHAT_ENTRY_Handle *entry = ui_chat_entry_new(app, context); 252 253 enqueue_chat_entry_update(entry); 254 255 gtk_container_add(GTK_CONTAINER(ui->chats_listbox), entry->entry_box); 256 257 gtk_container_add( 258 GTK_CONTAINER(ui->chats_stack), 259 entry->chat->chat_box 260 ); 261 262 gtk_container_add( 263 GTK_CONTAINER(ui->chat_title_stack), 264 entry->chat->title->chat_title_box 265 ); 266 267 ui->chat_entries = g_list_append(ui->chat_entries, entry); 268 269 GtkWidget *row = gtk_widget_get_parent(entry->entry_box); 270 271 g_object_set_qdata( 272 G_OBJECT(row), 273 app->quarks.ui, 274 entry 275 ); 276 277 if (ui->chat_selection) 278 util_source_remove(ui->chat_selection); 279 280 ui->chat_selection = util_idle_add( 281 G_SOURCE_FUNC(_select_chat_to_activate), 282 entry 283 ); 284 } 285 286 static int 287 _iterate_profile_contacts(void *cls, 288 UNUSED struct GNUNET_CHAT_Handle *handle, 289 struct GNUNET_CHAT_Contact *contact) 290 { 291 g_assert((cls) && (contact)); 292 293 MESSENGER_Application *app = (MESSENGER_Application*) cls; 294 295 struct GNUNET_CHAT_Context *context = GNUNET_CHAT_contact_get_context( 296 contact 297 ); 298 299 if (GNUNET_SYSERR == GNUNET_CHAT_context_get_status(context)) 300 return GNUNET_YES; 301 302 _add_new_chat_entry(app, context); 303 return GNUNET_YES; 304 } 305 306 static int 307 _iterate_profile_groups(void *cls, 308 UNUSED struct GNUNET_CHAT_Handle *handle, 309 UNUSED struct GNUNET_CHAT_Group *group) 310 { 311 g_assert(cls); 312 313 MESSENGER_Application *app = (MESSENGER_Application*) cls; 314 315 struct GNUNET_CHAT_Context *context = GNUNET_CHAT_group_get_context( 316 group 317 ); 318 319 if (GNUNET_SYSERR == GNUNET_CHAT_context_get_status(context)) 320 return GNUNET_YES; 321 322 _add_new_chat_entry(app, GNUNET_CHAT_group_get_context(group)); 323 return GNUNET_YES; 324 } 325 326 void 327 event_update_profile(MESSENGER_Application *app) 328 { 329 g_assert(app); 330 331 if (MESSENGER_STATE_NEW_ACCOUNT == app->ui.state) 332 { 333 app->ui.state = MESSENGER_STATE_MAIN_WINDOW; 334 335 ui_new_account_dialog_update(app, &(app->ui.new_account)); 336 } 337 338 UI_MESSENGER_Handle *ui = &(app->ui.messenger); 339 CHAT_MESSENGER_Handle *chat = &(app->chat.messenger); 340 341 event_refresh_accounts(app); 342 343 const char *name = GNUNET_CHAT_get_name(chat->handle); 344 345 account_switch_name_avatar_to_info( 346 GNUNET_CHAT_get_connected(chat->handle), 347 ui->profile_avatar 348 ); 349 350 ui_label_set_text(ui->profile_label, name); 351 352 const char *key = GNUNET_CHAT_get_key(chat->handle); 353 354 ui_label_set_text(ui->profile_key_label, key); 355 356 gtk_stack_set_visible_child(ui->chats_stack, ui->no_chat_box); 357 358 GList *children = gtk_container_get_children(GTK_CONTAINER(ui->leaflet_chat)); 359 360 if (children) { 361 hdy_leaflet_set_visible_child(ui->leaflet_chat, GTK_WIDGET(children->data)); 362 g_list_free(children); 363 } 364 365 GNUNET_CHAT_iterate_contacts(chat->handle, _iterate_profile_contacts, app); 366 GNUNET_CHAT_iterate_groups(chat->handle, _iterate_profile_groups, app); 367 } 368 369 static void 370 _clear_chat_entry(GtkWidget *widget, 371 gpointer user_data) 372 { 373 g_assert((widget) && (user_data)); 374 375 MESSENGER_Application *app = (MESSENGER_Application*) user_data; 376 UI_MESSENGER_Handle *ui = &(app->ui.messenger); 377 378 GtkListBoxRow *row = GTK_LIST_BOX_ROW(widget); 379 380 if (!gtk_list_box_row_get_selectable(row)) 381 return; 382 383 UI_CHAT_ENTRY_Handle *entry = (UI_CHAT_ENTRY_Handle*) g_object_get_qdata( 384 G_OBJECT(row), 385 app->quarks.ui 386 ); 387 388 ui->chat_entries = g_list_remove(ui->chat_entries, entry); 389 390 ui_chat_entry_dispose(entry, app); 391 } 392 393 static int 394 _cleanup_profile_contacts(void *cls, 395 UNUSED struct GNUNET_CHAT_Handle *handle, 396 struct GNUNET_CHAT_Contact *contact) 397 { 398 if (contact) 399 contact_destroy_info(contact); 400 return GNUNET_YES; 401 } 402 403 static int 404 _cleanup_profile_files(void *cls, 405 UNUSED struct GNUNET_CHAT_Handle *handle, 406 struct GNUNET_CHAT_File *file) 407 { 408 if (file) 409 file_destroy_info(file); 410 return GNUNET_YES; 411 } 412 413 void 414 event_cleanup_profile(MESSENGER_Application *app) 415 { 416 g_assert(app); 417 418 UI_MESSENGER_Handle *ui = &(app->ui.messenger); 419 CHAT_MESSENGER_Handle *chat = &(app->chat.messenger); 420 421 account_remove_name_avatar_from_info( 422 GNUNET_CHAT_get_connected(chat->handle), 423 ui->profile_avatar 424 ); 425 426 GList *entries = gtk_container_get_children( 427 GTK_CONTAINER(ui->chats_listbox) 428 ); 429 430 g_list_foreach(entries, (GFunc) _clear_chat_entry, app); 431 g_list_free(entries); 432 433 GNUNET_CHAT_iterate_contacts(chat->handle, _cleanup_profile_contacts, NULL); 434 GNUNET_CHAT_iterate_files(chat->handle, _cleanup_profile_files, NULL); 435 } 436 437 void 438 event_select_profile(MESSENGER_Application *app, 439 struct GNUNET_CHAT_Context *context, 440 struct GNUNET_CHAT_Message *msg) 441 { 442 g_assert((app) && (!context) && (msg)); 443 444 CHAT_MESSENGER_Handle *chat = &(app->chat.messenger); 445 446 struct GNUNET_CHAT_Account *account = GNUNET_CHAT_message_get_account(msg); 447 448 if (GNUNET_CHAT_KIND_CREATED_ACCOUNT == GNUNET_CHAT_message_get_kind(msg)) 449 GNUNET_CHAT_connect(chat->handle, account); 450 } 451 452 gboolean 453 _delayed_context_drop(gpointer user_data) 454 { 455 g_assert(user_data); 456 457 struct GNUNET_CHAT_Context *context = (struct GNUNET_CHAT_Context*) user_data; 458 459 // TODO: schedule_sync_lock(&(app->chat.schedule)); 460 461 struct GNUNET_CHAT_Group *group = GNUNET_CHAT_context_get_group(context); 462 struct GNUNET_CHAT_Contact *contact = GNUNET_CHAT_context_get_contact(context); 463 464 if (group) 465 GNUNET_CHAT_group_leave(group); 466 else if (contact) 467 GNUNET_CHAT_contact_delete(contact); 468 469 // TODO: schedule_sync_unlock(&(app->chat.schedule)); 470 471 return FALSE; 472 } 473 474 void 475 event_update_chats(MESSENGER_Application *app, 476 struct GNUNET_CHAT_Context *context, 477 struct GNUNET_CHAT_Message *msg) 478 { 479 g_assert((app) && (context) && (msg)); 480 481 UI_CHAT_ENTRY_Handle *handle = GNUNET_CHAT_context_get_user_pointer(context); 482 483 const enum GNUNET_CHAT_MessageKind kind = GNUNET_CHAT_message_get_kind( 484 msg 485 ); 486 487 if (GNUNET_CHAT_KIND_JOIN == kind) 488 { 489 if (!handle) 490 _add_new_chat_entry(app, context); 491 else 492 enqueue_chat_entry_update(handle); 493 494 if (app->settings.leave_chats_delay > 0) 495 util_timeout_add_seconds( 496 app->settings.leave_chats_delay, 497 _delayed_context_drop, 498 context 499 ); 500 } 501 else if ((handle) && (handle->entry_box)) 502 _clear_chat_entry(gtk_widget_get_parent(handle->entry_box), app); 503 504 struct GNUNET_CHAT_Contact *contact = GNUNET_CHAT_message_get_sender( 505 msg 506 ); 507 508 if (!contact) 509 return; 510 511 if (GNUNET_YES == contact_create_info(contact)) 512 contact_update_attributes(contact, app); 513 } 514 515 static void 516 _update_contact_context(struct GNUNET_CHAT_Contact *contact) 517 { 518 g_assert(contact); 519 520 struct GNUNET_CHAT_Context *context = GNUNET_CHAT_contact_get_context( 521 contact 522 ); 523 524 if (!context) 525 return; 526 527 UI_CHAT_ENTRY_Handle *handle = GNUNET_CHAT_context_get_user_pointer(context); 528 529 if (!handle) 530 return; 531 532 enqueue_chat_entry_update(handle); 533 } 534 535 void 536 event_presence_contact(MESSENGER_Application *app, 537 struct GNUNET_CHAT_Context *context, 538 struct GNUNET_CHAT_Message *msg) 539 { 540 g_assert((app) && (context) && (msg)); 541 542 UI_CHAT_ENTRY_Handle *handle = GNUNET_CHAT_context_get_user_pointer(context); 543 544 struct GNUNET_CHAT_Contact *contact = GNUNET_CHAT_message_get_sender( 545 msg 546 ); 547 548 if (!contact) 549 return; 550 551 contact_create_info(contact); 552 553 if (!handle) 554 return; 555 556 struct GNUNET_CHAT_Group *group = GNUNET_CHAT_context_get_group(context); 557 558 UI_MESSAGE_Handle *message = NULL; 559 gboolean previous_presence = FALSE; 560 561 if (group) 562 message = (UI_MESSAGE_Handle*) ( 563 GNUNET_CHAT_member_get_user_pointer(group, contact) 564 ); 565 else 566 message = (UI_MESSAGE_Handle*) contact_get_last_message_from_info(contact); 567 568 if (message) 569 { 570 ui_chat_remove_message(handle->chat, app, message); 571 previous_presence = TRUE; 572 } 573 574 message = ui_message_new(app, UI_MESSAGE_STATUS); 575 ui_message_update(message, app, msg); 576 577 contact_update_attributes(contact, app); 578 _update_contact_context(contact); 579 580 ui_message_set_contact(message, contact); 581 582 const enum GNUNET_CHAT_MessageKind kind = GNUNET_CHAT_message_get_kind( 583 msg 584 ); 585 586 const char *text = ( 587 GNUNET_CHAT_KIND_JOIN == kind? _("joined the chat") : _("left the chat") 588 ); 589 590 if ((!ui_messenger_is_context_active(&(app->ui.messenger), context)) && 591 ((!previous_presence) || (GNUNET_CHAT_KIND_LEAVE == kind)) && 592 (GNUNET_YES == GNUNET_CHAT_message_is_recent(msg))) 593 _show_notification( 594 app, 595 context, 596 contact, 597 text, 598 "avatar-default-symbolic", 599 "presence.online" 600 ); 601 602 char time [20]; 603 time_t timestamp = GNUNET_CHAT_message_get_timestamp( 604 msg 605 ); 606 607 strftime(time, 20, "%Y-%m-%d %H:%M:%S", localtime(×tamp)); 608 609 ui_label_set_text(message->text_label, text); 610 ui_label_set_text(message->timestamp_label, time); 611 612 ui_chat_add_message(handle->chat, app, message); 613 614 if (group) 615 GNUNET_CHAT_member_set_user_pointer(group, contact, message); 616 else 617 contact_set_last_message_to_info(contact, message); 618 619 enqueue_chat_entry_update(handle); 620 } 621 622 void 623 event_update_contacts(UNUSED MESSENGER_Application *app, 624 struct GNUNET_CHAT_Context *context, 625 struct GNUNET_CHAT_Message *msg) 626 { 627 g_assert((app) && (msg)); 628 629 struct GNUNET_CHAT_Contact *contact = GNUNET_CHAT_message_get_sender( 630 msg 631 ); 632 633 if (!contact) 634 return; 635 636 if (GNUNET_CHAT_KIND_SHARED_ATTRIBUTES == GNUNET_CHAT_message_get_kind(msg)) 637 contact_update_attributes(contact, app); 638 639 contact_update_info(contact); 640 _update_contact_context(contact); 641 642 if (!context) 643 return; 644 645 UI_CHAT_ENTRY_Handle *handle = GNUNET_CHAT_context_get_user_pointer(context); 646 647 if (!handle) 648 return; 649 650 enqueue_chat_entry_update(handle); 651 } 652 653 static void 654 _event_invitation_action(MESSENGER_Application *app, 655 gboolean status, 656 gpointer user_data) 657 { 658 g_assert((app) && (user_data)); 659 660 struct GNUNET_CHAT_Invitation *invitation = ( 661 (struct GNUNET_CHAT_Invitation*) user_data 662 ); 663 664 application_chat_lock(app); 665 666 if (status) 667 GNUNET_CHAT_invitation_accept(invitation); 668 else 669 GNUNET_CHAT_invitation_reject(invitation); 670 671 application_chat_unlock(app); 672 } 673 674 void 675 event_invitation(MESSENGER_Application *app, 676 struct GNUNET_CHAT_Context *context, 677 struct GNUNET_CHAT_Message *msg) 678 { 679 g_assert((app) && (context) && (msg)); 680 681 UI_CHAT_ENTRY_Handle *handle = GNUNET_CHAT_context_get_user_pointer(context); 682 683 if (!handle) 684 return; 685 686 struct GNUNET_CHAT_Invitation *invitation; 687 invitation = GNUNET_CHAT_message_get_invitation(msg); 688 689 if (!invitation) 690 return; 691 692 UI_MESSAGE_Handle *message = GNUNET_CHAT_message_get_user_pointer(msg); 693 enum GNUNET_GenericReturnValue new_message = GNUNET_NO; 694 695 if (!message) 696 { 697 if (app->settings.delete_invitations_delay > 0) 698 GNUNET_CHAT_message_delete( 699 msg, 700 app->settings.delete_invitations_delay 701 ); 702 703 const enum GNUNET_GenericReturnValue sent = 704 GNUNET_CHAT_message_is_sent(msg); 705 706 if ((GNUNET_YES != sent) && (app->settings.send_read_receipts)) 707 GNUNET_CHAT_context_send_read_receipt(context, msg); 708 709 message = ui_message_new(app, UI_MESSAGE_STATUS); 710 new_message = GNUNET_YES; 711 } 712 713 if (!message) 714 return; 715 716 ui_message_update(message, app, msg); 717 718 if (GNUNET_YES == new_message) 719 { 720 struct GNUNET_CHAT_Contact *sender = GNUNET_CHAT_message_get_sender( 721 msg 722 ); 723 724 struct GNUNET_CHAT_Contact *recipient = GNUNET_CHAT_message_get_recipient( 725 msg 726 ); 727 728 ui_message_set_contact(message, sender); 729 730 const char *invite_message = ( 731 GNUNET_YES != GNUNET_CHAT_invitation_is_direct(invitation) 732 )? _("invited %s to a chat") : _("requested %s to chat"); 733 734 const char *recipient_name = ( 735 (recipient) && 736 (GNUNET_YES != GNUNET_CHAT_contact_is_owned(recipient)) 737 )? GNUNET_CHAT_contact_get_name(recipient) : _("you"); 738 739 GString *message_string = g_string_new(NULL); 740 g_string_printf(message_string, invite_message, recipient_name); 741 742 if ((!ui_messenger_is_context_active(&(app->ui.messenger), context)) && 743 (GNUNET_YES == GNUNET_CHAT_message_is_recent(msg))) 744 _show_notification( 745 app, 746 context, 747 sender, 748 message_string->str, 749 "mail-message-new-symbolic", 750 "im.received" 751 ); 752 753 ui_label_set_text(message->text_label, message_string->str); 754 g_string_free(message_string, TRUE); 755 756 ui_message_set_status_callback( 757 message, _event_invitation_action, invitation 758 ); 759 760 ui_chat_add_message(handle->chat, app, message); 761 } 762 763 enqueue_chat_entry_update(handle); 764 } 765 766 void 767 event_receive_message(MESSENGER_Application *app, 768 struct GNUNET_CHAT_Context *context, 769 struct GNUNET_CHAT_Message *msg) 770 { 771 g_assert((app) && (context) && (msg)); 772 773 UI_CHAT_ENTRY_Handle *handle = GNUNET_CHAT_context_get_user_pointer(context); 774 775 if (!handle) 776 return; 777 778 const int sent = GNUNET_CHAT_message_is_sent(msg); 779 780 if ((sent) && (app->settings.auto_delete_delay > 0)) 781 GNUNET_CHAT_message_delete( 782 msg, 783 app->settings.auto_delete_delay 784 ); 785 786 const gchar *text = GNUNET_CHAT_message_get_text(msg); 787 788 if ((text) && (!(*text))) 789 goto skip_message; 790 791 if ((GNUNET_YES != sent) && (app->settings.send_read_receipts)) 792 GNUNET_CHAT_context_send_read_receipt(context, msg); 793 794 const UI_MESSAGE_Type type = ( 795 GNUNET_YES == sent? UI_MESSAGE_SENT : UI_MESSAGE_DEFAULT 796 ); 797 798 UI_MESSAGE_Handle *message = ui_message_new(app, type); 799 800 struct GNUNET_CHAT_File *file = GNUNET_CHAT_message_get_file(msg); 801 802 if (file) 803 { 804 file_create_info(file); 805 file_add_ui_message_to_info(file, message); 806 807 if (app->settings.delete_files_delay > 0) 808 GNUNET_CHAT_message_delete( 809 msg, 810 app->settings.delete_files_delay 811 ); 812 } 813 814 ui_message_update(message, app, msg); 815 816 struct GNUNET_CHAT_Contact *contact = GNUNET_CHAT_message_get_sender( 817 msg 818 ); 819 820 ui_message_set_contact(message, contact); 821 822 char time [20]; 823 time_t timestamp = GNUNET_CHAT_message_get_timestamp( 824 msg 825 ); 826 827 strftime(time, 20, "%Y-%m-%d %H:%M:%S", localtime(×tamp)); 828 829 if ((!ui_messenger_is_context_active(&(app->ui.messenger), context)) && 830 (GNUNET_YES == GNUNET_CHAT_message_is_recent(msg)) && 831 (GNUNET_YES != sent)) 832 _show_notification( 833 app, 834 context, 835 contact, 836 text, 837 "mail-unread-symbolic", 838 "im.received" 839 ); 840 841 ui_label_set_markup_text(message->text_label, text); 842 ui_label_set_text(message->timestamp_label, time); 843 844 ui_chat_add_message(handle->chat, app, message); 845 846 skip_message: 847 enqueue_chat_entry_update(handle); 848 } 849 850 static UI_MESSAGE_Handle* 851 _find_ui_message_handle(MESSENGER_Application *app, 852 struct GNUNET_CHAT_Context *context, 853 struct GNUNET_CHAT_Message *msg) 854 { 855 g_assert((app) && (context) && (msg)); 856 857 UI_CHAT_ENTRY_Handle *handle = GNUNET_CHAT_context_get_user_pointer(context); 858 859 if ((!handle) || (!(handle->chat))) 860 return NULL; 861 862 UI_MESSAGE_Handle *message = GNUNET_CHAT_message_get_user_pointer(msg); 863 864 if (!message) 865 { 866 GList *rows = gtk_container_get_children( 867 GTK_CONTAINER(handle->chat->messages_listbox) 868 ); 869 870 for (GList *row = rows; row; row = row->next) 871 { 872 message = (UI_MESSAGE_Handle*) g_object_get_qdata( 873 G_OBJECT(row->data), app->quarks.ui 874 ); 875 876 if ((message) && (message->msg == msg)) 877 break; 878 879 message = NULL; 880 } 881 882 if (rows) 883 g_list_free(rows); 884 } 885 886 return message; 887 } 888 889 static void 890 _event_update_tag_message_state(MESSENGER_Application *app, 891 struct GNUNET_CHAT_Context *context, 892 struct GNUNET_CHAT_Message *msg) 893 { 894 g_assert((msg) && (GNUNET_CHAT_KIND_TAG == GNUNET_CHAT_message_get_kind(msg))); 895 896 struct GNUNET_CHAT_Message *target; 897 target = GNUNET_CHAT_message_get_target(msg); 898 899 if (!target) 900 return; 901 902 struct GNUNET_CHAT_Contact *contact; 903 contact = GNUNET_CHAT_message_get_sender(target); 904 905 if (contact) 906 contact_update_info(contact); 907 908 UI_CHAT_ENTRY_Handle *handle = GNUNET_CHAT_context_get_user_pointer(context); 909 910 if ((!handle) || (!(handle->chat))) 911 return; 912 913 UI_MESSAGE_Handle *message = _find_ui_message_handle(app, context, target); 914 915 if (!message) 916 return; 917 918 if (GNUNET_YES == GNUNET_CHAT_message_is_deleted(msg)) 919 ui_message_remove_tag(message, app, msg); 920 else 921 ui_message_add_tag(message, app, msg); 922 } 923 924 void 925 event_delete_message(MESSENGER_Application *app, 926 struct GNUNET_CHAT_Context *context, 927 struct GNUNET_CHAT_Message *msg) 928 { 929 g_assert((app) && (context) && (msg)); 930 931 UI_CHAT_ENTRY_Handle *handle = GNUNET_CHAT_context_get_user_pointer(context); 932 933 if ((!handle) || (!(handle->chat))) 934 return; 935 936 UI_MESSAGE_Handle *message = _find_ui_message_handle(app, context, msg); 937 938 if (message) 939 ui_chat_remove_message(handle->chat, app, message); 940 941 if (GNUNET_CHAT_KIND_TAG == GNUNET_CHAT_message_get_kind(msg)) 942 _event_update_tag_message_state(app, context, msg); 943 944 enqueue_chat_entry_update(handle); 945 } 946 947 void 948 event_tag_message(MESSENGER_Application *app, 949 struct GNUNET_CHAT_Context *context, 950 struct GNUNET_CHAT_Message *msg) 951 { 952 g_assert((app) && (context) && (msg)); 953 954 UI_CHAT_ENTRY_Handle *handle = GNUNET_CHAT_context_get_user_pointer(context); 955 956 struct GNUNET_CHAT_Message *target = GNUNET_CHAT_message_get_target(msg); 957 958 _event_update_tag_message_state(app, context, msg); 959 960 if ((!handle) || (!(handle->chat))) 961 return; 962 963 UI_MESSAGE_Handle *message = _find_ui_message_handle(app, context, target); 964 965 if (message) 966 ui_message_update(message, app, message->msg); 967 968 enqueue_chat_entry_update(handle); 969 } 970 971 static enum GNUNET_GenericReturnValue 972 _iterate_contacts_update_own(void *cls, 973 UNUSED struct GNUNET_CHAT_Handle *handle, 974 struct GNUNET_CHAT_Contact *contact) 975 { 976 g_assert((cls) && (contact)); 977 978 MESSENGER_Application *app = (MESSENGER_Application*) cls; 979 980 if (GNUNET_YES != GNUNET_CHAT_contact_is_owned(contact)) 981 return GNUNET_YES; 982 983 contact_update_attributes(contact, app); 984 return GNUNET_YES; 985 } 986 987 void 988 event_update_attributes(MESSENGER_Application *app) 989 { 990 g_assert(app); 991 992 CHAT_MESSENGER_Handle *chat = &(app->chat.messenger); 993 994 struct GNUNET_CHAT_Account *account = GNUNET_CHAT_get_connected( 995 chat->handle 996 ); 997 998 if (account) 999 account_update_attributes(account, app); 1000 1001 GNUNET_CHAT_iterate_contacts( 1002 chat->handle, 1003 _iterate_contacts_update_own, 1004 app 1005 ); 1006 } 1007 1008 void 1009 event_discourse(MESSENGER_Application *app, 1010 struct GNUNET_CHAT_Context *context, 1011 struct GNUNET_CHAT_Message *msg) 1012 { 1013 g_assert((app) && (context) && (msg)); 1014 1015 struct GNUNET_CHAT_Discourse *discourse = GNUNET_CHAT_message_get_discourse( 1016 msg 1017 ); 1018 1019 if (!discourse) 1020 return; 1021 1022 if (GNUNET_YES == GNUNET_CHAT_message_is_sent(msg)) 1023 { 1024 if (GNUNET_YES == GNUNET_CHAT_discourse_is_open(discourse)) 1025 discourse_create_info(discourse); 1026 else 1027 discourse_destroy_info(discourse); 1028 } 1029 1030 discourse_update_subscriptions(discourse); 1031 1032 if (context == app->ui.discourse.context) 1033 ui_discourse_window_update(&(app->ui.discourse), context); 1034 } 1035 1036 void 1037 event_discourse_data(MESSENGER_Application *app, 1038 struct GNUNET_CHAT_Context *context, 1039 struct GNUNET_CHAT_Message *msg) 1040 { 1041 g_assert((app) && (context) && (msg)); 1042 1043 struct GNUNET_CHAT_Discourse *discourse = GNUNET_CHAT_message_get_discourse( 1044 msg 1045 ); 1046 1047 if (!discourse) 1048 return; 1049 1050 discourse_stream_message(discourse, msg); 1051 }