ObjXMPP  Diff

Differences From Artifact [c7bf092d14]:

To Artifact [c9e6b14c30]:


1
2

3
4
5
6
7
8
9
1

2
3
4
5
6
7
8
9

-
+







/*
 * Copyright (c) 2010, 2011, 2012, 2013, 2015, 2016, 2017, 2018
 * 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
146

147
148
149
150
151
152
153
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,
		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
172

173
174
175
176
177
178
179
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,
		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
211

212
213
214
215
216
217
218
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,
		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
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
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,
		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: (OF_KINDOF(OFTCPSocket *))sock
-     (void)socket: (OFTCPSocket *)sock
  didConnectToHost: (OFString *)host
	      port: (uint16_t)port
	 exception: (id)exception
{
	if (exception != nil) {
		if ([_nextSRVRecords count] > 0) {
		if (_nextSRVRecords.count > 0) {
			[self xmpp_tryNextSRVRecord];
			return;
		}

		[_delegates broadcastSelector: @selector(connection:
						   didThrowException:)
				   withObject: self
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
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) {
	if (_nextSRVRecords.count == 0) {
		[_nextSRVRecords release];
		_nextSRVRecords = nil;
	}

	[_socket asyncConnectToHost: [record target]
			       port: [record port]];
	[_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 (OF_KINDOF(OFDNSResourceRecord *) record in
	for (OFDNSResourceRecord *record in
	    [answerRecords objectForKey: domainName])
		if ([record isKindOfClass: [OFSRVDNSResourceRecord class]])
		       [records addObject: record];

	/* TODO: Sort records */
	[records makeImmutable];

	if ([records count] == 0) {
	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];
	[(OFTCPSocket *)_socket setDelegate: self];
	[_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 isAtEndOfStream]) {
	if (_socket.atEndOfStream) {
		[_delegates broadcastSelector: @selector(connectionWasClosed:
						   error:)
				   withObject: self
				   withObject: nil];
		return false;
	}

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
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];
		[socket verifyPeerCertificate];
	} @catch (SSLInvalidCertificateException *e) {
		if (reason != NULL)
			*reason = [[[e reason] copy] autorelease];
			*reason = e.reason;

		return false;
	}

	cert = [_socket peerCertificate];
	SANs = [cert subjectAlternativeName];
	cert = socket.peerCertificate;
	SANs = cert.subjectAlternativeName;

	if ([[SANs objectForKey: @"otherName"]
		objectForKey: OID_SRVName] != nil ||
	     [SANs objectForKey: @"dNSName"] != nil ||
	     [SANs objectForKey: @"uniformResourceIdentifier"] != nil)
	    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]];
	[_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) {
	if ((ID = IQ.ID) == nil) {
		ID = [self generateStanzaID];
		[IQ setID: ID];
		IQ.ID = ID;
	}

	if ((key = [[IQ to] fullJID]) == nil)
		key = [_JID bareJID];
	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
528

529
530

531
532
533
534


535
536
537
538
539
540
541
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) {
	if ((ID = IQ.ID) == nil) {
		ID = [self generateStanzaID];
		[IQ setID: ID];
		IQ.ID = ID;
	}

	if ((key = [[IQ to] fullJID]) == nil)
		key = [_JID bareJID];
	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
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
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]) {
		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"]) {
		if ([attribute.name isEqual: @"version"] &&
		    ![attribute.stringValue isEqual: @"1.0"]) {
			[self xmpp_sendStreamError: @"unsupported-version"
					      text: nil];
			return;
		}
	}

	[parser setDelegate: _elementBuilder];
	parser.delegate = _elementBuilder;
}

- (void)elementBuilder: (OFXMLElementBuilder *)builder
       didBuildElement: (OFXMLElement *)element
{
	/* Ignore whitespace elements */
	if ([element name] == nil)
	if (element.name == nil)
		return;

	[element setDefaultNamespace: XMPP_NS_CLIENT];
	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])
	if ([element.namespace isEqual: XMPP_NS_CLIENT])
		[self xmpp_handleStanza: element];

	if ([[element namespace] isEqual: XMPP_NS_STREAM])
	if ([element.namespace isEqual: XMPP_NS_STREAM])
		[self xmpp_handleStream: element];

	if ([[element namespace] isEqual: XMPP_NS_STARTTLS])
	if ([element.namespace isEqual: XMPP_NS_STARTTLS])
		[self xmpp_handleTLS: element];

	if ([[element namespace] isEqual: XMPP_NS_SASL])
	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 {
	else
		[self close];
	}
}

