gnunet_chat_handle_intern.c (30163B)
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_intern.c 23 */ 24 25 #include "gnunet_chat_contact.h" 26 #include "gnunet_chat_context.h" 27 #include "gnunet_chat_discourse.h" 28 #include "gnunet_chat_file.h" 29 #include "gnunet_chat_group.h" 30 #include "gnunet_chat_handle.h" 31 #include "gnunet_chat_invitation.h" 32 #include "gnunet_chat_lobby.h" 33 #include "gnunet_chat_message.h" 34 #include "gnunet_chat_ticket.h" 35 #include "gnunet_chat_util.h" 36 37 #include "internal/gnunet_chat_accounts.h" 38 #include "internal/gnunet_chat_tagging.h" 39 40 #include <gnunet/gnunet_arm_service.h> 41 #include <gnunet/gnunet_common.h> 42 #include <gnunet/gnunet_identity_service.h> 43 #include <gnunet/gnunet_messenger_service.h> 44 #include <gnunet/gnunet_reclaim_service.h> 45 #include <gnunet/gnunet_scheduler_lib.h> 46 #include <gnunet/gnunet_time_lib.h> 47 #include <gnunet/gnunet_util_lib.h> 48 49 #include <stdio.h> 50 #include <string.h> 51 52 #define GNUNET_UNUSED __attribute__ ((unused)) 53 54 static const char gnunet_service_name_arm [] = "arm"; 55 static const char gnunet_service_name_fs [] = "fs"; 56 static const char gnunet_service_name_gns [] = "gns"; 57 static const char gnunet_service_name_identity [] = "identity"; 58 static const char gnunet_service_name_messenger [] = "messenger"; 59 static const char gnunet_service_name_namestore [] = "namestore"; 60 static const char gnunet_service_name_reclaim [] = "reclaim"; 61 62 void 63 on_handle_shutdown(void *cls) 64 { 65 struct GNUNET_CHAT_Handle *chat = cls; 66 67 GNUNET_assert((chat) && (chat->shutdown_hook)); 68 chat->shutdown_hook = NULL; 69 70 handle_destroy(chat); 71 } 72 73 void 74 on_handle_service_request(void *cls, 75 enum GNUNET_ARM_RequestStatus status, 76 enum GNUNET_ARM_Result result) 77 { 78 GNUNET_assert(cls); 79 80 struct GNUNET_CHAT_InternalServices *services = cls; 81 services->op = NULL; 82 83 if (status != GNUNET_ARM_REQUEST_SENT_OK) 84 return; 85 86 struct GNUNET_CHAT_Handle *chat = services->chat; 87 88 GNUNET_CONTAINER_DLL_remove( 89 chat->services_head, 90 chat->services_tail, 91 services 92 ); 93 94 GNUNET_free(services); 95 } 96 97 static void 98 _request_service_via_arm(struct GNUNET_CHAT_Handle *chat, 99 const char *service_name) 100 { 101 GNUNET_assert((chat) && (chat->arm) && (service_name)); 102 103 struct GNUNET_CHAT_InternalServices *services = GNUNET_new( 104 struct GNUNET_CHAT_InternalServices 105 ); 106 107 if (! services) 108 return; 109 110 services->chat = chat; 111 services->op = GNUNET_ARM_request_service_start( 112 chat->arm, 113 service_name, 114 GNUNET_OS_INHERIT_STD_NONE, 115 on_handle_service_request, 116 services 117 ); 118 119 GNUNET_CONTAINER_DLL_insert( 120 chat->services_head, 121 chat->services_tail, 122 services 123 ); 124 } 125 126 void 127 on_handle_arm_connection(void *cls, 128 int connected) 129 { 130 struct GNUNET_CHAT_Handle *chat = cls; 131 132 GNUNET_assert((chat) && (chat->arm)); 133 134 if (GNUNET_YES == connected) { 135 _request_service_via_arm(chat, gnunet_service_name_identity); 136 _request_service_via_arm(chat, gnunet_service_name_messenger); 137 _request_service_via_arm(chat, gnunet_service_name_fs); 138 _request_service_via_arm(chat, gnunet_service_name_gns); 139 _request_service_via_arm(chat, gnunet_service_name_namestore); 140 _request_service_via_arm(chat, gnunet_service_name_reclaim); 141 } else { 142 _request_service_via_arm(chat, gnunet_service_name_arm); 143 } 144 } 145 146 void* 147 notify_handle_fs_progress(void* cls, 148 const struct GNUNET_FS_ProgressInfo* info) 149 { 150 struct GNUNET_CHAT_Handle *chat = cls; 151 152 GNUNET_assert(info); 153 154 if (!chat) 155 return NULL; 156 157 switch (info->status) { 158 case GNUNET_FS_STATUS_PUBLISH_START: { 159 struct GNUNET_CHAT_File *file = info->value.publish.cctx; 160 161 file_update_upload( 162 file, 163 0, 164 info->value.publish.size 165 ); 166 167 return file; 168 } case GNUNET_FS_STATUS_PUBLISH_PROGRESS: { 169 struct GNUNET_CHAT_File *file = info->value.publish.cctx; 170 171 file_update_upload( 172 file, 173 info->value.publish.completed, 174 info->value.publish.size 175 ); 176 177 return file; 178 } case GNUNET_FS_STATUS_PUBLISH_COMPLETED: { 179 struct GNUNET_CHAT_File *file = info->value.publish.cctx; 180 181 file->uri = GNUNET_FS_uri_dup( 182 info->value.publish.specifics.completed.chk_uri 183 ); 184 185 file_update_upload( 186 file, 187 info->value.publish.size, 188 info->value.publish.size 189 ); 190 191 file->publish = NULL; 192 break; 193 } case GNUNET_FS_STATUS_PUBLISH_ERROR: { 194 break; 195 } case GNUNET_FS_STATUS_DOWNLOAD_START: { 196 struct GNUNET_CHAT_File *file = info->value.download.cctx; 197 198 file_update_download( 199 file, 200 0, 201 info->value.download.size 202 ); 203 204 return file; 205 } case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE: { 206 return info->value.download.cctx; 207 } case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE: { 208 return info->value.download.cctx; 209 } case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS: { 210 struct GNUNET_CHAT_File *file = info->value.download.cctx; 211 212 file_update_download( 213 file, 214 info->value.download.completed, 215 info->value.download.size 216 ); 217 218 return file; 219 } case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED: { 220 struct GNUNET_CHAT_File *file = info->value.download.cctx; 221 222 file_update_download( 223 file, 224 info->value.download.size, 225 info->value.download.size 226 ); 227 228 file->download = NULL; 229 break; 230 } case GNUNET_FS_STATUS_DOWNLOAD_ERROR: { 231 break; 232 } case GNUNET_FS_STATUS_UNINDEX_START: { 233 struct GNUNET_CHAT_File *file = info->value.unindex.cctx; 234 235 file_update_unindex( 236 file, 237 0, 238 info->value.unindex.size 239 ); 240 241 return file; 242 } case GNUNET_FS_STATUS_UNINDEX_PROGRESS: { 243 struct GNUNET_CHAT_File *file = info->value.unindex.cctx; 244 245 file_update_unindex( 246 file, 247 info->value.unindex.completed, 248 info->value.unindex.size 249 ); 250 251 return file; 252 } case GNUNET_FS_STATUS_UNINDEX_COMPLETED: { 253 struct GNUNET_CHAT_File *file = info->value.unindex.cctx; 254 255 file_update_unindex( 256 file, 257 info->value.unindex.size, 258 info->value.unindex.size 259 ); 260 261 file->unindex = NULL; 262 char *filename = handle_create_file_path( 263 chat, &(file->hash) 264 ); 265 266 if (!filename) 267 break; 268 269 if (GNUNET_YES == GNUNET_DISK_file_test_read(filename)) 270 remove(filename); 271 272 GNUNET_free(filename); 273 break; 274 } default: { 275 break; 276 } 277 } 278 279 return NULL; 280 } 281 282 static void 283 on_handle_refresh (void *cls) 284 { 285 GNUNET_assert(cls); 286 287 struct GNUNET_CHAT_Handle* handle = cls; 288 289 handle->refresh = NULL; 290 291 handle_send_internal_message( 292 handle, 293 NULL, 294 NULL, 295 GNUNET_CHAT_FLAG_REFRESH, 296 NULL, 297 GNUNET_YES 298 ); 299 } 300 301 void 302 on_handle_gnunet_identity (void *cls, 303 struct GNUNET_IDENTITY_Ego *ego, 304 void **ctx, 305 const char *name) 306 { 307 GNUNET_assert(cls); 308 309 if ((name) && (GNUNET_YES == util_is_lobby_name(name))) 310 return; 311 312 struct GNUNET_CHAT_Handle* handle = cls; 313 314 if ((!ctx) || (!ego)) 315 { 316 handle->refreshing = GNUNET_YES; 317 goto send_refresh; 318 } 319 320 struct GNUNET_CHAT_InternalAccounts *accounts = handle->accounts_head; 321 322 while (accounts) 323 { 324 if (!(accounts->account)) 325 goto skip_account; 326 327 if (ego != accounts->account->ego) 328 goto check_matching_name; 329 330 if ((name) && ((!(accounts->account->name)) || 331 (0 != strcmp(accounts->account->name, name)))) 332 { 333 util_set_name_field(name, &(accounts->account->name)); 334 335 handle_send_internal_message( 336 handle, 337 accounts->account, 338 NULL, 339 GNUNET_CHAT_FLAG_UPDATE_ACCOUNT, 340 NULL, 341 GNUNET_YES 342 ); 343 } 344 else if ((!name) && (!(accounts->op))) 345 { 346 if (handle->current == accounts->account) 347 handle_disconnect(handle); 348 349 account_destroy(accounts->account); 350 internal_accounts_destroy(accounts); 351 } 352 else if (!name) 353 account_update_ego(accounts->account, handle, NULL); 354 355 goto send_refresh; 356 357 check_matching_name: 358 if ((name) && (accounts->account->name) && (ego) && 359 (0 == strcmp(accounts->account->name, name))) 360 { 361 account_update_ego(accounts->account, handle, ego); 362 goto send_refresh; 363 } 364 365 skip_account: 366 accounts = accounts->next; 367 } 368 369 if (!name) 370 return; 371 372 accounts = internal_accounts_create( 373 handle, 374 account_create_from_ego(ego, name) 375 ); 376 377 send_refresh: 378 if ((GNUNET_YES != handle->refreshing) || 379 (handle->refresh)) 380 return; 381 382 handle->refresh = GNUNET_SCHEDULER_add_with_priority( 383 GNUNET_SCHEDULER_PRIORITY_IDLE, 384 on_handle_refresh, 385 handle 386 ); 387 } 388 389 void 390 cb_account_creation (void *cls, 391 const struct GNUNET_CRYPTO_BlindablePrivateKey *key, 392 enum GNUNET_ErrorCode ec) 393 { 394 GNUNET_assert(cls); 395 396 struct GNUNET_CHAT_InternalAccounts *accounts = ( 397 (struct GNUNET_CHAT_InternalAccounts*) cls 398 ); 399 400 accounts->op = NULL; 401 402 if ((!(accounts->account)) && (accounts->identifier)) 403 accounts->account = account_create( 404 accounts->identifier 405 ); 406 407 internal_accounts_stop_method(accounts); 408 409 if (GNUNET_EC_NONE == ec) 410 return; 411 412 handle_send_internal_message( 413 accounts->handle, 414 accounts->account, 415 NULL, 416 GNUNET_CHAT_FLAG_WARNING, 417 GNUNET_ErrorCode_get_hint(ec), 418 GNUNET_YES 419 ); 420 } 421 422 void 423 cb_account_deletion (void *cls, 424 enum GNUNET_ErrorCode ec) 425 { 426 GNUNET_assert(cls); 427 428 struct GNUNET_CHAT_InternalAccounts *accounts = ( 429 (struct GNUNET_CHAT_InternalAccounts*) cls 430 ); 431 432 accounts->op = NULL; 433 434 internal_accounts_stop_method(accounts); 435 436 if (accounts->handle->current == accounts->account) 437 handle_disconnect(accounts->handle); 438 439 if (GNUNET_EC_NONE != ec) 440 handle_send_internal_message( 441 accounts->handle, 442 accounts->account, 443 NULL, 444 GNUNET_CHAT_FLAG_WARNING, 445 GNUNET_ErrorCode_get_hint(ec), 446 GNUNET_YES 447 ); 448 else 449 { 450 handle_send_internal_message( 451 accounts->handle, 452 accounts->account, 453 NULL, 454 GNUNET_CHAT_FLAG_DELETE_ACCOUNT, 455 NULL, 456 GNUNET_YES 457 ); 458 459 account_delete(accounts->account); 460 } 461 462 account_destroy(accounts->account); 463 internal_accounts_destroy(accounts); 464 } 465 466 void 467 cb_account_rename (void *cls, 468 enum GNUNET_ErrorCode ec) 469 { 470 GNUNET_assert(cls); 471 472 struct GNUNET_CHAT_InternalAccounts *accounts = ( 473 (struct GNUNET_CHAT_InternalAccounts*) cls 474 ); 475 476 accounts->op = NULL; 477 478 internal_accounts_stop_method(accounts); 479 480 if (GNUNET_EC_NONE == ec) 481 return; 482 483 handle_send_internal_message( 484 accounts->handle, 485 accounts->account, 486 NULL, 487 GNUNET_CHAT_FLAG_WARNING, 488 GNUNET_ErrorCode_get_hint(ec), 489 GNUNET_YES 490 ); 491 } 492 493 void 494 cb_lobby_deletion (void *cls, 495 enum GNUNET_ErrorCode ec) 496 { 497 GNUNET_assert(cls); 498 499 struct GNUNET_CHAT_InternalAccounts *accounts = ( 500 (struct GNUNET_CHAT_InternalAccounts*) cls 501 ); 502 503 accounts->op = NULL; 504 505 internal_accounts_stop_method(accounts); 506 507 if (GNUNET_EC_NONE != ec) 508 handle_send_internal_message( 509 accounts->handle, 510 accounts->account, 511 NULL, 512 GNUNET_CHAT_FLAG_WARNING, 513 GNUNET_ErrorCode_get_hint(ec), 514 GNUNET_YES 515 ); 516 517 internal_accounts_destroy(accounts); 518 } 519 520 static void 521 cb_account_update_completion (void *cls, 522 const struct GNUNET_CRYPTO_BlindablePrivateKey *key, 523 enum GNUNET_ErrorCode ec) 524 { 525 GNUNET_assert(cls); 526 527 struct GNUNET_CHAT_InternalAccounts *accounts = ( 528 (struct GNUNET_CHAT_InternalAccounts*) cls 529 ); 530 531 accounts->op = NULL; 532 533 cb_account_creation(cls, key, ec); 534 } 535 536 void 537 cb_account_update (void *cls, 538 enum GNUNET_ErrorCode ec) 539 { 540 GNUNET_assert(cls); 541 542 struct GNUNET_CHAT_InternalAccounts *accounts = ( 543 (struct GNUNET_CHAT_InternalAccounts*) cls 544 ); 545 546 if ((GNUNET_EC_NONE != ec) || (!(accounts->identifier))) 547 { 548 cb_account_deletion(cls, ec); 549 return; 550 } 551 552 accounts->op = GNUNET_IDENTITY_create( 553 accounts->handle->identity, 554 accounts->identifier, 555 NULL, 556 GNUNET_PUBLIC_KEY_TYPE_ECDSA, 557 cb_account_update_completion, 558 accounts 559 ); 560 } 561 562 int 563 intern_provide_contact_for_member(struct GNUNET_CHAT_Handle *handle, 564 const struct GNUNET_MESSENGER_Contact *member, 565 struct GNUNET_CHAT_Context *context) 566 { 567 GNUNET_assert((handle) && (handle->contacts)); 568 569 if (!member) 570 return GNUNET_SYSERR; 571 572 struct GNUNET_ShortHashCode shorthash; 573 util_shorthash_from_member(member, &shorthash); 574 575 struct GNUNET_CHAT_Contact *contact = GNUNET_CONTAINER_multishortmap_get( 576 handle->contacts, &shorthash 577 ); 578 579 if (contact) 580 { 581 if ((context) && (NULL == contact->context)) 582 { 583 contact->context = context; 584 context->contact = member; 585 } 586 587 return GNUNET_OK; 588 } 589 590 contact = contact_create_from_member( 591 handle, member 592 ); 593 594 if (context) 595 { 596 contact->context = context; 597 context->contact = member; 598 } 599 600 if (GNUNET_OK == GNUNET_CONTAINER_multishortmap_put( 601 handle->contacts, &shorthash, contact, 602 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) 603 return GNUNET_OK; 604 605 if (context) 606 context->contact = NULL; 607 608 contact_destroy(contact); 609 return GNUNET_SYSERR; 610 } 611 612 struct GNUNET_CHAT_CheckHandleRoomMembers 613 { 614 const struct GNUNET_CRYPTO_BlindablePublicKey *ignore_key; 615 const struct GNUNET_MESSENGER_Contact *contact; 616 }; 617 618 int 619 check_handle_room_members (void* cls, 620 GNUNET_UNUSED struct GNUNET_MESSENGER_Room *room, 621 const struct GNUNET_MESSENGER_Contact *member) 622 { 623 struct GNUNET_CHAT_CheckHandleRoomMembers *check = cls; 624 625 GNUNET_assert((check) && (member)); 626 627 const struct GNUNET_CRYPTO_BlindablePublicKey *member_key = ( 628 GNUNET_MESSENGER_contact_get_key(member) 629 ); 630 631 if ((member_key) && (check->ignore_key) && 632 (0 == GNUNET_memcmp(member_key, check->ignore_key))) 633 return GNUNET_YES; 634 635 if (check->contact) 636 { 637 check->contact = NULL; 638 return GNUNET_NO; 639 } 640 641 check->contact = member; 642 return GNUNET_YES; 643 } 644 645 int 646 scan_handle_room_members (void* cls, 647 GNUNET_UNUSED struct GNUNET_MESSENGER_Room *room, 648 const struct GNUNET_MESSENGER_Contact *member) 649 { 650 struct GNUNET_CHAT_Handle *handle = cls; 651 652 if (GNUNET_OK == intern_provide_contact_for_member(handle, member, NULL)) 653 return GNUNET_YES; 654 else 655 return GNUNET_NO; 656 } 657 658 void 659 on_monitor_namestore_record(void *cls, 660 GNUNET_UNUSED const 661 struct GNUNET_CRYPTO_BlindablePrivateKey *zone, 662 const char *label, 663 unsigned int count, 664 const struct GNUNET_GNSRECORD_Data *data) 665 { 666 struct GNUNET_CHAT_Handle *chat = cls; 667 668 if (chat->destruction) 669 { 670 GNUNET_NAMESTORE_zone_monitor_stop(chat->monitor); 671 chat->monitor = NULL; 672 return; 673 } 674 675 handle_process_records(chat, label, count, data); 676 677 if (chat->monitor) 678 GNUNET_NAMESTORE_zone_monitor_next(chat->monitor, 1); 679 } 680 681 void 682 on_handle_message_callback(void *cls); 683 684 static enum GNUNET_GenericReturnValue 685 it_context_iterate_dependencies(void *cls, 686 const struct GNUNET_HashCode *key, 687 void *value) 688 { 689 struct GNUNET_CHAT_Message *message = (struct GNUNET_CHAT_Message*) value; 690 691 if ((message) && (!message->task)) 692 message->task = GNUNET_SCHEDULER_add_now( 693 on_handle_message_callback, message 694 ); 695 696 return GNUNET_YES; 697 } 698 699 void 700 on_handle_internal_message_callback(void *cls) 701 { 702 struct GNUNET_CHAT_InternalMessages *internal = cls; 703 704 GNUNET_assert( 705 (internal) && 706 (internal->chat) && 707 (internal->msg) && 708 (internal->task) 709 ); 710 711 internal->task = NULL; 712 713 struct GNUNET_CHAT_Handle *handle = internal->chat; 714 struct GNUNET_CHAT_Context *context = internal->msg->context; 715 716 if (!(handle->msg_cb)) 717 return; 718 719 handle->msg_cb(handle->msg_cls, context, internal->msg); 720 } 721 722 static enum GNUNET_GenericReturnValue 723 it_invitation_update (GNUNET_UNUSED void *cls, 724 GNUNET_UNUSED const struct GNUNET_HashCode *key, 725 void *value) 726 { 727 struct GNUNET_CHAT_Invitation *invitation = (struct GNUNET_CHAT_Invitation*) value; 728 729 if (invitation) 730 invitation_update(invitation); 731 732 return GNUNET_YES; 733 } 734 735 void 736 on_handle_message_callback(void *cls) 737 { 738 struct GNUNET_CHAT_Message *message = (struct GNUNET_CHAT_Message*) cls; 739 740 GNUNET_assert( 741 (message) && 742 (message->context) && 743 (message->context->handle) 744 ); 745 746 message->task = NULL; 747 748 if (GNUNET_YES != message_has_msg(message)) 749 return; 750 751 const struct GNUNET_TIME_Absolute timestamp = GNUNET_TIME_absolute_ntoh( 752 message->msg->header.timestamp 753 ); 754 755 struct GNUNET_TIME_Relative task_delay; 756 switch (message->msg->header.kind) 757 { 758 case GNUNET_MESSENGER_KIND_DELETION: 759 { 760 const struct GNUNET_TIME_Relative delay = GNUNET_TIME_relative_ntoh( 761 message->msg->body.deletion.delay 762 ); 763 764 task_delay = GNUNET_TIME_absolute_get_difference( 765 GNUNET_TIME_absolute_get(), 766 GNUNET_TIME_absolute_add(timestamp, delay) 767 ); 768 769 break; 770 } 771 default: 772 { 773 task_delay = GNUNET_TIME_relative_get_zero_(); 774 break; 775 } 776 } 777 778 if (! GNUNET_TIME_relative_is_zero(task_delay)) 779 { 780 message->task = GNUNET_SCHEDULER_add_delayed( 781 task_delay, 782 on_handle_message_callback, 783 message 784 ); 785 786 return; 787 } 788 789 struct GNUNET_CHAT_Context *context = message->context; 790 struct GNUNET_CHAT_Handle *handle = context->handle; 791 const struct GNUNET_MESSENGER_Contact *sender; 792 793 if (GNUNET_MESSENGER_FLAG_DELETE & message->flags) 794 goto skip_msg_handing; 795 796 switch (message->msg->header.kind) 797 { 798 case GNUNET_MESSENGER_KIND_INVITE: 799 { 800 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(context->invites, 801 &(message->hash))) 802 break; 803 804 struct GNUNET_CHAT_Invitation *invitation = invitation_create_from_message( 805 context, &(message->hash), &(message->msg->body.invite) 806 ); 807 808 if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put( 809 context->invites, &(message->hash), invitation, 810 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) 811 invitation_destroy(invitation); 812 else 813 GNUNET_CONTAINER_multihashmap_put( 814 handle->invitations, &(invitation->key.hash), invitation, 815 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); 816 break; 817 } 818 case GNUNET_MESSENGER_KIND_FILE: 819 { 820 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(context->files, 821 &(message->hash))) 822 break; 823 824 GNUNET_CONTAINER_multihashmap_put( 825 context->files, &(message->hash), NULL, 826 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST 827 ); 828 829 struct GNUNET_CHAT_File *file = GNUNET_CONTAINER_multihashmap_get( 830 context->handle->files, &(message->msg->body.file.hash) 831 ); 832 833 if (file) 834 break; 835 836 file = file_create_from_message( 837 context->handle, &(message->msg->body.file) 838 ); 839 840 if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put( 841 context->handle->files, &(file->hash), file, 842 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) 843 file_destroy(file); 844 break; 845 } 846 case GNUNET_MESSENGER_KIND_TAG: 847 { 848 struct GNUNET_CHAT_InternalTagging *tagging = GNUNET_CONTAINER_multihashmap_get( 849 context->taggings, &(message->msg->body.tag.hash)); 850 851 if (!tagging) 852 { 853 tagging = internal_tagging_create(); 854 855 if (!tagging) 856 break; 857 858 if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put( 859 context->taggings, &(message->msg->body.tag.hash), tagging, 860 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) 861 { 862 internal_tagging_destroy(tagging); 863 break; 864 } 865 } 866 867 internal_tagging_add(tagging, message); 868 break; 869 } 870 default: 871 break; 872 } 873 874 skip_msg_handing: 875 sender = GNUNET_MESSENGER_get_sender(context->room, &(message->hash)); 876 877 if (!sender) 878 goto clear_dependencies; 879 880 struct GNUNET_ShortHashCode shorthash; 881 util_shorthash_from_member(sender, &shorthash); 882 883 struct GNUNET_CHAT_Contact *contact = GNUNET_CONTAINER_multishortmap_get( 884 handle->contacts, &shorthash 885 ); 886 887 if (!contact) 888 goto clear_dependencies; 889 890 if (GNUNET_MESSENGER_FLAG_DELETE & message->flags) 891 goto skip_sender_handing; 892 893 switch (message->msg->header.kind) 894 { 895 case GNUNET_MESSENGER_KIND_JOIN: 896 { 897 contact_update_join(contact, context, 898 &(message->hash), message->flags); 899 900 GNUNET_CONTAINER_multihashmap_get_multiple( 901 handle->invitations, 902 GNUNET_MESSENGER_room_get_key(context->room), 903 it_invitation_update, handle); 904 905 if ((GNUNET_MESSENGER_FLAG_SENT & message->flags) && 906 (GNUNET_MESSENGER_FLAG_RECENT & message->flags)) 907 handle_send_room_name(handle, context->room); 908 909 break; 910 } 911 case GNUNET_MESSENGER_KIND_LEAVE: 912 { 913 GNUNET_CONTAINER_multihashmap_get_multiple( 914 handle->invitations, 915 GNUNET_MESSENGER_room_get_key(context->room), 916 it_invitation_update, handle); 917 918 break; 919 } 920 case GNUNET_MESSENGER_KIND_KEY: 921 { 922 contact_update_key(contact); 923 break; 924 } 925 case GNUNET_MESSENGER_KIND_TICKET: 926 { 927 struct GNUNET_CHAT_InternalTickets *tickets = contact->tickets_head; 928 while (tickets) 929 { 930 if (0 == strncmp(tickets->ticket->ticket.gns_name, 931 message->msg->body.ticket.identifier, 932 sizeof(tickets->ticket->ticket.gns_name))) 933 break; 934 935 tickets = tickets->next; 936 } 937 938 if (tickets) 939 break; 940 941 tickets = GNUNET_new( 942 struct GNUNET_CHAT_InternalTickets 943 ); 944 945 if (!tickets) 946 break; 947 948 tickets->ticket = ticket_create_from_message( 949 handle, contact, &(message->msg->body.ticket) 950 ); 951 952 if (!tickets->ticket) 953 { 954 GNUNET_free(tickets); 955 break; 956 } 957 958 GNUNET_CONTAINER_DLL_insert_tail( 959 contact->tickets_head, 960 contact->tickets_tail, 961 tickets 962 ); 963 break; 964 } 965 case GNUNET_MESSENGER_KIND_SUBSCRIBTION: 966 { 967 const struct GNUNET_ShortHashCode *sid = &(message->msg->body.subscription.discourse); 968 struct GNUNET_CHAT_Discourse *discourse = GNUNET_CONTAINER_multishortmap_get( 969 context->discourses, sid 970 ); 971 972 if (! discourse) 973 { 974 struct GNUNET_CHAT_DiscourseId id; 975 util_discourse_id_from_shorthash(sid, &id); 976 977 discourse = discourse_create(context, &id); 978 979 if (GNUNET_OK != GNUNET_CONTAINER_multishortmap_put(context->discourses, 980 sid, discourse, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) 981 { 982 discourse_destroy(discourse); 983 break; 984 } 985 } 986 987 enum GNUNET_GenericReturnValue subscription_update = GNUNET_NO; 988 989 if (GNUNET_MESSENGER_FLAG_SUBSCRIPTION_UNSUBSCRIBE & message->msg->body.subscription.flags) 990 discourse_unsubscribe( 991 discourse, 992 contact, 993 GNUNET_TIME_absolute_ntoh(message->msg->header.timestamp), 994 GNUNET_TIME_relative_ntoh(message->msg->body.subscription.time) 995 ); 996 else 997 subscription_update = discourse_subscribe( 998 discourse, 999 contact, 1000 GNUNET_TIME_absolute_ntoh(message->msg->header.timestamp), 1001 GNUNET_TIME_relative_ntoh(message->msg->body.subscription.time) 1002 ); 1003 1004 if (GNUNET_YES == subscription_update) 1005 message->flags |= GNUNET_MESSENGER_FLAG_UPDATE; 1006 1007 break; 1008 } 1009 default: 1010 break; 1011 } 1012 1013 skip_sender_handing: 1014 if (!(handle->msg_cb)) 1015 goto clear_dependencies; 1016 1017 handle->msg_cb(handle->msg_cls, context, message); 1018 1019 clear_dependencies: 1020 GNUNET_CONTAINER_multihashmap_get_multiple(context->dependencies, 1021 &(message->hash), 1022 it_context_iterate_dependencies, 1023 NULL); 1024 GNUNET_CONTAINER_multihashmap_remove_all(context->dependencies, 1025 &(message->hash)); 1026 } 1027 1028 void 1029 on_handle_message (void *cls, 1030 struct GNUNET_MESSENGER_Room *room, 1031 const struct GNUNET_MESSENGER_Contact *sender, 1032 const struct GNUNET_MESSENGER_Contact *recipient, 1033 const struct GNUNET_MESSENGER_Message *msg, 1034 const struct GNUNET_HashCode *hash, 1035 enum GNUNET_MESSENGER_MessageFlags flags) 1036 { 1037 struct GNUNET_CHAT_Handle *handle = cls; 1038 1039 GNUNET_assert( 1040 (handle) && 1041 (room) && 1042 (msg) && 1043 (hash) 1044 ); 1045 1046 if ((handle->destruction) || 1047 (GNUNET_OK != handle_request_context_by_room(handle, room))) 1048 return; 1049 1050 struct GNUNET_CHAT_Context *context = GNUNET_CONTAINER_multihashmap_get( 1051 handle->contexts, GNUNET_MESSENGER_room_get_key(room) 1052 ); 1053 1054 if (GNUNET_MESSENGER_KIND_MERGE == msg->header.kind) 1055 context_request_message(context, &(msg->body.merge.previous)); 1056 1057 context_request_message(context, &(msg->header.previous)); 1058 1059 if ((GNUNET_CHAT_KIND_UNKNOWN == util_message_kind_from_kind(msg->header.kind)) || 1060 (GNUNET_OK != intern_provide_contact_for_member(handle, sender, NULL))) 1061 return; 1062 1063 const struct GNUNET_TIME_Absolute timestamp = GNUNET_TIME_absolute_ntoh( 1064 msg->header.timestamp 1065 ); 1066 1067 struct GNUNET_ShortHashCode shorthash; 1068 util_shorthash_from_member(sender, &shorthash); 1069 1070 struct GNUNET_CHAT_Contact *contact = GNUNET_CONTAINER_multishortmap_get( 1071 handle->contacts, &shorthash 1072 ); 1073 1074 if (flags & GNUNET_MESSENGER_FLAG_SENT) 1075 contact->owned = GNUNET_YES; 1076 1077 struct GNUNET_TIME_Absolute *time = GNUNET_CONTAINER_multishortmap_get( 1078 context->timestamps, &shorthash 1079 ); 1080 1081 if (!time) 1082 { 1083 time = GNUNET_new(struct GNUNET_TIME_Absolute); 1084 *time = timestamp; 1085 1086 if (GNUNET_OK != GNUNET_CONTAINER_multishortmap_put( 1087 context->timestamps, &shorthash, time, 1088 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) 1089 GNUNET_free(time); 1090 } 1091 else 1092 { 1093 const struct GNUNET_TIME_Relative delta = GNUNET_TIME_absolute_get_difference( 1094 timestamp, *time 1095 ); 1096 1097 if (GNUNET_TIME_relative_is_zero(delta)) 1098 *time = timestamp; 1099 } 1100 1101 const struct GNUNET_HashCode *dependency = NULL; 1102 1103 struct GNUNET_CHAT_Message *message = GNUNET_CONTAINER_multihashmap_get( 1104 context->messages, hash 1105 ); 1106 1107 if (message) 1108 { 1109 if (message->flags & GNUNET_MESSENGER_FLAG_DELETE) 1110 return; 1111 1112 message_update_msg (message, flags, msg); 1113 1114 if (0 == (message->flags & GNUNET_MESSENGER_FLAG_UPDATE)) 1115 return; 1116 1117 goto handle_callback; 1118 } 1119 1120 message = message_create_from_msg(context, hash, flags, msg); 1121 1122 if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put( 1123 context->messages, hash, message, 1124 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) 1125 { 1126 message_destroy(message); 1127 return; 1128 } 1129 1130 handle_callback: 1131 switch (msg->header.kind) 1132 { 1133 case GNUNET_MESSENGER_KIND_DELETION: 1134 { 1135 dependency = &(msg->body.deletion.hash); 1136 break; 1137 } 1138 case GNUNET_MESSENGER_KIND_TRANSCRIPT: 1139 { 1140 dependency = &(msg->body.transcript.hash); 1141 break; 1142 } 1143 case GNUNET_MESSENGER_KIND_TAG: 1144 { 1145 dependency = &(msg->body.tag.hash); 1146 break; 1147 } 1148 default: 1149 break; 1150 } 1151 1152 if ((dependency) && 1153 (GNUNET_YES != GNUNET_CONTAINER_multihashmap_contains(context->messages, dependency))) 1154 { 1155 GNUNET_CONTAINER_multihashmap_put( 1156 context->dependencies, 1157 dependency, 1158 message, 1159 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE 1160 ); 1161 1162 GNUNET_MESSENGER_get_message(room, dependency); 1163 return; 1164 } 1165 1166 on_handle_message_callback(message); 1167 } 1168 1169 int 1170 it_destroy_handle_groups (GNUNET_UNUSED void *cls, 1171 GNUNET_UNUSED const struct GNUNET_HashCode *key, 1172 void *value) 1173 { 1174 GNUNET_assert(value); 1175 1176 struct GNUNET_CHAT_Group *group = value; 1177 group_destroy(group); 1178 return GNUNET_YES; 1179 } 1180 1181 int 1182 it_destroy_handle_contacts (GNUNET_UNUSED void *cls, 1183 GNUNET_UNUSED const struct GNUNET_ShortHashCode *key, 1184 void *value) 1185 { 1186 GNUNET_assert(value); 1187 1188 struct GNUNET_CHAT_Contact *contact = value; 1189 contact_destroy(contact); 1190 return GNUNET_YES; 1191 } 1192 1193 int 1194 it_destroy_handle_contexts (GNUNET_UNUSED void *cls, 1195 GNUNET_UNUSED const struct GNUNET_HashCode *key, 1196 void *value) 1197 { 1198 GNUNET_assert(value); 1199 1200 struct GNUNET_CHAT_Context *context = value; 1201 context_destroy(context); 1202 return GNUNET_YES; 1203 } 1204 1205 int 1206 it_destroy_handle_files (GNUNET_UNUSED void *cls, 1207 GNUNET_UNUSED const struct GNUNET_HashCode *key, 1208 void *value) 1209 { 1210 GNUNET_assert(value); 1211 1212 struct GNUNET_CHAT_File *file = value; 1213 file_destroy(file); 1214 return GNUNET_YES; 1215 }