Comment: | Use dot syntax |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
abf66b5c9bef6eb979cc63a8036e6c02 |
User & Date: | js on 2019-03-16 20:58:13 |
Other Links: | manifest | tags |
2019-10-03
| ||
23:42 | Adjust to ObjFW changes check-in: eafae30990 user: js tags: trunk | |
2019-03-16
| ||
20:58 | Use dot syntax check-in: abf66b5c9b user: js tags: trunk | |
2019-01-27
| ||
15:29 | Fix connecting to servers with SRV records check-in: d5b95a3e8c user: js tags: trunk | |
Modified src/XMPPConnection.h from [8e9ce8e74b] to [3eae11716b].
︙ | ︙ | |||
143 144 145 146 147 148 149 | @end /*! * @brief A class which abstracts a connection to an XMPP service. */ @interface XMPPConnection: OFObject { | | | 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | @end /*! * @brief A class which abstracts a connection to an XMPP service. */ @interface XMPPConnection: OFObject { OFTCPSocket *_socket; char _buffer[XMPP_CONNECTION_BUFFER_LENGTH]; OFXMLParser *_parser, *_oldParser; OFXMLElementBuilder *_elementBuilder, *_oldElementBuilder; OFString *_Nullable _username, *_Nullable _password, *_Nullable _server; OFString *_Nullable _resource; bool _usesAnonymousAuthentication; OFString *_Nullable _privateKeyFile, *_Nullable _certificateFile; |
︙ | ︙ | |||
231 232 233 234 235 236 237 | * @brief An object for data storage, conforming to the XMPPStorage protocol. */ @property OF_NULLABLE_PROPERTY (nonatomic, assign) id <XMPPStorage> dataStorage; /*! * @brief The socket used for the connection. */ | | | 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 | * @brief An object for data storage, conforming to the XMPPStorage protocol. */ @property OF_NULLABLE_PROPERTY (nonatomic, assign) id <XMPPStorage> dataStorage; /*! * @brief The socket used for the connection. */ @property (readonly, nonatomic) OFTCPSocket *socket; /*! * @brief Whether encryption is required. */ @property (nonatomic) bool encryptionRequired; /*! |
︙ | ︙ |
Modified src/XMPPConnection.m from [c7bf092d14] to [c9e6b14c30].
1 | /* | | | 1 2 3 4 5 6 7 8 9 | /* * Copyright (c) 2010, 2011, 2012, 2013, 2015, 2016, 2017, 2018, 2019 * Jonathan Schleifer <js@heap.zone> * Copyright (c) 2011, 2012, Florian Zeitz <florob@babelmonkeys.de> * * https://heap.zone/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above |
︙ | ︙ | |||
139 140 141 142 143 144 145 | { OFString *old = _username; if (username != nil) { char *node; Stringprep_rc rc; | | | 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | { OFString *old = _username; if (username != nil) { char *node; Stringprep_rc rc; if ((rc = stringprep_profile(username.UTF8String, &node, "SASLprep", 0)) != STRINGPREP_OK) @throw [XMPPStringPrepFailedException exceptionWithConnection: self profile: @"SASLprep" string: username]; @try { |
︙ | ︙ | |||
165 166 167 168 169 170 171 | { OFString *old = _resource; if (resource != nil) { char *res; Stringprep_rc rc; | | | 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | { OFString *old = _resource; if (resource != nil) { char *res; Stringprep_rc rc; if ((rc = stringprep_profile(resource.UTF8String, &res, "Resourceprep", 0)) != STRINGPREP_OK) @throw [XMPPStringPrepFailedException exceptionWithConnection: self profile: @"Resourceprep" string: resource]; @try { |
︙ | ︙ | |||
204 205 206 207 208 209 210 | OFString *oldDomain = _domain; OFString *oldDomainToASCII = _domainToASCII; if (domain != nil) { char *srv; Stringprep_rc rc; | | | 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | OFString *oldDomain = _domain; OFString *oldDomainToASCII = _domainToASCII; if (domain != nil) { char *srv; Stringprep_rc rc; if ((rc = stringprep_profile(domain.UTF8String, &srv, "Nameprep", 0)) != STRINGPREP_OK) @throw [XMPPStringPrepFailedException exceptionWithConnection: self profile: @"Nameprep" string: domain]; @try { |
︙ | ︙ | |||
235 236 237 238 239 240 241 | { OFString *old = _password; if (password != nil) { char *pass; Stringprep_rc rc; | | | | | 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 | { OFString *old = _password; if (password != nil) { char *pass; Stringprep_rc rc; if ((rc = stringprep_profile(password.UTF8String, &pass, "SASLprep", 0)) != STRINGPREP_OK) @throw [XMPPStringPrepFailedException exceptionWithConnection: self profile: @"SASLprep" string: password]; @try { _password = [[OFString alloc] initWithUTF8String: pass]; } @finally { free(pass); } } else _password = nil; [old release]; } - (void)socket: (OFTCPSocket *)sock didConnectToHost: (OFString *)host port: (uint16_t)port exception: (id)exception { if (exception != nil) { if (_nextSRVRecords.count > 0) { [self xmpp_tryNextSRVRecord]; return; } [_delegates broadcastSelector: @selector(connection: didThrowException:) withObject: self |
︙ | ︙ | |||
282 283 284 285 286 287 288 | } - (void)xmpp_tryNextSRVRecord { OFSRVDNSResourceRecord *record = [[[_nextSRVRecords objectAtIndex: 0] copy] autorelease]; | | | | | | | | | 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 | } - (void)xmpp_tryNextSRVRecord { OFSRVDNSResourceRecord *record = [[[_nextSRVRecords objectAtIndex: 0] copy] autorelease]; if (_nextSRVRecords.count == 0) { [_nextSRVRecords release]; _nextSRVRecords = nil; } [_socket asyncConnectToHost: record.target port: record.port]; } - (void)resolver: (OFDNSResolver *)resolver didResolveDomainName: (OFString *)domainName answerRecords: (OFDictionary *)answerRecords authorityRecords: (OFDictionary *)authorityRecords additionalRecords: (OFDictionary *)additionalRecords exception: (id)exception { OFMutableArray *records = [OFMutableArray array]; if (exception != nil) { [_delegates broadcastSelector: @selector(connection:didThrowException:) withObject: self withObject: exception]; return; } for (OFDNSResourceRecord *record in [answerRecords objectForKey: domainName]) if ([record isKindOfClass: [OFSRVDNSResourceRecord class]]) [records addObject: record]; /* TODO: Sort records */ [records makeImmutable]; if (records.count == 0) { /* Fall back to A / AAAA record. */ [_socket asyncConnectToHost: _domainToASCII port: _port]; return; } [_nextSRVRecords release]; _nextSRVRecords = nil; _nextSRVRecords = [records mutableCopy]; [self xmpp_tryNextSRVRecord]; } - (void)asyncConnect { void *pool = objc_autoreleasePoolPush(); if (_socket != nil) @throw [OFAlreadyConnectedException exception]; _socket = [[OFTCPSocket alloc] init]; [_socket setDelegate: self]; if (_server != nil) [_socket asyncConnectToHost: _server port: _port]; else { OFString *SRVDomain = [_domainToASCII stringByPrependingString: @"_xmpp-client._tcp."]; [[OFThread DNSResolver] asyncResolveHost: SRVDomain recordClass: OF_DNS_RESOURCE_RECORD_CLASS_IN recordType: OF_DNS_RESOURCE_RECORD_TYPE_SRV delegate: self]; } objc_autoreleasePoolPop(pool); } - (bool)xmpp_parseBuffer: (const void *)buffer length: (size_t)length { if (_socket.atEndOfStream) { [_delegates broadcastSelector: @selector(connectionWasClosed: error:) withObject: self withObject: nil]; return false; } |
︙ | ︙ | |||
444 445 446 447 448 449 450 451 452 | } - (bool)checkCertificateAndGetReason: (OFString **)reason { X509Certificate *cert; OFDictionary *SANs; bool serviceSpecific = false; @try { | > | | | | | | | | | | | | | 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 | } - (bool)checkCertificateAndGetReason: (OFString **)reason { X509Certificate *cert; OFDictionary *SANs; bool serviceSpecific = false; SSLSocket *socket = (SSLSocket *)_socket; @try { [socket verifyPeerCertificate]; } @catch (SSLInvalidCertificateException *e) { if (reason != NULL) *reason = e.reason; return false; } cert = socket.peerCertificate; SANs = cert.subjectAlternativeName; if ([[SANs objectForKey: @"otherName"] objectForKey: OID_SRVName] != nil || [SANs objectForKey: @"dNSName"] != nil || [SANs objectForKey: @"uniformResourceIdentifier"] != nil) serviceSpecific = true; if ([cert hasSRVNameMatchingDomain: _domainToASCII service: @"xmpp-client"] || [cert hasDNSNameMatchingDomain: _domainToASCII]) return true; if (!serviceSpecific && [cert hasCommonNameMatchingDomain: _domainToASCII]) return true; return false; } - (void)sendStanza: (OFXMLElement *)element { [_delegates broadcastSelector: @selector(connection:didSendElement:) withObject: self withObject: element]; [_socket writeString: element.XMLString]; } - (void)sendIQ: (XMPPIQ *)IQ callbackTarget: (id)target selector: (SEL)selector { void *pool = objc_autoreleasePoolPush(); XMPPCallback *callback; OFString *ID, *key; if ((ID = IQ.ID) == nil) { ID = [self generateStanzaID]; IQ.ID = ID; } if ((key = IQ.to.fullJID) == nil) key = _JID.bareJID; if (key == nil) // Only happens for resource bind key = @"bind"; key = [key stringByAppendingString: ID]; callback = [XMPPCallback callbackWithTarget: target selector: selector]; [_callbacks setObject: callback |
︙ | ︙ | |||
521 522 523 524 525 526 527 | - (void)sendIQ: (XMPPIQ *)IQ callbackBlock: (xmpp_callback_block_t)block { void *pool = objc_autoreleasePoolPush(); XMPPCallback *callback; OFString *ID, *key; | | | | | | 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 | - (void)sendIQ: (XMPPIQ *)IQ callbackBlock: (xmpp_callback_block_t)block { void *pool = objc_autoreleasePoolPush(); XMPPCallback *callback; OFString *ID, *key; if ((ID = IQ.ID) == nil) { ID = [self generateStanzaID]; IQ.ID = ID; } if ((key = IQ.to.fullJID) == nil) key = _JID.bareJID; if (key == nil) // Connection not yet bound, can't send stanzas @throw [OFInvalidArgumentException exception]; key = [key stringByAppendingString: ID]; callback = [XMPPCallback callbackWithBlock: block]; [_callbacks setObject: callback forKey: key]; |
︙ | ︙ | |||
573 574 575 576 577 578 579 | if (![namespace isEqual: XMPP_NS_STREAM]) { [self xmpp_sendStreamError: @"invalid-namespace" text: nil]; return; } for (OFXMLAttribute *attribute in attributes) { | | | | | | | | | | | | | < | | | | | 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 | if (![namespace isEqual: XMPP_NS_STREAM]) { [self xmpp_sendStreamError: @"invalid-namespace" text: nil]; return; } for (OFXMLAttribute *attribute in attributes) { if ([attribute.name isEqual: @"from"] && ![attribute.stringValue isEqual: _domain]) { [self xmpp_sendStreamError: @"invalid-from" text: nil]; return; } if ([attribute.name isEqual: @"version"] && ![attribute.stringValue isEqual: @"1.0"]) { [self xmpp_sendStreamError: @"unsupported-version" text: nil]; return; } } parser.delegate = _elementBuilder; } - (void)elementBuilder: (OFXMLElementBuilder *)builder didBuildElement: (OFXMLElement *)element { /* Ignore whitespace elements */ if (element.name == nil) return; element.defaultNamespace = XMPP_NS_CLIENT; [element setPrefix: @"stream" forNamespace: XMPP_NS_STREAM]; [_delegates broadcastSelector: @selector(connection:didReceiveElement:) withObject: self withObject: element]; if ([element.namespace isEqual: XMPP_NS_CLIENT]) [self xmpp_handleStanza: element]; if ([element.namespace isEqual: XMPP_NS_STREAM]) [self xmpp_handleStream: element]; if ([element.namespace isEqual: XMPP_NS_STARTTLS]) [self xmpp_handleTLS: element]; if ([element.namespace isEqual: XMPP_NS_SASL]) [self xmpp_handleSASL: element]; } - (void)elementBuilder: (OFXMLElementBuilder *)builder didNotExpectCloseTag: (OFString *)name prefix: (OFString *)prefix namespace: (OFString *)ns { if (![name isEqual: @"stream"] || ![prefix isEqual: @"stream"] || ![ns isEqual: XMPP_NS_STREAM]) @throw [OFMalformedXMLException exception]; else [self close]; } - (void)xmpp_startStream { OFString *langString = @""; /* Make sure we don't get any old events */ _parser.delegate = nil; _elementBuilder.delegate = nil; /* * We can't release them now, as we are currently inside them. Release * them the next time the parser returns. */ _oldParser = _parser; _oldElementBuilder = _elementBuilder; _parser = [[OFXMLParser alloc] init]; _parser.delegate = self; _elementBuilder = [[XMPPXMLElementBuilder alloc] init]; _elementBuilder.delegate = self; if (_language != nil) langString = [OFString stringWithFormat: @"xml:lang='%@' ", _language]; [_socket writeFormat: @"<?xml version='1.0'?>\n" @"<stream:stream to='%@' " |
︙ | ︙ | |||
687 688 689 690 691 692 693 | _streamOpen = _needsSession = _encrypted = false; _supportsRosterVersioning = _supportsStreamManagement = false; _lastID = 0; } - (void)xmpp_handleStanza: (OFXMLElement *)element { | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 | _streamOpen = _needsSession = _encrypted = false; _supportsRosterVersioning = _supportsStreamManagement = false; _lastID = 0; } - (void)xmpp_handleStanza: (OFXMLElement *)element { if ([element.name isEqual: @"iq"]) { [self xmpp_handleIQ: [XMPPIQ stanzaWithElement: element]]; return; } if ([element.name isEqual: @"message"]) { [self xmpp_handleMessage: [XMPPMessage stanzaWithElement: element]]; return; } if ([element.name isEqual: @"presence"]) { [self xmpp_handlePresence: [XMPPPresence stanzaWithElement: element]]; return; } [self xmpp_sendStreamError: @"unsupported-stanza-type" text: nil]; } - (void)xmpp_handleStream: (OFXMLElement *)element { if ([element.name isEqual: @"features"]) { [self xmpp_handleFeatures: element]; return; } if ([element.name isEqual: @"error"]) { OFString *condition, *reason; [self close]; [_delegates broadcastSelector: @selector(connectionWasClosed:) withObject: self withObject: element]; condition = [[element elementsForNamespace: XMPP_NS_XMPP_STREAM].firstObject name]; if (condition == nil) condition = @"undefined"; reason = [element elementForName: @"text" namespace: XMPP_NS_XMPP_STREAM].stringValue; @throw [XMPPStreamErrorException exceptionWithConnection: self condition: condition reason: reason]; return; } assert(0); } - (void)xmpp_handleTLS: (OFXMLElement *)element { if ([element.name isEqual: @"proceed"]) { /* FIXME: Catch errors here */ SSLSocket *newSock; [_delegates broadcastSelector: @selector( connectionWillUpgradeToTLS:) withObject: self]; newSock = [[SSLSocket alloc] initWithSocket: _socket]; newSock.certificateVerificationEnabled = false; #if 0 /* FIXME: Not yet implemented by ObjOpenSSL */ [newSock setCertificateFile: _certificateFile]; [newSock setPrivateKeyFile: _privateKeyFile]; [newSock setPrivateKeyPassphrase: _privateKeyPassphrase]; #endif [newSock startTLSWithExpectedHost: nil]; [_socket release]; _socket = newSock; [_socket setDelegate: self]; _encrypted = true; [_delegates broadcastSelector: @selector( connectionDidUpgradeToTLS:) withObject: self]; /* Stream restart */ [self xmpp_startStream]; return; } if ([element.name isEqual: @"failure"]) /* TODO: Find/create an exception to throw here */ @throw [OFException exception]; assert(0); } - (void)xmpp_handleSASL: (OFXMLElement *)element { if ([element.name isEqual: @"challenge"]) { OFXMLElement *responseTag; OFData *challenge = [OFData dataWithBase64EncodedString: element.stringValue]; OFData *response = [_authModule continueWithData: challenge]; responseTag = [OFXMLElement elementWithName: @"response" namespace: XMPP_NS_SASL]; if (response) { if (response.count == 0) responseTag.stringValue = @"="; else responseTag.stringValue = response.stringByBase64Encoding; } [self sendStanza: responseTag]; return; } if ([element.name isEqual: @"success"]) { [_authModule continueWithData: [OFData dataWithBase64EncodedString: element.stringValue]]; [_delegates broadcastSelector: @selector( connectionWasAuthenticated:) withObject: self]; /* Stream restart */ [self xmpp_startStream]; return; } if ([element.name isEqual: @"failure"]) { /* FIXME: Do more parsing/handling */ @throw [XMPPAuthFailedException exceptionWithConnection: self reason: element.XMLString]; } assert(0); } - (void)xmpp_handleIQ: (XMPPIQ *)IQ { bool handled = false; XMPPCallback *callback; OFString *key; if ((key = IQ.from.fullJID) == nil) key = _JID.bareJID; if (key == nil) // Only happens for resource bind key = @"bind"; key = [key stringByAppendingString: IQ.ID]; if ((callback = [_callbacks objectForKey: key])) { [callback runWithIQ: IQ connection: self]; [_callbacks removeObjectForKey: key]; return; } handled = [_delegates broadcastSelector: @selector( connection:didReceiveIQ:) withObject: self withObject: IQ]; if (!handled && ![IQ.type isEqual: @"error"] && ![IQ.type isEqual: @"result"]) { [self sendStanza: [IQ errorIQWithType: @"cancel" condition: @"service-unavailable"]]; } } - (void)xmpp_handleMessage: (XMPPMessage *)message { |
︙ | ︙ | |||
909 910 911 912 913 914 915 | _supportsRosterVersioning = true; if ([element elementForName: @"sm" namespace: XMPP_NS_SM] != nil) _supportsStreamManagement = true; if (mechs != nil) { | | | | 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 | _supportsRosterVersioning = true; if ([element elementForName: @"sm" namespace: XMPP_NS_SM] != nil) _supportsStreamManagement = true; if (mechs != nil) { for (OFXMLElement *mech in mechs.children) [mechanisms addObject: mech.stringValue]; if (_usesAnonymousAuthentication) { if (![mechanisms containsObject: @"ANONYMOUS"]) @throw [XMPPAuthFailedException exceptionWithConnection: self reason: @"No supported " @"auth mechanism"]; |
︙ | ︙ | |||
989 990 991 992 993 994 995 | OFData *initialMessage = [_authModule initialMessage]; authTag = [OFXMLElement elementWithName: @"auth" namespace: XMPP_NS_SASL]; [authTag addAttributeWithName: @"mechanism" stringValue: authName]; if (initialMessage != nil) { | | | | | | 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 | OFData *initialMessage = [_authModule initialMessage]; authTag = [OFXMLElement elementWithName: @"auth" namespace: XMPP_NS_SASL]; [authTag addAttributeWithName: @"mechanism" stringValue: authName]; if (initialMessage != nil) { if (initialMessage.count == 0) authTag.stringValue = @"="; else authTag.stringValue = initialMessage.stringByBase64Encoding; } [self sendStanza: authTag]; } - (void)xmpp_sendResourceBind { |
︙ | ︙ | |||
1034 1035 1036 1037 1038 1039 1040 | elementWithName: @"error" namespace: XMPP_NS_STREAM]; [error setPrefix: @"stream" forNamespace: XMPP_NS_STREAM]; [error addChild: [OFXMLElement elementWithName: condition namespace: XMPP_NS_XMPP_STREAM]]; if (text) | | | | | | 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 | elementWithName: @"error" namespace: XMPP_NS_STREAM]; [error setPrefix: @"stream" forNamespace: XMPP_NS_STREAM]; [error addChild: [OFXMLElement elementWithName: condition namespace: XMPP_NS_XMPP_STREAM]]; if (text) [error addChild: [OFXMLElement elementWithName: @"text" namespace: XMPP_NS_XMPP_STREAM stringValue: text]]; _parser.delegate = nil; [self sendStanza: error]; [self close]; } - (void)xmpp_handleResourceBindForConnection: (XMPPConnection *)connection IQ: (XMPPIQ *)IQ { OFXMLElement *bindElement, *JIDElement; assert([IQ.type isEqual: @"result"]); bindElement = [IQ elementForName: @"bind" namespace: XMPP_NS_BIND]; assert(bindElement != nil); JIDElement = [bindElement elementForName: @"jid" namespace: XMPP_NS_BIND]; _JID = [[XMPPJID alloc] initWithString: JIDElement.stringValue]; if (_needsSession) { [self xmpp_sendSession]; return; } [_delegates broadcastSelector: @selector(connection:wasBoundToJID:) |
︙ | ︙ | |||
1085 1086 1087 1088 1089 1090 1091 | callbackTarget: self selector: @selector(xmpp_handleSessionForConnection:IQ:)]; } - (void)xmpp_handleSessionForConnection: (XMPPConnection *)connection IQ: (XMPPIQ *)IQ { | | | | 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 | callbackTarget: self selector: @selector(xmpp_handleSessionForConnection:IQ:)]; } - (void)xmpp_handleSessionForConnection: (XMPPConnection *)connection IQ: (XMPPIQ *)IQ { if (![IQ.type isEqual: @"result"]) OF_ENSURE(0); [_delegates broadcastSelector: @selector(connection:wasBoundToJID:) withObject: self withObject: _JID]; } - (OFString *)xmpp_IDNAToASCII: (OFString *)domain { OFString *ret; char *cDomain; Idna_rc rc; if ((rc = idna_to_ascii_8z(domain.UTF8String, &cDomain, IDNA_USE_STD3_ASCII_RULES)) != IDNA_SUCCESS) @throw [XMPPIDNATranslationFailedException exceptionWithConnection: self operation: @"ToASCII" string: domain]; @try { |
︙ | ︙ |
Modified src/XMPPContact+Private.h from [bbdffdaaee] to [235329a86f].
1 2 3 4 5 | #import "XMPPContact.h" OF_ASSUME_NONNULL_BEGIN @interface XMPPContact () | > | > > < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #import "XMPPContact.h" OF_ASSUME_NONNULL_BEGIN @interface XMPPContact () @property (readwrite, nonatomic, setter=xmpp_setRosterItem:) XMPPRosterItem *rosterItem; @property OF_NULLABLE_PROPERTY (retain, nonatomic) XMPPJID *xmpp_lockedOnJID; - (void)xmpp_setPresence: (XMPPPresence *)presence resource: (OFString *)resource; - (void)xmpp_removePresenceForResource: (OFString *)resource; @end OF_ASSUME_NONNULL_END |
Modified src/XMPPContact.m from [b77733fbe8] to [7b7906a48d].
1 2 | /* * Copyright (c) 2013, Florian Zeitz <florob@babelmonkeys.de> | | | 1 2 3 4 5 6 7 8 9 10 | /* * Copyright (c) 2013, Florian Zeitz <florob@babelmonkeys.de> * Copyright (c) 2013, 2016, 2019, Jonathan Schleifer <js@heap.zone> * * https://heap.zone/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. * |
︙ | ︙ | |||
53 54 55 56 57 58 59 | [super dealloc]; } - (void)sendMessage: (XMPPMessage *)message connection: (XMPPConnection *)connection { if (_lockedOnJID == nil) | | | | 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | [super dealloc]; } - (void)sendMessage: (XMPPMessage *)message connection: (XMPPConnection *)connection { if (_lockedOnJID == nil) message.to = _rosterItem.JID; else message.to = _lockedOnJID; [connection sendStanza: message]; } - (void)xmpp_setRosterItem: (XMPPRosterItem *)rosterItem { XMPPRosterItem *old = _rosterItem; |
︙ | ︙ | |||
77 78 79 80 81 82 83 | if (resource != nil) [_presences setObject: presence forKey: resource]; else [_presences setObject: presence forKey: @""]; | | | | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | if (resource != nil) [_presences setObject: presence forKey: resource]; else [_presences setObject: presence forKey: @""]; self.xmpp_lockedOnJID = nil; } - (void)xmpp_removePresenceForResource: (OFString *)resource { if (resource != nil) { [_presences removeObjectForKey: resource]; } else { [_presences release]; _presences = [[OFMutableDictionary alloc] init]; } self.xmpp_lockedOnJID = nil; } - (void)xmpp_setLockedOnJID: (XMPPJID *)JID; { XMPPJID *old = _lockedOnJID; _lockedOnJID = [JID retain]; [old release]; } @end |
Modified src/XMPPContactManager.m from [44efae0635] to [f3f3c997c4].
1 2 | /* * Copyright (c) 2013, Florian Zeitz <florob@babelmonkeys.de> | | | 1 2 3 4 5 6 7 8 9 10 | /* * Copyright (c) 2013, Florian Zeitz <florob@babelmonkeys.de> * Copyright (c) 2013, 2016, 2019, Jonathan Schleifer <js@heap.zone> * * https://heap.zone/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. * |
︙ | ︙ | |||
63 64 65 66 67 68 69 | [super dealloc]; } - (void)sendSubscribedToJID: (XMPPJID *)subscriber { XMPPPresence *presence = [XMPPPresence presenceWithType: @"subscribed"]; | | | | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | [super dealloc]; } - (void)sendSubscribedToJID: (XMPPJID *)subscriber { XMPPPresence *presence = [XMPPPresence presenceWithType: @"subscribed"]; presence.to = subscriber; [_connection sendStanza: presence]; } - (void)sendUnsubscribedToJID: (XMPPJID *)subscriber { XMPPPresence *presence = [XMPPPresence presenceWithType: @"unsubscribed"]; presence.to = subscriber; [_connection sendStanza: presence]; } - (void)addDelegate: (id <XMPPContactManagerDelegate>)delegate { [_delegates addDelegate: delegate]; } |
︙ | ︙ | |||
99 100 101 102 103 104 105 | withObject: self withObject: contact]; [_contacts release]; _contacts = nil; _contacts = [[OFMutableDictionary alloc] init]; | | < | | | | | | | | | | | | | | 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | withObject: self withObject: contact]; [_contacts release]; _contacts = nil; _contacts = [[OFMutableDictionary alloc] init]; rosterItems = roster.rosterItems; for (OFString *bareJID in rosterItems) { XMPPContact *contact = [[[XMPPContact alloc] init] autorelease]; contact.rosterItem = [rosterItems objectForKey: bareJID]; [_contacts setObject: contact forKey: bareJID]; [_delegates broadcastSelector: @selector(contactManager: didAddContact:) withObject: self withObject: contact]; } } - (void)roster: (XMPPRoster *)roster didReceiveRosterItem: (XMPPRosterItem *)rosterItem { XMPPContact *contact; OFString *bareJID = rosterItem.JID.bareJID; contact = [_contacts objectForKey: bareJID]; if ([rosterItem.subscription isEqual: @"remove"]) { if (contact != nil) [_delegates broadcastSelector: @selector(contactManager: didRemoveContact:) withObject: self withObject: contact]; [_contacts removeObjectForKey: bareJID]; return; } if (contact == nil) { contact = [[[XMPPContact alloc] init] autorelease]; contact.rosterItem = rosterItem; [_contacts setObject: contact forKey: bareJID]; [_delegates broadcastSelector: @selector(contactManager: didAddContact:) withObject: self withObject: contact]; } else { [_delegates broadcastSelector: @selector(contact: willUpdateWithRosterItem:) withObject: contact withObject: rosterItem]; contact.rosterItem = rosterItem; } } - (void)connection: (XMPPConnection *)connection didReceivePresence: (XMPPPresence *)presence { XMPPContact *contact; XMPPJID *JID = presence.from; OFString *type = presence.type; /* Subscription request */ if ([type isEqual: @"subscribe"]) { of_log(@"ObjXMPP: received subscription request"); [_delegates broadcastSelector: @selector(contactManager: didReceiveSubscriptionRequest:) withObject: self withObject: presence]; return; } contact = [_contacts objectForKey: JID.bareJID]; if (contact == nil) return; /* Available presence */ if ([type isEqual: @"available"]) { [contact xmpp_setPresence: presence resource: JID.resource]; [_delegates broadcastSelector: @selector(contact: didSendPresence:) withObject: contact withObject: presence]; return; } /* Unavailable presence */ if ([type isEqual: @"unavailable"]) { [contact xmpp_removePresenceForResource: JID.resource]; [_delegates broadcastSelector: @selector(contact: didSendPresence:) withObject: contact withObject: presence]; return; } } - (void)connection: (XMPPConnection *)connection didReceiveMessage: (XMPPMessage *)message { XMPPJID *JID = message.from; XMPPContact *contact = [_contacts objectForKey: JID.bareJID]; if (contact == nil) return; contact.xmpp_lockedOnJID = JID; [_delegates broadcastSelector: @selector(contact:didSendMessage:) withObject: contact withObject: message]; } @end |
Modified src/XMPPDiscoEntity.m from [d5ee52dfea] to [d3e937014c].
1 2 | /* * Copyright (c) 2013, Florian Zeitz <florob@babelmonkeys.de> | | | 1 2 3 4 5 6 7 8 9 10 | /* * Copyright (c) 2013, Florian Zeitz <florob@babelmonkeys.de> * Copyright (c) 2013, 2016, 2019, Jonathan Schleifer <js@heap.zone> * * https://heap.zone/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. * |
︙ | ︙ | |||
104 105 106 107 108 109 110 | [super dealloc]; } - (void)addDiscoNode: (XMPPDiscoNode *)node { [_discoNodes setObject: node | | | | | | | | | | | < | < | | | 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | [super dealloc]; } - (void)addDiscoNode: (XMPPDiscoNode *)node { [_discoNodes setObject: node forKey: node.node]; } - (OFString *)capsHash { OFMutableString *caps = [OFMutableString string]; OFSHA1Hash *hash = [OFSHA1Hash cryptoHash]; OFData *digest; for (XMPPDiscoIdentity *identity in _identities) [caps appendFormat: @"%@/%@//%@<", identity.category, identity.type, identity.name]; for (OFString *feature in _features) [caps appendFormat: @"%@<", feature]; [hash updateWithBuffer: caps.UTF8String length: caps.UTF8StringLength]; digest = [OFData dataWithItems: hash.digest count: hash.digestSize]; return digest.stringByBase64Encoding; } - (void)connection: (XMPPConnection *)connection wasBoundToJID: (XMPPJID *)JID { _JID = [JID copy]; } - (bool)connection: (XMPPConnection *)connection didReceiveIQ: (XMPPIQ *)IQ { if (![IQ.to isEqual: _JID]) return false; OFXMLElement *query = [IQ elementForName: @"query" namespace: XMPP_NS_DISCO_ITEMS]; if (query != nil) { OFString *node = [query attributeForName: @"node"].stringValue; if (node == nil) return [self xmpp_handleItemsIQ: IQ connection: connection]; XMPPDiscoNode *responder = [_discoNodes objectForKey: node]; if (responder != nil) return [responder xmpp_handleItemsIQ: IQ connection: connection]; return false; } query = [IQ elementForName: @"query" namespace: XMPP_NS_DISCO_INFO]; if (query != nil) { OFString *node = [query attributeForName: @"node"].stringValue; if (node == nil) return [self xmpp_handleInfoIQ: IQ connection: connection]; OFString *capsNode = [_capsNode stringByAppendingFormat: @"#%@", self.capsHash]; if ([capsNode isEqual: node]) return [self xmpp_handleInfoIQ: IQ connection: connection]; XMPPDiscoNode *responder = [_discoNodes objectForKey: node]; if (responder != nil) return [responder xmpp_handleInfoIQ: IQ |
︙ | ︙ |
Modified src/XMPPDiscoIdentity.m from [e06950dbbc] to [45b58d387c].
1 2 | /* * Copyright (c) 2013, Florian Zeitz <florob@babelmonkeys.de> | | | 1 2 3 4 5 6 7 8 9 10 | /* * Copyright (c) 2013, Florian Zeitz <florob@babelmonkeys.de> * Copyright (c) 2013, 2016, 2019, Jonathan Schleifer <js@heap.zone> * * https://heap.zone/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. * |
︙ | ︙ | |||
48 49 50 51 52 53 54 | { self = [super init]; @try { if (category == nil || type == nil) @throw [OFInvalidArgumentException exception]; | | | | | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | { self = [super init]; @try { if (category == nil || type == nil) @throw [OFInvalidArgumentException exception]; _category = category.copy; _name = name.copy; _type = type.copy; } @catch (id e) { [self release]; @throw e; } return self; } |
︙ | ︙ | |||
107 108 109 110 111 112 113 | - (uint32_t)hash { uint32_t hash; OF_HASH_INIT(hash); | | | | | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | - (uint32_t)hash { uint32_t hash; OF_HASH_INIT(hash); OF_HASH_ADD_HASH(hash, _category.hash); OF_HASH_ADD_HASH(hash, _type.hash); OF_HASH_ADD_HASH(hash, _name.hash); OF_HASH_FINALIZE(hash); return hash; } - (of_comparison_result_t)compare: (id <OFComparing>)object |
︙ | ︙ |
Modified src/XMPPDiscoNode.m from [fb7f062274] to [270f41c89f].
1 2 | /* * Copyright (c) 2013, Florian Zeitz <florob@babelmonkeys.de> | | | 1 2 3 4 5 6 7 8 9 10 | /* * Copyright (c) 2013, Florian Zeitz <florob@babelmonkeys.de> * Copyright (c) 2013, 2016, 2019, Jonathan Schleifer <js@heap.zone> * * https://heap.zone/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. * |
︙ | ︙ | |||
113 114 115 116 117 118 119 | { [_features insertObject: feature]; } - (void)addChildNode: (XMPPDiscoNode *)node { [_childNodes setObject: node | | | 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | { [_features insertObject: feature]; } - (void)addChildNode: (XMPPDiscoNode *)node { [_childNodes setObject: node forKey: node.node]; } - (bool)xmpp_handleItemsIQ: (XMPPIQ *)IQ connection: (XMPPConnection *)connection { XMPPIQ *resultIQ; OFXMLElement *response; |
︙ | ︙ | |||
139 140 141 142 143 144 145 | for (XMPPDiscoNode *child in _childNodes) { OFXMLElement *item = [OFXMLElement elementWithName: @"item" namespace: XMPP_NS_DISCO_ITEMS]; [item addAttributeWithName: @"jid" | | | | | | | 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | for (XMPPDiscoNode *child in _childNodes) { OFXMLElement *item = [OFXMLElement elementWithName: @"item" namespace: XMPP_NS_DISCO_ITEMS]; [item addAttributeWithName: @"jid" stringValue: child.JID.fullJID]; if (child.node != nil) [item addAttributeWithName: @"node" stringValue: child.node]; if (child.name != nil) [item addAttributeWithName: @"name" stringValue: child.name]; [response addChild: item]; } [connection sendStanza: resultIQ]; return true; |
︙ | ︙ | |||
172 173 174 175 176 177 178 | for (XMPPDiscoIdentity *identity in _identities) { OFXMLElement *identityElement = [OFXMLElement elementWithName: @"identity" namespace: XMPP_NS_DISCO_INFO]; [identityElement addAttributeWithName: @"category" | | | | | | 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | for (XMPPDiscoIdentity *identity in _identities) { OFXMLElement *identityElement = [OFXMLElement elementWithName: @"identity" namespace: XMPP_NS_DISCO_INFO]; [identityElement addAttributeWithName: @"category" stringValue: identity.category]; [identityElement addAttributeWithName: @"type" stringValue: identity.type]; if (identity.name != nil) [identityElement addAttributeWithName: @"name" stringValue: identity.name]; [response addChild: identityElement]; } for (OFString *feature in _features) { OFXMLElement *featureElement = [OFXMLElement elementWithName: @"feature" |
︙ | ︙ |
Modified src/XMPPEXTERNALAuth.m from [09b7294848] to [e1c935f11f].
1 2 3 4 5 6 7 8 9 | /* * Copyright (c) 2012, Florian Zeitz <florob@babelmonkeys.de> * * https://heap.zone/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. * | > | 1 2 3 4 5 6 7 8 9 10 | /* * Copyright (c) 2012, Florian Zeitz <florob@babelmonkeys.de> * Copyright (c) 2019, Jonathan Schleifer <js@heap.zone> * * https://heap.zone/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. * |
︙ | ︙ | |||
40 41 42 43 44 45 46 | - (OFData *)initialMessage { OFMutableData *message = [OFMutableData data]; /* authzid */ if (_authzid != nil) | | | | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | - (OFData *)initialMessage { OFMutableData *message = [OFMutableData data]; /* authzid */ if (_authzid != nil) [message addItems: _authzid.UTF8String count: _authzid.UTF8StringLength]; [message makeImmutable]; return message; } @end |
Modified src/XMPPFileStorage.m from [f704cad7e3] to [51fa277e00].
1 | /* | | | 1 2 3 4 5 6 7 8 9 | /* * Copyright (c) 2012, 2019, Jonathan Schleifer <js@webkeks.org> * * https://heap.zone/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. * |
︙ | ︙ | |||
46 47 48 49 50 51 52 | self = [super init]; @try { OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; _file = [file copy]; @try { | | | | 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | self = [super init]; @try { OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; _file = [file copy]; @try { _data = [[OFData dataWithContentsOfFile: file] .messagePackValue copy]; } @catch (id e) { _data = [[OFMutableDictionary alloc] init]; } [pool release]; } @catch (id e) { [self release]; |
︙ | ︙ | |||
71 72 73 74 75 76 77 | [_data release]; [super dealloc]; } - (void)save { | | | | | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | [_data release]; [super dealloc]; } - (void)save { [_data.messagePackRepresentation writeToFile: _file]; } - (void)xmpp_setObject: (id)object forPath: (OFString *)path { OFArray *pathComponents = [path componentsSeparatedByString: @"."]; OFMutableDictionary *iter = _data; size_t i = 0, components = pathComponents.count; for (OFString *component in pathComponents) { if (i++ == components - 1) continue; OFMutableDictionary *iter2 = [iter objectForKey: component]; if (iter2 == nil) { iter2 = [OFMutableDictionary dictionary]; [iter setObject: iter2 forKey: component]; } iter = iter2; } if (object != nil) [iter setObject: object forKey: [pathComponents lastObject]]; else [iter removeObjectForKey: pathComponents.lastObject]; } - (id)xmpp_objectForPath: (OFString *)path { id object = _data; for (OFString *component in [path componentsSeparatedByString: @"."]) |
︙ | ︙ |
Modified src/XMPPIQ.m from [beaa481153] to [a98a2208b8].
1 | /* | | | 1 2 3 4 5 6 7 8 9 | /* * Copyright (c) 2011, 2019, Jonathan Schleifer <js@webkeks.org> * Copyright (c) 2011, Florian Zeitz <florob@babelmonkeys.de> * * https://heap.zone/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. |
︙ | ︙ | |||
52 53 54 55 56 57 58 | return self; } - (XMPPIQ *)resultIQ { XMPPIQ *ret = [XMPPIQ IQWithType: @"result" | | | | | | | | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | return self; } - (XMPPIQ *)resultIQ { XMPPIQ *ret = [XMPPIQ IQWithType: @"result" ID: self.ID]; ret.to = self.from; ret.from = nil; return ret; } - (XMPPIQ *)errorIQWithType: (OFString *)type condition: (OFString *)condition text: (OFString *)text { XMPPIQ *ret = [XMPPIQ IQWithType: @"error" ID: self.ID]; void *pool = objc_autoreleasePoolPush(); OFXMLElement *error = [OFXMLElement elementWithName: @"error" namespace: XMPP_NS_CLIENT]; [error addAttributeWithName: @"type" stringValue: type]; [error addChild: [OFXMLElement elementWithName: condition namespace: XMPP_NS_STANZAS]]; if (text) [error addChild: [OFXMLElement elementWithName: @"text" namespace: XMPP_NS_STANZAS stringValue: text]]; [ret addChild: error]; ret.to = self.from; ret.from = nil; objc_autoreleasePoolPop(pool); return ret; } - (XMPPIQ *)errorIQWithType: (OFString *)type |
︙ | ︙ |
Modified src/XMPPJID.m from [02575a8c15] to [25d212cac9].
1 | /* | | | 1 2 3 4 5 6 7 8 9 | /* * Copyright (c) 2011, 2012, 2013, 2019, Jonathan Schleifer <js@heap.zone> * Copyright (c) 2011, 2012, 2013, Florian Zeitz <florob@babelmonkeys.de> * * https://heap.zone/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. |
︙ | ︙ | |||
41 42 43 44 45 46 47 | + (instancetype)JIDWithString: (OFString *)string { return [[[self alloc] initWithString: string] autorelease]; } - (instancetype)initWithString: (OFString *)string { | < < | | < | > > | | | | | | | | | | | | | | | | | > > > > | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | + (instancetype)JIDWithString: (OFString *)string { return [[[self alloc] initWithString: string] autorelease]; } - (instancetype)initWithString: (OFString *)string { self = [super init]; @try { size_t nodesep, resourcesep; if (string == nil) @throw [OFInvalidArgumentException exception]; nodesep = [string rangeOfString: @"@"].location; resourcesep = [string rangeOfString: @"/"].location; if (nodesep == SIZE_MAX) self.node = nil; else self.node = [string substringWithRange: of_range(0, nodesep)]; if (resourcesep == SIZE_MAX) { self.resource = nil; resourcesep = string.length; } else { of_range_t range = of_range(resourcesep + 1, string.length - resourcesep - 1); self.resource = [string substringWithRange: range]; } self.domain = [string substringWithRange: of_range(nodesep + 1, resourcesep - nodesep - 1)]; } @catch (id e) { [self release]; @throw e; } return self; } - (void)dealloc { [_node release]; |
︙ | ︙ | |||
111 112 113 114 115 116 117 | if (node == nil) { [old release]; _node = nil; return; } | | | 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | if (node == nil) { [old release]; _node = nil; return; } if (((rc = stringprep_profile(node.UTF8String, &nodepart, "Nodeprep", 0)) != STRINGPREP_OK) || (nodepart[0] == '\0') || (strlen(nodepart) > 1023)) @throw [XMPPStringPrepFailedException exceptionWithConnection: nil profile: @"Nodeprep" string: node]; |
︙ | ︙ | |||
134 135 136 137 138 139 140 | - (void)setDomain: (OFString *)domain { OFString *old = _domain; char *srv; Stringprep_rc rc; | | | 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | - (void)setDomain: (OFString *)domain { OFString *old = _domain; char *srv; Stringprep_rc rc; if (((rc = stringprep_profile(domain.UTF8String, &srv, "Nameprep", 0)) != STRINGPREP_OK) || (srv[0] == '\0') || (strlen(srv) > 1023)) @throw [XMPPStringPrepFailedException exceptionWithConnection: nil profile: @"Nameprep" string: domain]; |
︙ | ︙ | |||
163 164 165 166 167 168 169 | if (resource == nil) { [old release]; _resource = nil; return; } | | | 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | if (resource == nil) { [old release]; _resource = nil; return; } if (((rc = stringprep_profile(resource.UTF8String, &res, "Resourceprep", 0)) != STRINGPREP_OK) || (res[0] == '\0') || (strlen(res) > 1023)) @throw [XMPPStringPrepFailedException exceptionWithConnection: nil profile: @"Resourceprep" string: resource]; |
︙ | ︙ | |||
192 193 194 195 196 197 198 | return [OFString stringWithFormat: @"%@", _domain]; } - (OFString *)fullJID { /* If we don't have a resource, the full JID is equal to the bare JID */ if (_resource == nil) | | | | | 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | return [OFString stringWithFormat: @"%@", _domain]; } - (OFString *)fullJID { /* If we don't have a resource, the full JID is equal to the bare JID */ if (_resource == nil) return self.bareJID; if (_node != nil) return [OFString stringWithFormat: @"%@@%@/%@", _node, _domain, _resource]; else return [OFString stringWithFormat: @"%@/%@", _domain, _resource]; } - (OFString *)description { return [self fullJID]; } |
︙ | ︙ | |||
234 235 236 237 238 239 240 | - (uint32_t)hash { uint32_t hash; OF_HASH_INIT(hash); | | | | | 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | - (uint32_t)hash { uint32_t hash; OF_HASH_INIT(hash); OF_HASH_ADD_HASH(hash, _node.hash); OF_HASH_ADD_HASH(hash, _domain.hash); OF_HASH_ADD_HASH(hash, _resource.hash); OF_HASH_FINALIZE(hash); return hash; } @end |
Modified src/XMPPMessage.m from [49f1d7eed0] to [5fedee525e].
1 | /* | | | 1 2 3 4 5 6 7 8 9 | /* * Copyright (c) 2011, 2012, 2013, 2019, Jonathan Schleifer <js@heap.zone> * Copyright (c) 2011, Florian Zeitz <florob@babelmonkeys.de> * * https://heap.zone/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. |
︙ | ︙ | |||
87 88 89 90 91 92 93 | [self addChild: [OFXMLElement elementWithName: @"body" namespace: XMPP_NS_CLIENT stringValue: body]]; } - (OFString *)body { | | | | 87 88 89 90 91 92 93 94 95 96 97 | [self addChild: [OFXMLElement elementWithName: @"body" namespace: XMPP_NS_CLIENT stringValue: body]]; } - (OFString *)body { return [self elementForName: @"body" namespace: XMPP_NS_CLIENT].stringValue; } @end |
Modified src/XMPPMulticastDelegate.m from [6b55dc1bbf] to [7d0f01d54a].
1 | /* | | | 1 2 3 4 5 6 7 8 9 | /* * Copyright (c) 2012, 2019, Jonathan Schleifer <js@webkeks.org> * * https://heap.zone/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. * |
︙ | ︙ | |||
53 54 55 56 57 58 59 | - (void)addDelegate: (id)delegate { [_delegates addItem: &delegate]; } - (void)removeDelegate: (id)delegate { | | | | | | 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | - (void)addDelegate: (id)delegate { [_delegates addItem: &delegate]; } - (void)removeDelegate: (id)delegate { id const *items = _delegates.items; size_t i, count = _delegates.count; for (i = 0; i < count; i++) { if (items[i] != delegate) continue; [_delegates removeItemAtIndex: i]; return; } } - (bool)broadcastSelector: (SEL)selector withObject: (id)object { void *pool = objc_autoreleasePoolPush(); OFMutableData *currentDelegates = [[_delegates copy] autorelease]; id const *items = currentDelegates.items; size_t i, count = currentDelegates.count; bool handled = false; for (i = 0; i < count; i++) { id responder = items[i]; if (![responder respondsToSelector: selector]) continue; |
︙ | ︙ | |||
97 98 99 100 101 102 103 | - (bool)broadcastSelector: (SEL)selector withObject: (id)object1 withObject: (id)object2 { void *pool = objc_autoreleasePoolPush(); OFMutableData *currentDelegates = [[_delegates copy] autorelease]; | | | | 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | - (bool)broadcastSelector: (SEL)selector withObject: (id)object1 withObject: (id)object2 { void *pool = objc_autoreleasePoolPush(); OFMutableData *currentDelegates = [[_delegates copy] autorelease]; id const *items = currentDelegates.items; size_t i, count = currentDelegates.count; bool handled = false; for (i = 0; i < count; i++) { id responder = items[i]; if (![responder respondsToSelector: selector]) continue; |
︙ | ︙ |
Modified src/XMPPPLAINAuth.m from [4ce23edb9d] to [aab4070d6d].
1 2 3 4 5 6 7 8 9 | /* * Copyright (c) 2011, Florian Zeitz <florob@babelmonkeys.de> * * https://heap.zone/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. * | > | 1 2 3 4 5 6 7 8 9 10 | /* * Copyright (c) 2011, Florian Zeitz <florob@babelmonkeys.de> * Copyright (c) 2019, Jonathan Schleifer <js@webkeks.org> * * https://heap.zone/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. * |
︙ | ︙ | |||
44 45 46 47 48 49 50 | - (OFData *)initialMessage { OFMutableData *message = [OFMutableData data]; /* authzid */ if (_authzid != nil) | | | | | | | | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | - (OFData *)initialMessage { OFMutableData *message = [OFMutableData data]; /* authzid */ if (_authzid != nil) [message addItems: _authzid.UTF8String count: _authzid.UTF8StringLength]; /* separator */ [message addItem: ""]; /* authcid */ [message addItems: _authcid.UTF8String count: _authcid.UTF8StringLength]; /* separator */ [message addItem: ""]; /* passwd */ [message addItems: _password.UTF8String count: _password.UTF8StringLength]; [message makeImmutable]; return message; } @end |
Modified src/XMPPPresence.m from [d9aa17b963] to [47e770589e].
1 | /* | | | 1 2 3 4 5 6 7 8 9 | /* * Copyright (c) 2011, 2012, 2013, 2016, 2019, Jonathan Schleifer <js@heap.zone> * Copyright (c) 2011, 2012, 2013, Florian Zeitz <florob@babelmonkeys.de> * * https://heap.zone/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. |
︙ | ︙ | |||
26 27 28 29 30 31 32 | #include <inttypes.h> #import "XMPPPresence.h" #import "namespaces.h" /* This provides us with sortable values for show values */ static int | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | #include <inttypes.h> #import "XMPPPresence.h" #import "namespaces.h" /* This provides us with sortable values for show values */ static int showToInt(OFString *show) { if ([show isEqual: @"chat"]) return 0; if (show == nil) return 1; /* available */ if ([show isEqual: @"away"]) return 2; |
︙ | ︙ | |||
102 103 104 105 106 107 108 | self = [super initWithElement: element]; @try { OFXMLElement *subElement; if ((subElement = [element elementForName: @"show" namespace: XMPP_NS_CLIENT])) | | | | | < | 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | self = [super initWithElement: element]; @try { OFXMLElement *subElement; if ((subElement = [element elementForName: @"show" namespace: XMPP_NS_CLIENT])) self.show = subElement.stringValue; if ((subElement = [element elementForName: @"status" namespace: XMPP_NS_CLIENT])) self.status = subElement.stringValue; if ((subElement = [element elementForName: @"priority" namespace: XMPP_NS_CLIENT])) self.priority = [OFNumber numberWithIntMax: subElement.decimalValue]; } @catch (id e) { [self release]; @throw e; } return self; } |
︙ | ︙ | |||
170 171 172 173 174 175 176 | old = _status; _status = [status copy]; [old release]; } - (void)setPriority: (OFNumber *)priority { | | | | 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | old = _status; _status = [status copy]; [old release]; } - (void)setPriority: (OFNumber *)priority { intmax_t prio = priority.intMaxValue; OFNumber *old; if ((prio < -128) || (prio > 127)) @throw [OFInvalidArgumentException exception]; OFXMLElement *oldPriority = [self elementForName: @"priority" namespace: XMPP_NS_CLIENT]; if (oldPriority != nil) [self removeChild: oldPriority]; OFString *priority_s = [OFString stringWithFormat: @"%" @PRId8, priority.int8Value]; [self addChild: [OFXMLElement elementWithName: @"priority" namespace: XMPP_NS_CLIENT stringValue: priority_s]]; old = _priority; _priority = [priority copy]; [old release]; |
︙ | ︙ | |||
207 208 209 210 211 212 213 | if (object == self) return OF_ORDERED_SAME; if (![(id)object isKindOfClass: [XMPPPresence class]]) @throw [OFInvalidArgumentException exception]; otherPresence = (XMPPPresence *)object; | | | | | 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 | if (object == self) return OF_ORDERED_SAME; if (![(id)object isKindOfClass: [XMPPPresence class]]) @throw [OFInvalidArgumentException exception]; otherPresence = (XMPPPresence *)object; otherPriority = otherPresence.priority; if (otherPriority == nil) otherPriority = [OFNumber numberWithInt8: 0]; if (_priority != nil) priorityOrder = [_priority compare: otherPriority]; else priorityOrder = [[OFNumber numberWithInt8: 0] compare: otherPriority]; if (priorityOrder != OF_ORDERED_SAME) return priorityOrder; otherShow = otherPresence.show; if ([_show isEqual: otherShow]) return OF_ORDERED_SAME; if (showToInt(_show) < showToInt(otherShow)) return OF_ORDERED_ASCENDING; return OF_ORDERED_DESCENDING; } @end |
Modified src/XMPPRoster.m from [10ea13abbb] to [7c662ebfbc].
1 | /* | | | 1 2 3 4 5 6 7 8 9 | /* * Copyright (c) 2011, 2012, 2013, 2016, 2019, Jonathan Schleifer <js@heap.zone> * Copyright (c) 2012, Florian Zeitz <florob@babelmonkeys.de> * * https://heap.zone/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. |
︙ | ︙ | |||
62 63 64 65 66 67 68 | self = [super init]; @try { _rosterItems = [[OFMutableDictionary alloc] init]; _connection = connection; [_connection addDelegate: self]; _delegates = [[XMPPMulticastDelegate alloc] init]; | | | | | | | | | | | | | 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | self = [super init]; @try { _rosterItems = [[OFMutableDictionary alloc] init]; _connection = connection; [_connection addDelegate: self]; _delegates = [[XMPPMulticastDelegate alloc] init]; _dataStorage = _connection.dataStorage; } @catch (id e) { [self release]; @throw e; } return self; } - (void)dealloc { [_connection removeDelegate: self]; [_delegates release]; [_rosterItems release]; [super dealloc]; } - (void)requestRoster { XMPPIQ *IQ; OFXMLElement *query; _rosterRequested = true; IQ = [XMPPIQ IQWithType: @"get" ID: [_connection generateStanzaID]]; query = [OFXMLElement elementWithName: @"query" namespace: XMPP_NS_ROSTER]; if (_connection.supportsRosterVersioning) { OFString *ver = [_dataStorage stringValueForPath: @"roster.ver"]; if (ver == nil) ver = @""; [query addAttributeWithName: @"ver" stringValue: ver]; } [IQ addChild: query]; [_connection sendIQ: IQ callbackTarget: self selector: @selector(xmpp_handleInitialRosterForConnection: IQ:)]; } - (bool)connection: (XMPPConnection *)connection didReceiveIQ: (XMPPIQ *)IQ { OFXMLElement *rosterElement; OFXMLElement *element; XMPPRosterItem *rosterItem; OFString *origin; rosterElement = [IQ elementForName: @"query" namespace: XMPP_NS_ROSTER]; if (rosterElement == nil) return false; if (![IQ.type isEqual: @"set"]) return false; // Ensure the roster push has been sent by the server origin = IQ.from.fullJID; if (origin != nil && ![origin isEqual: connection.JID.bareJID]) return false; element = [rosterElement elementForName: @"item" namespace: XMPP_NS_ROSTER]; if (element != nil) { rosterItem = [self xmpp_rosterItemWithXMLElement: element]; [_delegates broadcastSelector: @selector( roster:didReceiveRosterItem:) withObject: self withObject: rosterItem]; [self xmpp_updateRosterItem: rosterItem]; } if (_connection.supportsRosterVersioning) { OFString *ver = [rosterElement attributeForName: @"ver"].stringValue; [_dataStorage setStringValue: ver forPath: @"roster.ver"]; [_dataStorage save]; } [connection sendStanza: [IQ resultIQ]]; |
︙ | ︙ | |||
176 177 178 179 180 181 182 | ID: [_connection generateStanzaID]]; OFXMLElement *query = [OFXMLElement elementWithName: @"query" namespace: XMPP_NS_ROSTER]; OFXMLElement *item = [OFXMLElement elementWithName: @"item" namespace: XMPP_NS_ROSTER]; [item addAttributeWithName: @"jid" | | | | | | | 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | ID: [_connection generateStanzaID]]; OFXMLElement *query = [OFXMLElement elementWithName: @"query" namespace: XMPP_NS_ROSTER]; OFXMLElement *item = [OFXMLElement elementWithName: @"item" namespace: XMPP_NS_ROSTER]; [item addAttributeWithName: @"jid" stringValue: rosterItem.JID.bareJID]; if (rosterItem.name != nil) [item addAttributeWithName: @"name" stringValue: rosterItem.name]; for (OFString *group in rosterItem.groups) [item addChild: [OFXMLElement elementWithName: @"group" namespace: XMPP_NS_ROSTER stringValue: group]]; [query addChild: item]; [IQ addChild: query]; [_connection sendStanza: IQ]; } - (void)deleteRosterItem: (XMPPRosterItem *)rosterItem { XMPPIQ *IQ = [XMPPIQ IQWithType: @"set" ID: [_connection generateStanzaID]]; OFXMLElement *query = [OFXMLElement elementWithName: @"query" namespace: XMPP_NS_ROSTER]; OFXMLElement *item = [OFXMLElement elementWithName: @"item" namespace: XMPP_NS_ROSTER]; [item addAttributeWithName: @"jid" stringValue: rosterItem.JID.bareJID]; [item addAttributeWithName: @"subscription" stringValue: @"remove"]; [query addChild: item]; [IQ addChild: query]; [_connection sendStanza: IQ]; |
︙ | ︙ | |||
233 234 235 236 237 238 239 | @throw [OFInvalidArgumentException exception]; _dataStorage = dataStorage; } - (void)xmpp_updateRosterItem: (XMPPRosterItem *)rosterItem { | | | | | | | | | | | | | | | < | | < | | | | | | < | | | | | | | | | | | | | 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 | @throw [OFInvalidArgumentException exception]; _dataStorage = dataStorage; } - (void)xmpp_updateRosterItem: (XMPPRosterItem *)rosterItem { if (_connection.supportsRosterVersioning) { OFMutableDictionary *items = [[[_dataStorage dictionaryForPath: @"roster.items"] mutableCopy] autorelease]; if (items == nil) items = [OFMutableDictionary dictionary]; if (![rosterItem.subscription isEqual: @"remove"]) { OFMutableDictionary *item = [OFMutableDictionary dictionaryWithKeysAndObjects: @"JID", rosterItem.JID.bareJID, @"subscription", rosterItem.subscription, nil]; if (rosterItem.name != nil) [item setObject: rosterItem.name forKey: @"name"]; if ([rosterItem groups] != nil) [item setObject: rosterItem.groups forKey: @"groups"]; [items setObject: item forKey: rosterItem.JID.bareJID]; } else [items removeObjectForKey: rosterItem.JID.bareJID]; [_dataStorage setDictionary: items forPath: @"roster.items"]; } if (![rosterItem.subscription isEqual: @"remove"]) [_rosterItems setObject: rosterItem forKey: rosterItem.JID.bareJID]; else [_rosterItems removeObjectForKey: rosterItem.JID.bareJID]; } - (XMPPRosterItem *)xmpp_rosterItemWithXMLElement: (OFXMLElement *)element { OFString *subscription; OFMutableArray *groups = [OFMutableArray array]; XMPPRosterItem *rosterItem = [XMPPRosterItem rosterItem]; rosterItem.JID = [XMPPJID JIDWithString: [element attributeForName: @"jid"].stringValue]; rosterItem.name = [element attributeForName: @"name"].stringValue; subscription = [element attributeForName: @"subscription"].stringValue; if (![subscription isEqual: @"none"] && ![subscription isEqual: @"to"] && ![subscription isEqual: @"from"] && ![subscription isEqual: @"both"] && ![subscription isEqual: @"remove"]) subscription = @"none"; rosterItem.subscription = subscription; for (OFXMLElement *groupElement in [element elementsForName: @"group" namespace: XMPP_NS_ROSTER]) [groups addObject: groupElement.stringValue]; if (groups.count > 0) rosterItem.groups = groups; return rosterItem; } - (void)xmpp_handleInitialRosterForConnection: (XMPPConnection *)connection IQ: (XMPPIQ *)IQ { OFXMLElement *rosterElement = [IQ elementForName: @"query" namespace: XMPP_NS_ROSTER]; if (connection.supportsRosterVersioning) { if (rosterElement == nil) { for (OFDictionary *item in [_dataStorage dictionaryForPath: @"roster.items"]) { XMPPRosterItem *rosterItem; XMPPJID *JID; rosterItem = [XMPPRosterItem rosterItem]; JID = [XMPPJID JIDWithString: [item objectForKey: @"JID"]]; rosterItem.JID = JID; rosterItem.name = [item objectForKey: @"name"]; rosterItem.subscription = [item objectForKey: @"subscription"]; rosterItem.groups = [item objectForKey: @"groups"]; [_rosterItems setObject: rosterItem forKey: JID.bareJID]; } } else [_dataStorage setDictionary: nil forPath: @"roster.items"]; } for (OFXMLElement *element in rosterElement.children) { void *pool = objc_autoreleasePoolPush(); XMPPRosterItem *rosterItem; if (![element.name isEqual: @"item"] || ![element.namespace isEqual: XMPP_NS_ROSTER]) continue; rosterItem = [self xmpp_rosterItemWithXMLElement: element]; [self xmpp_updateRosterItem: rosterItem]; objc_autoreleasePoolPop(pool); } if (connection.supportsRosterVersioning && rosterElement != nil) { OFString *ver = [rosterElement attributeForName: @"ver"].stringValue; [_dataStorage setStringValue: ver forPath: @"roster.ver"]; [_dataStorage save]; } [_delegates broadcastSelector: @selector(rosterWasReceived:) withObject: self]; } @end |
Modified src/XMPPSCRAMAuth.m from [bdb2995bf2] to [0d02b6cf21].
1 2 | /* * Copyright (c) 2011, Florian Zeitz <florob@babelmonkeys.de> | | | 1 2 3 4 5 6 7 8 9 10 | /* * Copyright (c) 2011, Florian Zeitz <florob@babelmonkeys.de> * Copyright (c) 2011, 2019, Jonathan Schleifer <js@webkeks.org> * * https://heap.zone/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. * |
︙ | ︙ | |||
137 138 139 140 141 142 143 | if (authzid) { OFMutableString *new = [[authzid mutableCopy] autorelease]; [new replaceOccurrencesOfString: @"=" withString: @"=3D"]; [new replaceOccurrencesOfString: @"," withString: @"=2C"]; | > | > | | 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | if (authzid) { OFMutableString *new = [[authzid mutableCopy] autorelease]; [new replaceOccurrencesOfString: @"=" withString: @"=3D"]; [new replaceOccurrencesOfString: @"," withString: @"=2C"]; [new makeImmutable]; _authzid = [new copy]; } else _authzid = nil; [old release]; } - (void)setAuthcid: (OFString *)authcid { OFString *old = _authcid; if (authcid) { OFMutableString *new = [[authcid mutableCopy] autorelease]; [new replaceOccurrencesOfString: @"=" withString: @"=3D"]; [new replaceOccurrencesOfString: @"," withString: @"=2C"]; [new makeImmutable]; _authcid = [new copy]; } else _authcid = nil; [old release]; } - (OFData *)initialMessage |
︙ | ︙ | |||
189 190 191 192 193 194 195 | _cNonce = [[self xmpp_genNonce] retain]; [_clientFirstMessageBare release]; _clientFirstMessageBare = nil; _clientFirstMessageBare = [[OFString alloc] initWithFormat: @"n=%@,r=%@", _authcid, _cNonce]; | | | | | | 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | _cNonce = [[self xmpp_genNonce] retain]; [_clientFirstMessageBare release]; _clientFirstMessageBare = nil; _clientFirstMessageBare = [[OFString alloc] initWithFormat: @"n=%@,r=%@", _authcid, _cNonce]; [ret addItems: _GS2Header.UTF8String count: _GS2Header.UTF8StringLength]; [ret addItems: _clientFirstMessageBare.UTF8String count: _clientFirstMessageBare.UTF8StringLength]; [ret makeImmutable]; return ret; } - (OFData *)continueWithData: (OFData *)data |
︙ | ︙ | |||
235 236 237 238 239 240 241 | GOT_ITERCOUNT = 0x04 } got = 0; hash = [[[_hashType alloc] init] autorelease]; ret = [OFMutableData data]; authMessage = [OFMutableData data]; | | | | | | | | | | | | | | | | | | | | | | | | | 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 | GOT_ITERCOUNT = 0x04 } got = 0; hash = [[[_hashType alloc] init] autorelease]; ret = [OFMutableData data]; authMessage = [OFMutableData data]; OFString *challenge = [OFString stringWithUTF8String: data.items length: data.count * data.itemSize]; for (OFString *component in [challenge componentsSeparatedByString: @","]) { OFString *entry = [component substringWithRange: of_range(2, component.length - 2)]; if ([component hasPrefix: @"r="]) { if (![entry hasPrefix: _cNonce]) @throw [XMPPAuthFailedException exceptionWithConnection: nil reason: @"Received wrong " @"nonce"]; sNonce = entry; got |= GOT_SNONCE; } else if ([component hasPrefix: @"s="]) { salt = [OFData dataWithBase64EncodedString: entry]; got |= GOT_SALT; } else if ([component hasPrefix: @"i="]) { iterCount = entry.decimalValue; got |= GOT_ITERCOUNT; } } if (got != (GOT_SNONCE | GOT_SALT | GOT_ITERCOUNT)) @throw [OFInvalidServerReplyException exception]; // Add c=<base64(GS2Header+channelBindingData)> tmpArray = [OFMutableData dataWithItems: _GS2Header.UTF8String count: _GS2Header.UTF8StringLength]; if (_plusAvailable && _connection.encrypted) { OFData *channelBinding = [((SSLSocket *)[_connection socket]) channelBindingDataWithType: @"tls-unique"]; [tmpArray addItems: channelBinding.items count: channelBinding.count]; } tmpString = tmpArray.stringByBase64Encoding; [ret addItems: "c=" count: 2]; [ret addItems: tmpString.UTF8String count: tmpString.UTF8StringLength]; // Add r=<nonce> [ret addItem: ","]; [ret addItems: "r=" count: 2]; [ret addItems: sNonce.UTF8String count: sNonce.UTF8StringLength]; /* * IETF RFC 5802: * SaltedPassword := Hi(Normalize(password), salt, i) */ tmpArray = [OFMutableData dataWithItems: _password.UTF8String count: _password.UTF8StringLength]; saltedPassword = [self xmpp_hiWithData: tmpArray salt: salt iterationCount: iterCount]; /* * IETF RFC 5802: * AuthMessage := client-first-message-bare + "," + * server-first-message + "," + * client-final-message-without-proof */ [authMessage addItems: _clientFirstMessageBare.UTF8String count: _clientFirstMessageBare.UTF8StringLength]; [authMessage addItem: ","]; [authMessage addItems: data.items count: data.count * data.itemSize]; [authMessage addItem: ","]; [authMessage addItems: ret.items count: ret.count]; /* * IETF RFC 5802: * ClientKey := HMAC(SaltedPassword, "Client Key") */ clientKey = [self xmpp_HMACWithKey: saltedPassword data: [OFData dataWithItems: "Client Key" |
︙ | ︙ | |||
332 333 334 335 336 337 338 | length: [_hashType digestSize]]; /* * IETF RFC 5802: * ClientSignature := HMAC(StoredKey, AuthMessage) */ clientSignature = [self | | | | 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 | length: [_hashType digestSize]]; /* * IETF RFC 5802: * ClientSignature := HMAC(StoredKey, AuthMessage) */ clientSignature = [self xmpp_HMACWithKey: [OFData dataWithItems: hash.digest count: hash.digestSize] data: authMessage]; /* * IETF RFC 5802: * ServerKey := HMAC(SaltedPassword, "Server Key") */ serverKey = [self xmpp_HMACWithKey: saltedPassword |
︙ | ︙ | |||
371 372 373 374 375 376 377 | [tmpArray addItem: &c]; } // Add p=<base64(ClientProof)> [ret addItem: ","]; [ret addItems: "p=" count: 2]; | | | | | | | | | 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 | [tmpArray addItem: &c]; } // Add p=<base64(ClientProof)> [ret addItem: ","]; [ret addItems: "p=" count: 2]; tmpString = tmpArray.stringByBase64Encoding; [ret addItems: tmpString.UTF8String count: tmpString.UTF8StringLength]; return ret; } - (OFData *)xmpp_parseServerFinalMessage: (OFData *)data { OFString *mess, *value; /* * server-final-message already received, * we were just waiting for the last word from the server */ if (_authenticated) return nil; mess = [OFString stringWithUTF8String: data.items length: data.count * data.itemSize]; value = [mess substringWithRange: of_range(2, mess.length - 2)]; if ([mess hasPrefix: @"v="]) { if (![value isEqual: _serverSignature.stringByBase64Encoding]) @throw [XMPPAuthFailedException exceptionWithConnection: nil reason: @"Received wrong " @"ServerSignature"]; _authenticated = true; } else @throw [XMPPAuthFailedException exceptionWithConnection: nil |
︙ | ︙ | |||
437 438 439 440 441 442 443 | { void *pool = objc_autoreleasePoolPush(); OFMutableData *k = [OFMutableData data]; size_t i, kSize, blockSize = [_hashType blockSize]; uint8_t *kI = NULL, *kO = NULL; id <OFCryptoHash> hashI, hashO; | | | | | | | | | | | | | | | | | 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 | { void *pool = objc_autoreleasePoolPush(); OFMutableData *k = [OFMutableData data]; size_t i, kSize, blockSize = [_hashType blockSize]; uint8_t *kI = NULL, *kO = NULL; id <OFCryptoHash> hashI, hashO; if (key.itemSize * key.count > blockSize) { hashI = [[[_hashType alloc] init] autorelease]; [hashI updateWithBuffer: key.items length: key.itemSize * key.count]; [k addItems: hashI.digest count: hashI.digestSize]; } else [k addItems: key.items count: key.itemSize * key.count]; @try { kI = [self allocMemoryWithSize: blockSize]; kO = [self allocMemoryWithSize: blockSize]; kSize = k.count; memcpy(kI, k.items, kSize); memset(kI + kSize, 0, blockSize - kSize); memcpy(kO, kI, blockSize); for (i = 0; i < blockSize; i++) { kI[i] ^= HMAC_IPAD; kO[i] ^= HMAC_OPAD; } hashI = [[[_hashType alloc] init] autorelease]; [hashI updateWithBuffer: kI length: blockSize]; [hashI updateWithBuffer: data.items length: data.itemSize * data.count]; hashO = [[[_hashType alloc] init] autorelease]; [hashO updateWithBuffer: kO length: blockSize]; [hashO updateWithBuffer: hashI.digest length: hashI.digestSize]; } @finally { [self freeMemory: kI]; [self freeMemory: kO]; } [hashO retain]; |
︙ | ︙ |
Modified src/XMPPStanza.m from [b2b145f65c] to [127836dc56].
1 | /* | | | 1 2 3 4 5 6 7 8 9 | /* * Copyright (c) 2011, 2012, 2013, 2019, Jonathan Schleifer <js@heap.zone> * Copyright (c) 2011, Florian Zeitz <florob@babelmonkeys.de> * * https://heap.zone/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. |
︙ | ︙ | |||
125 126 127 128 129 130 131 | stringValue: nil]; @try { if (![name isEqual: @"iq"] && ![name isEqual: @"message"] && ![name isEqual: @"presence"]) @throw [OFInvalidArgumentException exception]; | | | | | | | | | | | 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | stringValue: nil]; @try { if (![name isEqual: @"iq"] && ![name isEqual: @"message"] && ![name isEqual: @"presence"]) @throw [OFInvalidArgumentException exception]; self.defaultNamespace = XMPP_NS_CLIENT; [self setPrefix: @"stream" forNamespace: XMPP_NS_STREAM]; if (type != nil) self.type = type; if (ID != nil) self.ID = ID; } @catch (id e) { [self release]; @throw e; } return self; } - (instancetype)initWithElement: (OFXMLElement *)element { self = [super initWithElement: element]; @try { OFXMLAttribute *attribute; if ((attribute = [element attributeForName: @"from"])) self.from = [XMPPJID JIDWithString: attribute.stringValue]; if ((attribute = [element attributeForName: @"to"])) self.to = [XMPPJID JIDWithString: attribute.stringValue]; if ((attribute = [element attributeForName: @"type"])) self.type = attribute.stringValue; if ((attribute = [element attributeForName: @"id"])) self.ID = attribute.stringValue; } @catch (id e) { [self release]; @throw e; } return self; } |
︙ | ︙ | |||
190 191 192 193 194 195 196 | _from = [from copy]; [old release]; [self removeAttributeForName: @"from"]; if (from != nil) [self addAttributeWithName: @"from" | | | | 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | _from = [from copy]; [old release]; [self removeAttributeForName: @"from"]; if (from != nil) [self addAttributeWithName: @"from" stringValue: from.fullJID]; } - (void)setTo: (XMPPJID *)to { XMPPJID *old = _to; _to = [to copy]; [old release]; [self removeAttributeForName: @"to"]; if (to != nil) [self addAttributeWithName: @"to" stringValue: to.fullJID]; } - (void)setType: (OFString *)type { OFString *old = _type; _type = [type copy]; [old release]; |
︙ | ︙ |
Modified src/XMPPStreamManagement.m from [42b7f06f4f] to [72de3d76fb].
1 2 3 4 5 6 7 8 9 | /* * Copyright (c) 2012, Florian Zeitz <florob@babelmonkeys.de> * * https://heap.zone/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. * | > | 1 2 3 4 5 6 7 8 9 10 | /* * Copyright (c) 2012, Florian Zeitz <florob@babelmonkeys.de> * Copyright (c) 2019, Jonathan Schleifer <js@heap.zone> * * https://heap.zone/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. * |
︙ | ︙ | |||
52 53 54 55 56 57 58 | [super dealloc]; } - (void)connection: (XMPPConnection *)connection didReceiveElement: (OFXMLElement *)element { | | | | 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | [super dealloc]; } - (void)connection: (XMPPConnection *)connection didReceiveElement: (OFXMLElement *)element { OFString *elementName = element.name; OFString *elementNS = element.namespace; if ([elementNS isEqual: XMPP_NS_SM]) { if ([elementName isEqual: @"enabled"]) { _receivedCount = 0; return; } |
︙ | ︙ | |||
95 96 97 98 99 100 101 | { } */ - (void)connection: (XMPPConnection *)connection wasBoundToJID: (XMPPJID *)JID { | | | 96 97 98 99 100 101 102 103 104 105 106 107 108 | { } */ - (void)connection: (XMPPConnection *)connection wasBoundToJID: (XMPPJID *)JID { if (connection.supportsStreamManagement) [connection sendStanza: [OFXMLElement elementWithName: @"enable" namespace: XMPP_NS_SM]]; } @end |
Modified tests/test.m from [d958b131ca] to [6b93900f41].
1 | /* | | | 1 2 3 4 5 6 7 8 9 | /* * Copyright (c) 2010, 2011, 2019, Jonathan Schleifer <js@webkeks.org> * Copyright (c) 2011, 2012, Florian Zeitz <florob@babelmonkeys.de> * * https://heap.zone/objxmpp/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. |
︙ | ︙ | |||
49 50 51 52 53 54 55 | @implementation AppDelegate - (void)applicationDidFinishLaunching { OFArray *arguments = [OFApplication arguments]; XMPPPresence *pres = [XMPPPresence presence]; | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | @implementation AppDelegate - (void)applicationDidFinishLaunching { OFArray *arguments = [OFApplication arguments]; XMPPPresence *pres = [XMPPPresence presence]; pres.show = @"xa"; pres.status = @"Bored"; pres.priority = [OFNumber numberWithInt8: 20]; pres.to = [XMPPJID JIDWithString: @"alice@example.com"]; pres.from = [XMPPJID JIDWithString: @"bob@example.org"]; assert([pres.XMLString isEqual: @"<presence to='alice@example.com' " @"from='bob@example.org'><show>xa</show>" @"<status>Bored</status><priority>20</priority>" @"</presence>"]); XMPPPresence *pres2 = [XMPPPresence presence]; pres2.show = @"away"; pres2.status = @"Bored"; pres2.priority = [OFNumber numberWithInt8: 23]; pres2.to = [XMPPJID JIDWithString: @"alice@example.com"]; pres2.from = [XMPPJID JIDWithString: @"bob@example.org"]; assert([pres compare: pres2] == OF_ORDERED_ASCENDING); XMPPMessage *msg = [XMPPMessage messageWithType: @"chat"]; msg.body = @"Hello everyone"; msg.to = [XMPPJID JIDWithString: @"jdev@conference.jabber.org"]; msg.from = [XMPPJID JIDWithString: @"alice@example.com"]; assert([msg.XMLString isEqual: @"<message type='chat' " @"to='jdev@conference.jabber.org' " @"from='alice@example.com'><body>Hello everyone</body>" @"</message>"]); XMPPIQ *IQ = [XMPPIQ IQWithType: @"set" ID: @"128"]; IQ.to = [XMPPJID JIDWithString: @"juliet@capulet.lit"]; IQ.from = [XMPPJID JIDWithString: @"romeo@montague.lit"]; assert([IQ.XMLString isEqual: @"<iq type='set' id='128' " @"to='juliet@capulet.lit' " @"from='romeo@montague.lit'/>"]); OFXMLElement *elem = [OFXMLElement elementWithName: @"iq"]; [elem addAttributeWithName: @"from" stringValue: @"bob@localhost"]; [elem addAttributeWithName: @"to" stringValue: @"alice@localhost"]; [elem addAttributeWithName: @"type" stringValue: @"get"]; [elem addAttributeWithName: @"id" stringValue: @"42"]; XMPPStanza *stanza = [XMPPStanza stanzaWithElement: elem]; assert([elem.XMLString isEqual: [stanza XMLString]]); assert(([[OFString stringWithFormat: @"%@, %@, %@, %@", stanza.from.fullJID, stanza.to.fullJID, stanza.type, stanza.ID] isEqual: @"bob@localhost, alice@localhost, get, 42"])); conn = [[XMPPConnection alloc] init]; [conn addDelegate: self]; XMPPFileStorage *storage = [[XMPPFileStorage alloc] initWithFile: @"storage.binarypack"]; conn.dataStorage = storage; roster = [[XMPPRoster alloc] initWithConnection: conn]; [roster addDelegate: self]; [[XMPPStreamManagement alloc] initWithConnection: conn]; if (arguments.count != 3) { of_log(@"Invalid count of command line arguments!"); [OFApplication terminateWithStatus: 1]; } conn.domain = [arguments objectAtIndex: 0]; conn.username = [arguments objectAtIndex: 1]; conn.password = [arguments objectAtIndex: 2]; conn.resource = @"ObjXMPP"; [conn asyncConnect]; } - (void)connection: (XMPPConnection *)conn didReceiveElement: (OFXMLElement *)element { |
︙ | ︙ | |||
143 144 145 146 147 148 149 | - (void)connectionWasAuthenticated: (XMPPConnection *)conn { of_log(@"Auth successful"); } - (void)connection: (XMPPConnection *)conn_ | | | | | | | | | | | | | | | | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 | - (void)connectionWasAuthenticated: (XMPPConnection *)conn { of_log(@"Auth successful"); } - (void)connection: (XMPPConnection *)conn_ wasBoundToJID: (XMPPJID *)JID { of_log(@"Bound to JID: %@", JID.fullJID); of_log(@"Supports SM: %@", conn_.supportsStreamManagement ? @"true" : @"false"); XMPPDiscoEntity *discoEntity = [[XMPPDiscoEntity alloc] initWithConnection: conn]; [discoEntity addIdentity: [XMPPDiscoIdentity identityWithCategory: @"client" type: @"pc" name: @"ObjXMPP"]]; XMPPDiscoNode *nodeMusic = [XMPPDiscoNode discoNodeWithJID: JID node: @"music" name: @"My music"]; [discoEntity addChildNode: nodeMusic]; XMPPDiscoNode *nodeRHCP = [XMPPDiscoNode discoNodeWithJID: JID node: @"fa3b6" name: @"Red Hot Chili Peppers"]; [nodeMusic addChildNode: nodeRHCP]; XMPPDiscoNode *nodeStop = [XMPPDiscoNode discoNodeWithJID: JID node: @"qwe87" name: @"Can't Stop"]; [nodeRHCP addChildNode: nodeStop]; XMPPDiscoNode *nodeClueso = [XMPPDiscoNode discoNodeWithJID: JID node: @"ea386" name: @"Clueso"]; [nodeMusic addChildNode: nodeClueso]; XMPPDiscoNode *nodeChicago = [XMPPDiscoNode discoNodeWithJID: JID node: @"qwr87" name: @"Chicago"]; [nodeClueso addChildNode: nodeChicago]; [discoEntity addDiscoNode: nodeMusic]; [discoEntity addDiscoNode: nodeRHCP]; [discoEntity addDiscoNode: nodeClueso]; [discoEntity addDiscoNode: nodeStop]; [discoEntity addDiscoNode: nodeChicago]; [roster requestRoster]; } - (void)rosterWasReceived: (XMPPRoster *)roster_ { XMPPPresence *pres; of_log(@"Got roster: %@", roster_.rosterItems); pres = [XMPPPresence presence]; pres.priority = [OFNumber numberWithInt8: 10]; pres.status = @"ObjXMPP test is working!"; [conn sendStanza: pres]; #ifdef OF_HAVE_BLOCKS XMPPIQ *IQ = [XMPPIQ IQWithType: @"get" ID: [conn generateStanzaID]]; [IQ addChild: [OFXMLElement elementWithName: @"ping" namespace: @"urn:xmpp:ping"]]; [conn sendIQ: IQ callbackBlock: ^ (XMPPConnection *c, XMPPIQ *resp) { of_log(@"Ping response: %@", resp); }]; #endif } - (void)connectionDidUpgradeToTLS: (XMPPConnection *)conn_ |
︙ | ︙ |