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