gnunet_chat_handle.c (26674B)
1 /* 2 This file is part of GNUnet. 3 Copyright (C) 2021--2025 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 gnunet_chat_handle.c 23 */ 24 25 #include "gnunet_chat_handle.h" 26 27 #include "gnunet_chat_account.h" 28 #include "gnunet_chat_handle_intern.c" 29 #include "gnunet_chat_message.h" 30 #include <gnunet/gnunet_arm_service.h> 31 #include <gnunet/gnunet_common.h> 32 #include <gnunet/gnunet_messenger_service.h> 33 #include <gnunet/gnunet_reclaim_service.h> 34 #include <gnunet/gnunet_scheduler_lib.h> 35 #include <gnunet/gnunet_util_lib.h> 36 37 static const unsigned int initial_map_size_of_handle = 8; 38 static const unsigned int minimum_amount_of_other_members_in_group = 2; 39 40 struct GNUNET_CHAT_Handle* 41 handle_create_from_config (const struct GNUNET_CONFIGURATION_Handle* cfg, 42 GNUNET_CHAT_ContextMessageCallback msg_cb, 43 void *msg_cls) 44 { 45 GNUNET_assert(cfg); 46 47 struct GNUNET_CHAT_Handle* handle = GNUNET_new(struct GNUNET_CHAT_Handle); 48 49 handle->cfg = cfg; 50 handle->shutdown_hook = GNUNET_SCHEDULER_add_shutdown( 51 on_handle_shutdown, handle 52 ); 53 54 handle->destruction = NULL; 55 56 handle->services_head = NULL; 57 handle->services_tail = NULL; 58 59 handle->internal_head = NULL; 60 handle->internal_tail = NULL; 61 62 handle->directory = NULL; 63 64 char *dir_path = NULL; 65 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename(cfg, 66 GNUNET_MESSENGER_SERVICE_NAME, 67 "MESSENGER_DIR", 68 &dir_path)) 69 { 70 if (dir_path) 71 GNUNET_free(dir_path); 72 } 73 else if ((GNUNET_YES != GNUNET_DISK_directory_test(dir_path, GNUNET_YES)) && 74 (GNUNET_OK != GNUNET_DISK_directory_create(dir_path))) 75 { 76 GNUNET_free(dir_path); 77 } 78 else 79 { 80 char *chat_directory = NULL; 81 util_get_dirname(dir_path, "chat", &chat_directory); 82 GNUNET_free(dir_path); 83 84 if ((GNUNET_YES != GNUNET_DISK_directory_test(chat_directory, GNUNET_YES)) && 85 (GNUNET_OK != GNUNET_DISK_directory_create(chat_directory))) 86 GNUNET_free(chat_directory); 87 else 88 handle->directory = chat_directory; 89 } 90 91 handle->msg_cb = msg_cb; 92 handle->msg_cls = msg_cls; 93 94 handle->accounts_head = NULL; 95 handle->accounts_tail = NULL; 96 97 handle->refreshing = GNUNET_NO; 98 handle->own_contact = NULL; 99 100 handle->next = NULL; 101 handle->current = NULL; 102 handle->monitor = NULL; 103 104 handle->lobbies_head = NULL; 105 handle->lobbies_tail = NULL; 106 107 handle->lookups_head = NULL; 108 handle->lookups_tail = NULL; 109 110 handle->attributes_head = NULL; 111 handle->attributes_tail = NULL; 112 113 handle->tickets_head = NULL; 114 handle->tickets_tail = NULL; 115 116 handle->files = GNUNET_CONTAINER_multihashmap_create( 117 initial_map_size_of_handle, GNUNET_NO); 118 119 handle->contexts = NULL; 120 handle->contacts = NULL; 121 handle->groups = NULL; 122 handle->invitations = NULL; 123 124 handle->arm = GNUNET_ARM_connect( 125 handle->cfg, 126 on_handle_arm_connection, 127 handle 128 ); 129 130 if (handle->arm) 131 on_handle_arm_connection(handle, GNUNET_NO); 132 133 handle->identity = GNUNET_IDENTITY_connect( 134 handle->cfg, 135 on_handle_gnunet_identity, 136 handle 137 ); 138 139 handle->fs = GNUNET_FS_start( 140 handle->cfg, "libgnunetchat", 141 notify_handle_fs_progress, handle, 142 GNUNET_FS_FLAGS_NONE, 143 GNUNET_FS_OPTIONS_END 144 ); 145 146 handle->gns = NULL; 147 handle->messenger = NULL; 148 149 handle->namestore = GNUNET_NAMESTORE_connect( 150 handle->cfg 151 ); 152 153 handle->reclaim = GNUNET_RECLAIM_connect( 154 handle->cfg 155 ); 156 157 handle->public_key = NULL; 158 handle->user_pointer = NULL; 159 return handle; 160 } 161 162 void 163 handle_update_key (struct GNUNET_CHAT_Handle *handle) 164 { 165 GNUNET_assert(handle); 166 167 if (handle->public_key) 168 GNUNET_free(handle->public_key); 169 170 handle->public_key = NULL; 171 handle->own_contact = NULL; 172 173 if (!(handle->messenger)) 174 return; 175 176 const struct GNUNET_CRYPTO_BlindablePublicKey *pubkey; 177 pubkey = GNUNET_MESSENGER_get_key(handle->messenger); 178 179 if (pubkey) 180 handle->public_key = GNUNET_CRYPTO_blindable_public_key_to_string(pubkey); 181 } 182 183 void 184 handle_destroy (struct GNUNET_CHAT_Handle *handle) 185 { 186 GNUNET_assert(handle); 187 188 if (handle->shutdown_hook) 189 GNUNET_SCHEDULER_cancel(handle->shutdown_hook); 190 if (handle->destruction) 191 GNUNET_SCHEDULER_cancel(handle->destruction); 192 if (handle->connection) 193 GNUNET_SCHEDULER_cancel(handle->connection); 194 if (handle->refresh) 195 GNUNET_SCHEDULER_cancel(handle->refresh); 196 197 if (handle->monitor) 198 GNUNET_NAMESTORE_zone_monitor_stop(handle->monitor); 199 200 handle->connection = NULL; 201 202 if (handle->current) 203 handle_disconnect(handle); 204 205 GNUNET_CONTAINER_multihashmap_iterate( 206 handle->files, it_destroy_handle_files, NULL 207 ); 208 209 while (handle->attributes_head) 210 internal_attributes_destroy(handle->attributes_head); 211 212 if (handle->reclaim) 213 GNUNET_RECLAIM_disconnect(handle->reclaim); 214 215 if (handle->namestore) 216 GNUNET_NAMESTORE_disconnect(handle->namestore); 217 218 struct GNUNET_CHAT_InternalAccounts *accounts; 219 while (handle->accounts_head) 220 { 221 accounts = handle->accounts_head; 222 223 internal_accounts_stop_method(accounts); 224 225 if (accounts->account) 226 account_destroy(accounts->account); 227 228 internal_accounts_destroy(accounts); 229 } 230 231 if (handle->fs) 232 GNUNET_FS_stop(handle->fs); 233 234 if (handle->identity) 235 GNUNET_IDENTITY_disconnect(handle->identity); 236 237 struct GNUNET_CHAT_InternalServices *services; 238 while (handle->services_head) 239 { 240 services = handle->services_head; 241 242 if (services->op) 243 GNUNET_ARM_operation_cancel(services->op); 244 245 GNUNET_CONTAINER_DLL_remove( 246 handle->services_head, 247 handle->services_tail, 248 services 249 ); 250 251 GNUNET_free(services); 252 } 253 254 if (handle->arm) 255 GNUNET_ARM_disconnect(handle->arm); 256 257 GNUNET_CONTAINER_multihashmap_destroy(handle->files); 258 259 if (handle->directory) 260 GNUNET_free(handle->directory); 261 262 struct GNUNET_CHAT_InternalMessages *internal; 263 while (handle->internal_head) 264 { 265 internal = handle->internal_head; 266 267 if (internal->msg) 268 message_destroy(internal->msg); 269 270 if (internal->task) 271 GNUNET_SCHEDULER_cancel(internal->task); 272 273 GNUNET_CONTAINER_DLL_remove( 274 handle->internal_head, 275 handle->internal_tail, 276 internal 277 ); 278 279 GNUNET_free(internal); 280 } 281 282 GNUNET_free(handle); 283 } 284 285 static void 286 handle_update_identity(struct GNUNET_CHAT_Handle *handle) 287 { 288 GNUNET_assert( 289 (handle) && 290 (handle->current) && 291 (handle->contexts) && 292 (handle->groups) && 293 (handle->contacts) 294 ); 295 296 handle_update_key(handle); 297 298 if ((0 < GNUNET_CONTAINER_multihashmap_size(handle->contexts)) || 299 (0 < GNUNET_CONTAINER_multihashmap_size(handle->groups)) || 300 (0 < GNUNET_CONTAINER_multishortmap_size(handle->contacts))) 301 return; 302 303 GNUNET_assert(handle->messenger); 304 305 handle_send_internal_message( 306 handle, 307 handle->current, 308 NULL, 309 GNUNET_CHAT_FLAG_LOGIN, 310 NULL, 311 GNUNET_NO 312 ); 313 314 const struct GNUNET_CRYPTO_BlindablePrivateKey *zone = handle_get_key(handle); 315 316 if ((!zone) || (handle->monitor)) 317 return; 318 319 handle->monitor = GNUNET_NAMESTORE_zone_monitor_start( 320 handle->cfg, 321 zone, 322 GNUNET_YES, 323 NULL, 324 NULL, 325 on_monitor_namestore_record, 326 handle, 327 NULL, 328 NULL 329 ); 330 } 331 332 void 333 handle_connect (struct GNUNET_CHAT_Handle *handle, 334 struct GNUNET_CHAT_Account *account) 335 { 336 GNUNET_assert( 337 (handle) && (account) && 338 (!(handle->current)) && 339 (!(handle->contexts)) && 340 (!(handle->contacts)) && 341 (!(handle->groups)) && 342 (!(handle->invitations)) && 343 (handle->files) 344 ); 345 346 if (handle->monitor) 347 { 348 GNUNET_NAMESTORE_zone_monitor_stop(handle->monitor); 349 handle->monitor = NULL; 350 } 351 352 handle->contexts = GNUNET_CONTAINER_multihashmap_create( 353 initial_map_size_of_handle, GNUNET_NO); 354 handle->contacts = GNUNET_CONTAINER_multishortmap_create( 355 initial_map_size_of_handle, GNUNET_NO); 356 handle->groups = GNUNET_CONTAINER_multihashmap_create( 357 initial_map_size_of_handle, GNUNET_NO); 358 handle->invitations = GNUNET_CONTAINER_multihashmap_create( 359 initial_map_size_of_handle, GNUNET_NO); 360 361 handle->gns = GNUNET_GNS_connect(handle->cfg); 362 363 const struct GNUNET_CRYPTO_BlindablePrivateKey *key; 364 key = account_get_key(account); 365 366 const char *name = account_get_name(account); 367 368 handle->messenger = GNUNET_MESSENGER_connect( 369 handle->cfg, name, key, 370 on_handle_message, 371 handle 372 ); 373 374 handle->next = NULL; 375 handle->current = account; 376 handle_update_identity(handle); 377 } 378 379 void 380 handle_disconnect (struct GNUNET_CHAT_Handle *handle) 381 { 382 GNUNET_assert( 383 (handle) && 384 (handle->current) && 385 (handle->groups) && 386 (handle->contacts) && 387 (handle->contexts) && 388 (handle->files) 389 ); 390 391 handle_send_internal_message( 392 handle, 393 handle->current, 394 NULL, 395 GNUNET_CHAT_FLAG_LOGOUT, 396 NULL, 397 GNUNET_YES 398 ); 399 400 handle->own_contact = NULL; 401 402 while (handle->attributes_head) 403 internal_attributes_destroy(handle->attributes_head); 404 405 while (handle->tickets_head) 406 internal_tickets_destroy(handle->tickets_head); 407 408 GNUNET_CONTAINER_multihashmap_iterate( 409 handle->groups, it_destroy_handle_groups, NULL 410 ); 411 412 GNUNET_CONTAINER_multishortmap_iterate( 413 handle->contacts, it_destroy_handle_contacts, NULL 414 ); 415 416 GNUNET_CONTAINER_multihashmap_iterate( 417 handle->contexts, it_destroy_handle_contexts, NULL 418 ); 419 420 struct GNUNET_CHAT_InternalMessages *internal; 421 while (handle->internal_head) 422 { 423 internal = handle->internal_head; 424 425 if (!(internal->msg->context)) 426 break; 427 428 if (internal->msg) 429 message_destroy(internal->msg); 430 431 if (internal->task) 432 GNUNET_SCHEDULER_cancel(internal->task); 433 434 GNUNET_CONTAINER_DLL_remove( 435 handle->internal_head, 436 handle->internal_tail, 437 internal 438 ); 439 440 GNUNET_free(internal); 441 } 442 443 if (handle->messenger) 444 GNUNET_MESSENGER_disconnect(handle->messenger); 445 446 struct GNUNET_CHAT_UriLookups *lookups; 447 while (handle->lookups_head) 448 { 449 lookups = handle->lookups_head; 450 451 if (lookups->request) 452 GNUNET_GNS_lookup_cancel(lookups->request); 453 454 if (lookups->uri) 455 uri_destroy(lookups->uri); 456 457 GNUNET_CONTAINER_DLL_remove( 458 handle->lookups_head, 459 handle->lookups_tail, 460 lookups 461 ); 462 463 GNUNET_free(lookups); 464 } 465 466 if (handle->gns) 467 GNUNET_GNS_disconnect(handle->gns); 468 469 GNUNET_CONTAINER_multihashmap_iterate( 470 handle->files, it_destroy_handle_files, NULL 471 ); 472 473 handle->gns = NULL; 474 handle->messenger = NULL; 475 476 struct GNUNET_CHAT_InternalLobbies *lobbies; 477 while (handle->lobbies_head) 478 { 479 lobbies = handle->lobbies_head; 480 481 if (lobbies->lobby) 482 lobby_destroy(lobbies->lobby); 483 484 GNUNET_CONTAINER_DLL_remove( 485 handle->lobbies_head, 486 handle->lobbies_tail, 487 lobbies 488 ); 489 490 GNUNET_free(lobbies); 491 } 492 493 handle->own_contact = NULL; 494 495 GNUNET_CONTAINER_multihashmap_destroy(handle->invitations); 496 GNUNET_CONTAINER_multihashmap_destroy(handle->groups); 497 GNUNET_CONTAINER_multishortmap_destroy(handle->contacts); 498 GNUNET_CONTAINER_multihashmap_destroy(handle->contexts); 499 GNUNET_CONTAINER_multihashmap_clear(handle->files); 500 501 handle->contexts = NULL; 502 handle->contacts = NULL; 503 handle->groups = NULL; 504 handle->invitations = NULL; 505 506 if (handle->connection) 507 GNUNET_SCHEDULER_cancel(handle->connection); 508 509 handle->current = NULL; 510 handle->connection = NULL; 511 512 handle_update_key(handle); 513 } 514 515 static struct GNUNET_CHAT_InternalAccounts* 516 find_accounts_by_name (const struct GNUNET_CHAT_Handle *handle, 517 const char *name, 518 enum GNUNET_GenericReturnValue skip_op) 519 { 520 GNUNET_assert((handle) && (name)); 521 522 struct GNUNET_CHAT_InternalAccounts *accounts = handle->accounts_head; 523 const char *account_name; 524 525 while (accounts) 526 { 527 if ((!(accounts->account)) || ((GNUNET_YES == skip_op) && 528 (accounts->op))) 529 goto skip_account; 530 531 account_name = account_get_name( 532 accounts->account 533 ); 534 535 if ((account_name) && (0 == strcmp(account_name, name))) 536 break; 537 538 skip_account: 539 accounts = accounts->next; 540 } 541 542 return accounts; 543 } 544 545 struct GNUNET_CHAT_Account* 546 handle_get_account_by_name (const struct GNUNET_CHAT_Handle *handle, 547 const char *name, 548 enum GNUNET_GenericReturnValue skip_op) 549 { 550 GNUNET_assert((handle) && (name)); 551 552 struct GNUNET_CHAT_InternalAccounts *accounts; 553 accounts = find_accounts_by_name(handle, name, skip_op); 554 555 if (!accounts) 556 return NULL; 557 558 return accounts->account; 559 } 560 561 static enum GNUNET_GenericReturnValue 562 update_accounts_operation (struct GNUNET_CHAT_InternalAccounts **out_accounts, 563 struct GNUNET_CHAT_Handle *handle, 564 const char *name, 565 enum GNUNET_CHAT_AccountMethod method) 566 { 567 GNUNET_assert(handle); 568 569 struct GNUNET_CHAT_InternalAccounts *accounts = *out_accounts; 570 571 if (accounts) 572 internal_accounts_stop_method(accounts); 573 else 574 { 575 accounts = internal_accounts_create(handle, NULL); 576 577 if (!accounts) 578 return GNUNET_SYSERR; 579 } 580 581 internal_accounts_start_method(accounts, method, name); 582 583 *out_accounts = accounts; 584 585 return GNUNET_OK; 586 } 587 588 enum GNUNET_GenericReturnValue 589 handle_create_account (struct GNUNET_CHAT_Handle *handle, 590 const char *name) 591 { 592 GNUNET_assert((handle) && (name)); 593 594 struct GNUNET_CHAT_InternalAccounts *accounts; 595 accounts = find_accounts_by_name(handle, name, GNUNET_NO); 596 597 if (accounts) 598 return GNUNET_SYSERR; 599 600 enum GNUNET_GenericReturnValue result; 601 result = update_accounts_operation( 602 &accounts, 603 handle, 604 name, 605 GNUNET_CHAT_ACCOUNT_CREATION 606 ); 607 608 if (GNUNET_OK != result) 609 return result; 610 611 accounts->op = GNUNET_IDENTITY_create( 612 handle->identity, 613 name, 614 NULL, 615 GNUNET_PUBLIC_KEY_TYPE_ECDSA, 616 cb_account_creation, 617 accounts 618 ); 619 620 if (!accounts->op) 621 return GNUNET_SYSERR; 622 623 return result; 624 } 625 626 enum GNUNET_GenericReturnValue 627 handle_delete_account (struct GNUNET_CHAT_Handle *handle, 628 const struct GNUNET_CHAT_Account *account) 629 { 630 GNUNET_assert((handle) && (account)); 631 632 struct GNUNET_CHAT_InternalAccounts *accounts; 633 accounts = handle->accounts_head; 634 635 while (accounts) 636 if (account == accounts->account) 637 break; 638 else 639 accounts = accounts->next; 640 641 if (!accounts) 642 return GNUNET_SYSERR; 643 644 enum GNUNET_GenericReturnValue result; 645 result = update_accounts_operation( 646 &accounts, 647 handle, 648 NULL, 649 GNUNET_CHAT_ACCOUNT_DELETION 650 ); 651 652 if (GNUNET_OK != result) 653 return result; 654 655 const char *name = account_get_name(account); 656 657 accounts->op = GNUNET_IDENTITY_delete( 658 handle->identity, 659 name, 660 cb_account_deletion, 661 accounts 662 ); 663 664 if (!accounts->op) 665 return GNUNET_SYSERR; 666 667 return result; 668 } 669 670 enum GNUNET_GenericReturnValue 671 handle_rename_account (struct GNUNET_CHAT_Handle *handle, 672 const struct GNUNET_CHAT_Account *account, 673 const char *new_name) 674 { 675 GNUNET_assert((handle) && (account) && (new_name)); 676 677 struct GNUNET_CHAT_InternalAccounts *accounts; 678 accounts = handle->accounts_head; 679 680 while (accounts) 681 if (account == accounts->account) 682 break; 683 else 684 accounts = accounts->next; 685 686 if (!accounts) 687 return GNUNET_SYSERR; 688 689 if (find_accounts_by_name(handle, new_name, GNUNET_NO)) 690 return GNUNET_SYSERR; 691 692 const char *old_name = account_get_name(account); 693 694 if (0 == strcmp(old_name, new_name)) 695 return GNUNET_OK; 696 697 enum GNUNET_GenericReturnValue result; 698 result = update_accounts_operation( 699 &accounts, 700 handle, 701 NULL, 702 GNUNET_CHAT_ACCOUNT_RENAMING 703 ); 704 705 if (GNUNET_OK != result) 706 return result; 707 708 accounts->op = GNUNET_IDENTITY_rename( 709 handle->identity, 710 old_name, 711 new_name, 712 cb_account_rename, 713 accounts 714 ); 715 716 if (!accounts->op) 717 return GNUNET_SYSERR; 718 719 return result; 720 } 721 722 enum GNUNET_GenericReturnValue 723 handle_delete_lobby (struct GNUNET_CHAT_Handle *handle, 724 const struct GNUNET_CHAT_Lobby *lobby) 725 { 726 GNUNET_assert((handle) && (lobby)); 727 728 if (!(lobby->context)) 729 return GNUNET_SYSERR; 730 731 const struct GNUNET_HashCode *key = GNUNET_MESSENGER_room_get_key( 732 lobby->context->room 733 ); 734 735 if (!key) 736 return GNUNET_SYSERR; 737 738 struct GNUNET_CHAT_InternalAccounts *accounts = NULL; 739 enum GNUNET_GenericReturnValue result; 740 result = update_accounts_operation( 741 &accounts, 742 handle, 743 NULL, 744 GNUNET_CHAT_ACCOUNT_DELETION 745 ); 746 747 if (GNUNET_OK != result) 748 return result; 749 750 char *name; 751 util_lobby_name(key, &name); 752 753 accounts->op = GNUNET_IDENTITY_delete( 754 handle->identity, 755 name, 756 cb_lobby_deletion, 757 accounts 758 ); 759 760 GNUNET_free(name); 761 762 if (!accounts->op) 763 return GNUNET_SYSERR; 764 765 return result; 766 } 767 768 const char* 769 handle_get_directory (const struct GNUNET_CHAT_Handle *handle) 770 { 771 GNUNET_assert(handle); 772 773 return handle->directory; 774 } 775 776 char* 777 handle_create_file_path (const struct GNUNET_CHAT_Handle *handle, 778 const struct GNUNET_HashCode *hash) 779 { 780 GNUNET_assert((handle) && (hash)); 781 782 const char *directory = handle_get_directory(handle); 783 784 if (!directory) 785 return NULL; 786 787 char *filename; 788 util_get_filename ( 789 directory, "files", hash, &filename 790 ); 791 792 return filename; 793 } 794 795 enum GNUNET_GenericReturnValue 796 handle_update (struct GNUNET_CHAT_Handle *handle) 797 { 798 GNUNET_assert((handle) && (handle->current)); 799 800 struct GNUNET_CHAT_InternalAccounts *accounts; 801 accounts = handle->accounts_head; 802 803 while (accounts) 804 if (handle->current == accounts->account) 805 break; 806 else 807 accounts = accounts->next; 808 809 if (!accounts) 810 return GNUNET_SYSERR; 811 812 const char *name = account_get_name(handle->current); 813 814 enum GNUNET_GenericReturnValue result; 815 result = update_accounts_operation( 816 &accounts, 817 handle, 818 name, 819 GNUNET_CHAT_ACCOUNT_UPDATING 820 ); 821 822 if (GNUNET_OK != result) 823 return result; 824 825 accounts->op = GNUNET_IDENTITY_delete( 826 handle->identity, 827 name, 828 cb_account_update, 829 accounts 830 ); 831 832 if (!accounts->op) 833 return GNUNET_SYSERR; 834 835 return result; 836 } 837 838 const struct GNUNET_CRYPTO_BlindablePrivateKey* 839 handle_get_key (const struct GNUNET_CHAT_Handle *handle) 840 { 841 GNUNET_assert(handle); 842 843 if (!(handle->current)) 844 return NULL; 845 846 return account_get_key(handle->current); 847 } 848 849 void 850 handle_send_internal_message (struct GNUNET_CHAT_Handle *handle, 851 struct GNUNET_CHAT_Account *account, 852 struct GNUNET_CHAT_Context *context, 853 enum GNUNET_CHAT_MessageFlag flag, 854 const char *warning, 855 enum GNUNET_GenericReturnValue feedback) 856 { 857 GNUNET_assert((handle) && (GNUNET_CHAT_FLAG_NONE != flag)); 858 859 if ((handle->destruction) || (!(handle->msg_cb))) 860 return; 861 862 struct GNUNET_CHAT_InternalMessages *internal = GNUNET_new( 863 struct GNUNET_CHAT_InternalMessages 864 ); 865 866 internal->chat = handle; 867 internal->msg = message_create_internally( 868 account, context, flag, warning 869 ); 870 871 if (!(internal->msg)) 872 { 873 GNUNET_free(internal); 874 return; 875 } 876 877 if (GNUNET_YES != feedback) 878 internal->task = GNUNET_SCHEDULER_add_now( 879 on_handle_internal_message_callback, 880 internal 881 ); 882 else 883 { 884 internal->task = NULL; 885 if (handle->msg_cb) 886 handle->msg_cb(handle->msg_cls, context, internal->msg); 887 } 888 889 if (context) 890 GNUNET_CONTAINER_DLL_insert( 891 handle->internal_head, 892 handle->internal_tail, 893 internal 894 ); 895 else 896 GNUNET_CONTAINER_DLL_insert_tail( 897 handle->internal_head, 898 handle->internal_tail, 899 internal 900 ); 901 } 902 903 void 904 handle_send_room_name (struct GNUNET_CHAT_Handle *handle, 905 struct GNUNET_MESSENGER_Room *room) 906 { 907 GNUNET_assert((handle) && (handle->messenger) && (room)); 908 909 if (handle->destruction) 910 return; 911 912 const char *name = GNUNET_MESSENGER_get_name(handle->messenger); 913 914 if (!name) 915 return; 916 917 struct GNUNET_MESSENGER_Message msg; 918 memset(&msg, 0, sizeof(msg)); 919 920 msg.header.kind = GNUNET_MESSENGER_KIND_NAME; 921 msg.body.name.name = GNUNET_strdup(name); 922 923 GNUNET_MESSENGER_send_message(room, &msg, NULL); 924 925 GNUNET_free(msg.body.name.name); 926 } 927 928 enum GNUNET_GenericReturnValue 929 handle_request_context_by_room (struct GNUNET_CHAT_Handle *handle, 930 struct GNUNET_MESSENGER_Room *room) 931 { 932 GNUNET_assert( 933 (handle) && 934 (handle->contexts) && 935 (room) 936 ); 937 938 const struct GNUNET_HashCode *key = GNUNET_MESSENGER_room_get_key(room); 939 940 struct GNUNET_CHAT_Context *context = GNUNET_CONTAINER_multihashmap_get( 941 handle->contexts, key 942 ); 943 944 struct GNUNET_CHAT_CheckHandleRoomMembers check; 945 946 if (!context) 947 goto new_context; 948 949 if ((GNUNET_CHAT_CONTEXT_TYPE_UNKNOWN == context->type) && 950 (GNUNET_YES != context->deleted)) 951 goto check_type; 952 953 return GNUNET_OK; 954 955 new_context: 956 context = context_create_from_room(handle, room); 957 958 if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put( 959 handle->contexts, key, context, 960 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) 961 { 962 context_destroy(context); 963 return GNUNET_SYSERR; 964 } 965 966 if (GNUNET_CHAT_CONTEXT_TYPE_GROUP == context->type) 967 goto setup_group; 968 969 check_type: 970 check.ignore_key = GNUNET_MESSENGER_get_key(handle->messenger); 971 check.contact = NULL; 972 973 const int checks = GNUNET_MESSENGER_iterate_members( 974 room, check_handle_room_members, &check 975 ); 976 977 if ((check.contact) && 978 (GNUNET_OK == intern_provide_contact_for_member(handle, 979 check.contact, 980 context))) 981 { 982 context_delete(context, GNUNET_NO); 983 984 context->type = GNUNET_CHAT_CONTEXT_TYPE_CONTACT; 985 context->deleted = GNUNET_NO; 986 987 context_write_records(context); 988 } 989 else if (checks >= minimum_amount_of_other_members_in_group) 990 { 991 context_delete(context, GNUNET_NO); 992 993 context->type = GNUNET_CHAT_CONTEXT_TYPE_GROUP; 994 context->deleted = GNUNET_NO; 995 996 if (context->contact) 997 { 998 struct GNUNET_CHAT_Contact *contact = handle_get_contact_from_messenger( 999 handle, check.contact 1000 ); 1001 1002 if ((contact) && (contact->context == context)) 1003 contact->context = NULL; 1004 1005 context->contact = NULL; 1006 } 1007 1008 goto setup_group; 1009 } 1010 1011 return GNUNET_OK; 1012 1013 setup_group: 1014 GNUNET_MESSENGER_iterate_members(room, scan_handle_room_members, handle); 1015 1016 struct GNUNET_CHAT_Group *group = group_create_from_context( 1017 handle, context 1018 ); 1019 1020 if (context->topic) 1021 group_publish(group); 1022 1023 if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put( 1024 handle->groups, key, group, 1025 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) 1026 { 1027 handle_send_internal_message( 1028 handle, 1029 NULL, 1030 context, 1031 GNUNET_CHAT_FLAG_UPDATE_CONTEXT, 1032 NULL, 1033 GNUNET_NO 1034 ); 1035 1036 context_write_records(context); 1037 return GNUNET_OK; 1038 } 1039 1040 group_destroy(group); 1041 1042 GNUNET_CONTAINER_multihashmap_remove(handle->contexts, key, context); 1043 context_destroy(context); 1044 return GNUNET_SYSERR; 1045 } 1046 1047 struct GNUNET_CHAT_Contact* 1048 handle_get_contact_from_messenger (const struct GNUNET_CHAT_Handle *handle, 1049 const struct GNUNET_MESSENGER_Contact *contact) 1050 { 1051 GNUNET_assert((handle) && (handle->contacts) && (contact)); 1052 1053 struct GNUNET_ShortHashCode shorthash; 1054 util_shorthash_from_member(contact, &shorthash); 1055 1056 return GNUNET_CONTAINER_multishortmap_get( 1057 handle->contacts, &shorthash 1058 ); 1059 } 1060 1061 struct GNUNET_CHAT_Group* 1062 handle_get_group_from_messenger (const struct GNUNET_CHAT_Handle *handle, 1063 const struct GNUNET_MESSENGER_Room *room) 1064 { 1065 GNUNET_assert((handle) && (handle->groups) && (room)); 1066 1067 const struct GNUNET_HashCode *key = GNUNET_MESSENGER_room_get_key(room); 1068 1069 if (!key) 1070 return NULL; 1071 1072 return GNUNET_CONTAINER_multihashmap_get( 1073 handle->groups, key 1074 ); 1075 } 1076 1077 struct GNUNET_CHAT_Context* 1078 handle_process_records (struct GNUNET_CHAT_Handle *handle, 1079 const char *label, 1080 unsigned int count, 1081 const struct GNUNET_GNSRECORD_Data *data) 1082 { 1083 GNUNET_assert((handle) && (data)); 1084 1085 if (!count) 1086 return NULL; 1087 1088 const struct GNUNET_MESSENGER_RoomEntryRecord *record = NULL; 1089 1090 for (unsigned int i = 0; i < count; i++) 1091 { 1092 if (GNUNET_YES == GNUNET_GNSRECORD_is_expired(data + i)) 1093 continue; 1094 1095 if ((GNUNET_GNSRECORD_TYPE_MESSENGER_ROOM_ENTRY == data[i].record_type) && 1096 (!(GNUNET_GNSRECORD_RF_SUPPLEMENTAL & data[i].flags))) 1097 { 1098 record = data[i].data; 1099 break; 1100 } 1101 } 1102 1103 if (!record) 1104 return NULL; 1105 1106 union GNUNET_MESSENGER_RoomKey key; 1107 GNUNET_memcpy (&(key.hash), &(record->key), sizeof(key)); 1108 1109 struct GNUNET_CHAT_Context *context = GNUNET_CONTAINER_multihashmap_get( 1110 handle->contexts, 1111 &(key.hash) 1112 ); 1113 1114 if ((context) && (context->room)) 1115 { 1116 context_read_records(context, label, count, data); 1117 return NULL; 1118 } 1119 1120 struct GNUNET_MESSENGER_Room *room = GNUNET_MESSENGER_enter_room( 1121 handle->messenger, 1122 &(record->door), 1123 &key 1124 ); 1125 1126 if (!room) 1127 return NULL; 1128 else if (context) 1129 { 1130 context_update_room(context, room, GNUNET_NO); 1131 context_read_records(context, label, count, data); 1132 return NULL; 1133 } 1134 1135 context = context_create_from_room(handle, room); 1136 context_read_records(context, label, count, data); 1137 1138 if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put( 1139 handle->contexts, &(key.hash), context, 1140 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) 1141 { 1142 context_destroy(context); 1143 GNUNET_MESSENGER_close_room(room); 1144 return NULL; 1145 } 1146 1147 if (GNUNET_CHAT_CONTEXT_TYPE_GROUP != context->type) 1148 return context; 1149 1150 struct GNUNET_CHAT_Group *group = group_create_from_context(handle, context); 1151 1152 if (context->topic) 1153 group_publish(group); 1154 1155 if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put( 1156 handle->groups, &(key.hash), group, 1157 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) 1158 group_destroy(group); 1159 1160 return context; 1161 }