- (void)xmpp_startStream
{
	OFString *langString = @"";

	/* Make sure we don't get any old events */
	[_parser setDelegate: nil];
	[_elementBuilder setDelegate: nil];
	_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 setDelegate: self];
	_parser.delegate = self;

	_elementBuilder = [[XMPPXMLElementBuilder alloc] init];
	[_elementBuilder setDelegate: self];
	_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
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
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"]) {
	if ([element.name isEqual: @"iq"]) {
		[self xmpp_handleIQ: [XMPPIQ stanzaWithElement: element]];
		return;
	}

	if ([[element name] isEqual: @"message"]) {
	if ([element.name isEqual: @"message"]) {
		[self xmpp_handleMessage:
		    [XMPPMessage stanzaWithElement: element]];
		return;
	}

	if ([[element name] isEqual: @"presence"]) {
	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"]) {
	if ([element.name isEqual: @"features"]) {
		[self xmpp_handleFeatures: element];
		return;
	}

	if ([[element name] isEqual: @"error"]) {
	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];
		condition = [[element elementsForNamespace:
		    XMPP_NS_XMPP_STREAM].firstObject name];

		if (condition == nil)
			condition = @"undefined";

		reason = [[element
		reason = [element
		    elementForName: @"text"
			 namespace: XMPP_NS_XMPP_STREAM] stringValue];
			 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"]) {
	if ([element.name isEqual: @"proceed"]) {
		/* FIXME: Catch errors here */
		SSLSocket *newSock;

		[_delegates broadcastSelector: @selector(
						   connectionWillUpgradeToTLS:)
				   withObject: self];

		newSock = [[SSLSocket alloc] initWithSocket: _socket];
		[newSock setCertificateVerificationEnabled: false];
		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;
		[(OFTCPSocket *)_socket setDelegate: self];
		[_socket setDelegate: self];

		_encrypted = true;

		[_delegates broadcastSelector: @selector(
						   connectionDidUpgradeToTLS:)
				   withObject: self];

		/* Stream restart */
		[self xmpp_startStream];

		return;
	}

	if ([[element name] isEqual: @"failure"])
	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"]) {
	if ([element.name isEqual: @"challenge"]) {
		OFXMLElement *responseTag;
		OFData *challenge =
		    [OFData dataWithBase64EncodedString: [element stringValue]];
		    [OFData dataWithBase64EncodedString: element.stringValue];
		OFData *response = [_authModule continueWithData: challenge];

		responseTag = [OFXMLElement elementWithName: @"response"
						  namespace: XMPP_NS_SASL];
		if (response) {
			if ([response count] == 0)
				[responseTag setStringValue: @"="];
			if (response.count == 0)
				responseTag.stringValue = @"=";
			else
				[responseTag setStringValue:
				    [response stringByBase64Encoding]];
				responseTag.stringValue =
				    response.stringByBase64Encoding;
		}

		[self sendStanza: responseTag];
		return;
	}

	if ([[element name] isEqual: @"success"]) {
	if ([element.name isEqual: @"success"]) {
		[_authModule continueWithData: [OFData
		    dataWithBase64EncodedString: [element stringValue]]];
		    dataWithBase64EncodedString: element.stringValue]];

		[_delegates broadcastSelector: @selector(
						   connectionWasAuthenticated:)
				   withObject: self];

		/* Stream restart */
		[self xmpp_startStream];

		return;
	}

	if ([[element name] isEqual: @"failure"]) {
	if ([element.name isEqual: @"failure"]) {
		/* FIXME: Do more parsing/handling */
		@throw [XMPPAuthFailedException
		    exceptionWithConnection: self
				     reason: [element XMLString]];
				     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 = IQ.from.fullJID) == nil)
		key = _JID.bareJID;
	if (key == nil) // Only happens for resource bind
		key = @"bind";
	key = [key stringByAppendingString: [IQ ID]];
	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"]) {
	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
916
917


918
919
920
921
922
923
924
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]];
		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
996
997


998
999
1000


1001
1002
1003
1004
1005
1006
1007
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 setStringValue: @"="];
		if (initialMessage.count == 0)
			authTag.stringValue = @"=";
		else
			[authTag setStringValue:
			    [initialMessage stringByBase64Encoding]];
			authTag.stringValue =
			    initialMessage.stringByBase64Encoding;
	}

	[self sendStanza: authTag];
}

- (void)xmpp_sendResourceBind
{
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
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
		[error	   addChild: [OFXMLElement
		    elementWithName: @"text"
			  namespace: XMPP_NS_XMPP_STREAM
			stringValue: text]];
	[_parser setDelegate: nil];
	_parser.delegate = nil;
	[self sendStanza: error];
	[self close];
}

- (void)xmpp_handleResourceBindForConnection: (XMPPConnection *)connection
					  IQ: (XMPPIQ *)IQ
{
	OFXMLElement *bindElement, *JIDElement;

	assert([[IQ type] isEqual: @"result"]);
	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]];
	_JID = [[XMPPJID alloc] initWithString: JIDElement.stringValue];

	if (_needsSession) {
		[self xmpp_sendSession];
		return;
	}

	[_delegates broadcastSelector: @selector(connection:wasBoundToJID:)
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
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"])
	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],
	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 {