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