Skip to content

Loading…

Added XEP-0138 Stream Compression support to XMPPFramework #247

Closed
wants to merge 19 commits into from

8 participants

@sprhawk
1. Separated XMPPStream internal Extension into the XMPPStreamInternal.h, which could be called from XMPPFeature;
2. Moved asyncSocket write/read data into a wrapper method -[XMPPStream write/readData ...], in which a registeredStreamPreprocessor could do preprocess (eg, XMPPCompression could deflate / inflate data);
3. Added XMPPFeature into calling procedure for XMPPStream;
4. Added XMPPCompression feature, which applies to XEP-0138 Stream Compression
5. Added XMPPCompression support into DesktopXMPP and iPhoneXMPP projects

Tested on OpenFire 3.8.2 and ejabbared 2.1.13

I accidently removed the branch and it caused this pull request closed. please add new comment to #563

sprhawk and others added some commits
@sprhawk sprhawk it works abd1ef2
@RomainLo RomainLo Fix a bug. Adding the copyWithZone within the XMPPPresence. Now the c…
…opied object is a XMPPPresence instance.
359d039
@ObjColumnist ObjColumnist copyWithZone added to XMPPIQ, XMPPMessage and XMPPPresence
Fixes #229
4664b9a
@ObjColumnist ObjColumnist xmppRoster:didReceiveRosterPush: API
Sent when a Roster Push is received as specified in Section 2.1.6 of RFC 6121.
87c91a5
@ObjColumnist ObjColumnist Initialize Variable
Fixes #231
2bb4a15
@ObjColumnist ObjColumnist registerWithElements:error API
Fixes #232
7a68911
Matt Johnson XEP-0045: added methods to XMPPMessage category to handle room subjec…
…ts, have XMPPRoom check incoming messages for room subjects and use them to set the roomSubject property
6ad09c8
@ObjColumnist ObjColumnist XEP-0045 Code Clean Up 8307348
@sergiosvieira sergiosvieira Bug Fix: Message value on XMPPRoomMessageCoreDataStorageObject is not…
… saving on CoreDate
187c7e8
@sergiosvieira sergiosvieira Bug Fix: Message value on XMPPRoomMessageHybridCoreDataStorageObject …
…is not saving on CoreDate
a89360f
@max-potapov max-potapov missing newline at end of files added 40b3ed1
@sprhawk sprhawk 1. Separated XMPPStream internal Extension into the XMPPStreamInterna…
…l.h, which could be called from XMPPFeature;

2. Moved asyncSocket write/read data into a wrapper method -[XMPPStream write/readData ...], in which a registeredStreamPreprocessor could do preprocess (eg, XMPPCompression could deflate / inflate data);
3. Added XMPPFeature into calling procedure for XMPPStream;
4. Added XMPPCompression feature, which applies to XEP-0138 Stream Compression
5. Added XMPPCompression support into DesktopXMPP and iPhoneXMPP projects
bbbb6e3
@sprhawk sprhawk disabled verbose log ( for showing compression rate ) e792a35
@sprhawk sprhawk optimized deflation ( one pass, one allocaing buffer )
optimized inflation ( try one pass, one allocating buffer )
d7f2834
@sprhawk sprhawk merge from master 6d9cecf
@ObjColumnist
Collaborator

Wow :+1:

It is quite a big commit so il look over it before I merge it in.

@sprhawk

Yes, I don't want to insert an XEP-0138 into the core implementation of XMPPStream, but the feature that needs re-negotiation requires some functionality of XMPPStream, so I have to separate internal header of XMPPStream into an XMPPStreamInternal.h. other changes are not so big as such -- they are separated at least.

And I'll work for XEP-0198 Stream Management next week, wish it is not big as this one :smile:

@sprhawk

BTW:
My hack into XMPPStream for XEP-0138 is just a workaround for now.

I added a feature array enumeration which is inserted before resource binding, because XEP-0138 recommends the negotiation is behind SASL but before resource binding, but if other feature as a "module" is to be added into the infrastructure of features like this, it may not work properly (due to the order is before resource binding)

@sprhawk sprhawk closed this
@sprhawk

Sorry I deleted the branch by accident

@sprhawk sprhawk reopened this
@ObjColumnist
Collaborator

Hi,

I tested this and it works fine until it gets to my roster (which has around 100 hundred items), at this point the compression fails.

I am guessing this is because the compressed data is split across multiple data chunks.

@sprhawk

@ObjColumnist I have no idea for now. I just follow the process of the XEP, it seems not provide any mechanism to handle it.
I'm working for other projects, currently I have no time to verify it.

@ObjColumnist
Collaborator

No problem :smile:

@wujie0919

hello

When I use the XEP - 0138, Authenticate error “xmppStream:didNotAuthenticate:”, why?

@sprhawk

@wujie0919 ah oh, your description is too simple, i cannot explain. try to use with OpenFire and have a try again

@wujie0919

2013-10-17 21:51:28:123 XMPPStream[1281:1803] SEND: <?xml version='1.0'?>
2013-10-17 21:51:28:123 XMPPStream[1281:1803] SEND:
2013-10-17 21:51:28:303 XMPPStream[1281:2713] RECV:
2013-10-17 21:51:28:384 XMPPStream[1281:2713] RECV: DIGEST-MD5PLAINCRAM-MD5zlib/stream:features
2013-10-17 21:51:28:385 XMPPStream[1281:2713] Compression Method: zlib
2013-10-17 21:51:28:385 XMPPStream[1281:2713] SEND: zlib
2013-10-17 21:51:28:469 XMPPStream[1281:2713] RECV:
2013-10-17 21:51:28:470 XMPPStream[1281:2713] SEND:
2013-10-17 21:51:28:470 XMPPStream[1281:2713] SEND: Compression Rate:79.3651%(100/126)
2013-10-17 21:51:28:552 XMPPStream[1281:1803] RECV: Compression Rate:58.8101%(257/437)
2013-10-17 21:51:28:553 XMPPStream[1281:1803] RECV:
2013-10-17 21:51:28:553 XMPPStream[1281:1803] RECV: DIGEST-MD5PLAINCRAM-MD5/stream:features
2013-10-17 21:51:28:554 XMPPStream[1281:303] RosterController: xmppStreamDidConnect:
2013-10-17 21:51:28:554 XMPPStream[1281:303] SEND:
2013-10-17 21:51:28:554 XMPPStream[1281:303] SEND: Compression Rate:90.1408%(64/71)
2013-10-17 21:51:28:640 XMPPStream[1281:1803] RECV: Compression Rate:75.4386%(172/228)
2013-10-17 21:51:28:641 XMPPStream[1281:2713] RECV: cmVhbG09InhtcHAucWluZ2Rhby5xeHVuLm9yZyIsbm9uY2U9IjJIYnVydFhZaGtzTlRBYkswQWlkZXRQemhKU1ZZWXU1ak5kb3BydGIiLHFvcD0iYXV0aCIsY2hhcnNldD11dGYtOCxhbGdvcml0aG09bWQ1LXNlc3M=
2013-10-17 21:51:28:642 XMPPStream[1281:2713] SEND: dXNlcm5hbWU9InRlc3QwNSIscmVhbG09InhtcHAucWluZ2Rhby5xeHVuLm9yZyIsbm9uY2U9IjJIYnVydFhZaGtzTlRBYkswQWlkZXRQemhKU1ZZWXU1ak5kb3BydGIiLGNub25jZT0iNzQxNkI2NzctQ0MzNC00QjA1LTg0RTctQUI2NTMzMzNFOTRFIixuYz0wMDAwMDAwMSxxb3A9YXV0aCxkaWdlc3QtdXJpPSJ4bXBwL3htcHAucWluZ2Rhby5xeHVuLm9yZyIscmVzcG9uc2U9MDRkZjZmNjJkNTkyYWYwNzRkYWMyNmQwZDM3MjBjMTgsY2hhcnNldD11dGYtOA==
2013-10-17 21:51:28:642 XMPPStream[1281:2713] SEND: Compression Rate:62.4390%(256/410)
2013-10-17 21:51:59:332 XMPPStream[1281:180b] RECV: Compression Rate:45.7364%(59/129)
2013-10-17 21:51:59:333 XMPPStream[1281:180b] RECV:
2013-10-17 21:51:59:333 XMPPStream[1281:303] RosterController: xmppStream:didNotAuthenticate:

you can see the error message。i look forword to your anser。

@sprhawk

I'm not an expert on XMPP. you can turn off md5 and try again. I have no idea whether it is related to MD5

@wujie0919

OK,Thank you all the same。

@sprhawk

@ObjColumnist I come back.
I reviewed the XEP-0138,I found

    Example 7. Entity Closes Stream Because of a Processing Error
    <stream:error>
      <undefined-condition xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>
      <failure xmlns='http://jabber.org/protocol/compress'>
        <processing-failed/>
      </failure>
    </stream:error>
    </stream:stream>

I do miss this implementation, and I will do it today.

@sprhawk sprhawk closed this
@sprhawk sprhawk deleted the sprhawk:compression branch
@sprhawk sprhawk reopened this
@sprhawk

@ObjColumnist I added stream:error handling and compression failure handling into recently commits; and fixed a bug of features handling order as @wujie0919 mentioned.

But I did not verify the error handling, it still needs some tests.

@goodleixiao

data: bc98cb4a c3401486 5f457d81 cc64a6b9 80b7953b ddb8902e d32e4a83 1a69ad74 a9858a8b 2254f106 8282500a 15445c08 2db52f93 09cd5b38 934ce365 2626adc6 1042b239 9939ffb9 7c674437 fc095155 9c608ce2 5bb643b8 875137a6 36015efd e64d4630 4b0b9417 6b856ab1 52dedb0f e6d48243 07392990 71330290 1978825f 56613be2 abe89591 97ca014c c66729f1 4b8a56ec d7e9d04a 4a6f5c97 789fa1a9 7c566249 b1cc27b6 4525fc8c f724123c 495e7b64 d01f9ff6 fcc683d7 e892f38e 3bbcf719 701c8f47 6dc1cba9 cbb87433 1019baaa 41dd44aa 69ae9477 e854b0ee 942aca81 aa14e940 a3ccab18 1b75f6b0 80059214 ddaad965 6b37f881 54dc8dba 6dd76d99 c4400339 b9cc202b 7101a2ad 15e26cc4 e5c64571 437e9c5d 45e9aac3 660c11cd 072d07c3 66fc5529 9026f748 f3c46b5e 90518b74 cebca3ae 4c27d2ba 72fb8f09 09a91b59 6906814e 69346542 b2ee1169 b696ac19 371ea399 01130b1c f87d8113 f68e0002 1f1765ee d90ab860 46d8a53b 389c362a 13d6c6a3 520326c0 9329e167 87e59dbc 039902b2 e0bc1ed1 5be63d7f d0f6df6e bce7bb39 77f8e2dd 76bdcba7 9800451a 4556ba1e 35bba202 31e3f5a9 02547aec 11981144 daacd2eb 1fe3f0f3 21c43b00 0000ffff ac58cb4e c24014fd 23f11557 8d89d185 1b13576e 0d4124dd a05148d4 15c8438a 92828828 3e22bec0 a4b48922 69a8c8c7 3877dafe 85773aa1 161f8d31 76d3b473 ef9d999c d3e939f7 c726c4c2 f6a2d363 9c093972 cf956d7e 8dc77fd1 3b7c06a7 ff312df0 3570e5c9 95a8bb2a 4726e3e5 d70b1502 def08f5c 6e31f516 9cf5d04a 822a7902 b95a761f e7c29ba1 a1507aa2 41a66916 53f67516 cf56b396 b62e0ba4 7f619f3e 307f5aee 5875e649 69be623d a7cd9664 ef955880 5e837c9d e85da227 48af8c59 b4d28662 0e23cd76 03b42cb3 a1b735c8 a470c834 5e708827 d2f30226 da6759b4 7d6f895d b480567f 8fe64a20 6ba05ed2 6a8774b3 56e386af 8d87819c 874c8756 ab6819a1 55848c02 b2ceca2a 0ace4ea5 7dc83729 aeb093a3 490d6b7a 36efecf6 63f3bfb5 953b0868 24125fe3 e3cb22ba 95711421 1368fbd1 bf4cb22f f36b4d61 211c8d33 c7e6dc85 79c66644 89d71002 83676129 bc21ae8a 21c73d38 be047fc0 5fde0d45 cdf226ef 34bcaaa4 5fb7d45b 4b4b0ee7 0c228400 67d5f7dc 9afa33b7 5cbf3e30 ebbef442 3889a1d0 b44c9547 2a95edc4 15279ccb 304e0524 16a351e1 0951673c 503586ba cc7af0f4 5c228641 7a15f3b8 64575538 3c20bdbe 79d4649a 53555900 6a4e5923 c69d5d93 311de482 7d9f338d 533ef5bf 9020862a 714b0cc6 900ab178 30ba1217 47d6a391 9f911ffd 8cbca780 1ffc63bf 85df1ff2 80f76cf1 1e89ef00 0000ffff
2014-02-17 09:52:15:256 ChatTest[1753:4503] Inflation failed: -3(invalid code lengths set)

@wujie0919

I have encountered a problem that when I have sent serveral messages continuously,one another unable to received any information.

But when reply you by the other end or iq ping each other,the other end could receive a message that previously cannot received.

Hope you reply

@sprhawk

@wujie0919 I don't think I find any method in the extension specification to recover the corrupted compressed data stream. Who can give a hint about it.

@am33t

I have identified 2 problems here.
First, Inflation failed: -3(invalid code lengths set) OR -3(invalid distance too far back)
you get this error because the packet size to be decompressed is over 1024. You can fix this by increasing size of buffer

Bytef output[4096]; //Method processInputData:
but, this will still happen if packet size is over 4096

Second, because of first error, method "processInputData:" doesn't give proper uncompressed data. This is why xml packet parser fails to parse and disconnects XMPP.

After this XMPPCompression does send stream error and ends streams but continues sending compressed packets. Session cant be created because server is expecting uncompressed data but client is sending compressed data. And server keeps on sending XML error. (xml-not-well-formed)

thanks

@sprhawk sprhawk closed this
@sprhawk

@am33t Thank you for your comments. I was not focused on this stuff for a long time.

I may have found the problem : that is

offset += blockSz; 

in processInputData method, changed with:

_inflation_strm.total_in - old_total_in;
@sprhawk

I accidently removed the branch and it caused this pull request closed. please add new comment to #563

@sprhawk

@wujie0919 you can try the new commits if you have spare time

@sprhawk

@goodleixiao please try the new commits from my own repo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jul 17, 2013
  1. @sprhawk

    it works

    sprhawk committed
Commits on Sep 7, 2013
  1. @RomainLo @sprhawk

    Fix a bug. Adding the copyWithZone within the XMPPPresence. Now the c…

    RomainLo committed with sprhawk
    …opied object is a XMPPPresence instance.
  2. @ObjColumnist @sprhawk
  3. @ObjColumnist @sprhawk

    xmppRoster:didReceiveRosterPush: API

    ObjColumnist committed with sprhawk
    Sent when a Roster Push is received as specified in Section 2.1.6 of RFC 6121.
  4. @ObjColumnist @sprhawk

    Initialize Variable

    ObjColumnist committed with sprhawk
    Fixes #231
  5. @ObjColumnist @sprhawk

    registerWithElements:error API

    ObjColumnist committed with sprhawk
    Fixes #232
  6. @sprhawk

    XEP-0045: added methods to XMPPMessage category to handle room subjec…

    Matt Johnson committed with sprhawk
    …ts, have XMPPRoom check incoming messages for room subjects and use them to set the roomSubject property
  7. @ObjColumnist @sprhawk

    XEP-0045 Code Clean Up

    ObjColumnist committed with sprhawk
  8. @sergiosvieira @sprhawk
  9. @sergiosvieira @sprhawk
  10. @max-potapov @sprhawk
  11. @sprhawk

    1. Separated XMPPStream internal Extension into the XMPPStreamInterna…

    sprhawk committed
    …l.h, which could be called from XMPPFeature;
    
    2. Moved asyncSocket write/read data into a wrapper method -[XMPPStream write/readData ...], in which a registeredStreamPreprocessor could do preprocess (eg, XMPPCompression could deflate / inflate data);
    3. Added XMPPFeature into calling procedure for XMPPStream;
    4. Added XMPPCompression feature, which applies to XEP-0138 Stream Compression
    5. Added XMPPCompression support into DesktopXMPP and iPhoneXMPP projects
  12. @sprhawk
Commits on Sep 8, 2013
  1. @sprhawk

    optimized deflation ( one pass, one allocaing buffer )

    sprhawk committed
    optimized inflation ( try one pass, one allocating buffer )
  2. @sprhawk

    merge from master

    sprhawk committed
Commits on Sep 9, 2013
  1. @sprhawk
Commits on Nov 7, 2013
  1. @sprhawk

    added XEP-0138 stream(compression) error handling, but not tested due…

    sprhawk committed
    … to wrong order of sasl and compression
  2. @sprhawk
  3. @sprhawk
View
13 Core/XMPPStream.h
@@ -15,7 +15,10 @@
@class XMPPPresence;
@class XMPPModule;
@class XMPPElementReceipt;
+@class XMPPFeature;
@protocol XMPPStreamDelegate;
+@protocol XMPPElementHandler;
+@protocol XMPPStreamPreprocessor;
#if TARGET_OS_IPHONE
#define MIN_KEEPALIVE_INTERVAL 20.0 // 20 Seconds
@@ -651,6 +654,16 @@ extern const NSTimeInterval XMPPStreamTimeoutNone;
**/
- (void)enumerateModulesOfClass:(Class)aClass withBlock:(void (^)(XMPPModule *module, NSUInteger idx, BOOL *stop))block;
+
+/**
+ *
+ **/
+- (void)addFeature:(XMPPFeature *)feature;
+- (void)removeFeature:(XMPPFeature *)feature;
+- (void)addStreamPreprocessor:(id<XMPPStreamPreprocessor>)preprocessor;
+- (void)removeStreamPreprocessor:(id<XMPPStreamPreprocessor>)preprocessor;
+- (void)addElementHandler:(id<XMPPElementHandler>)handler;
+- (void)removeElementHandler:(id<XMPPElementHandler>)handler;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Utilities
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
View
299 Core/XMPPStream.m
@@ -4,9 +4,11 @@
#import "XMPPInternal.h"
#import "XMPPSRVResolver.h"
#import "NSData+XMPP.h"
+#import "XMPPFeature.h"
#import <objc/runtime.h>
#import <libkern/OSAtomic.h>
+#import "XMPPStreamInternal.h"
#if TARGET_OS_IPHONE
// Note: You may need to add the CFNetwork Framework to your project
@@ -31,142 +33,11 @@
**/
#define return_from_block return
-// Define the timeouts (in seconds) for retreiving various parts of the XML stream
-#define TIMEOUT_XMPP_WRITE -1
-#define TIMEOUT_XMPP_READ_START 10
-#define TIMEOUT_XMPP_READ_STREAM -1
-
-// Define the tags we'll use to differentiate what it is we're currently reading or writing
-#define TAG_XMPP_READ_START 100
-#define TAG_XMPP_READ_STREAM 101
-#define TAG_XMPP_WRITE_START 200
-#define TAG_XMPP_WRITE_STREAM 201
-#define TAG_XMPP_WRITE_RECEIPT 202
-
-// Define the timeouts (in seconds) for SRV
-#define TIMEOUT_SRV_RESOLUTION 30.0
-
NSString *const XMPPStreamErrorDomain = @"XMPPStreamErrorDomain";
NSString *const XMPPStreamDidChangeMyJIDNotification = @"XMPPStreamDidChangeMyJID";
const NSTimeInterval XMPPStreamTimeoutNone = -1;
-enum XMPPStreamFlags
-{
- kP2PInitiator = 1 << 0, // If set, we are the P2P initializer
- kIsSecure = 1 << 1, // If set, connection has been secured via SSL/TLS
- kIsAuthenticated = 1 << 2, // If set, authentication has succeeded
- kDidStartNegotiation = 1 << 3, // If set, negotiation has started at least once
-};
-
-enum XMPPStreamConfig
-{
- kP2PMode = 1 << 0, // If set, the XMPPStream was initialized in P2P mode
- kResetByteCountPerConnection = 1 << 1, // If set, byte count should be reset per connection
-#if TARGET_OS_IPHONE
- kEnableBackgroundingOnSocket = 1 << 2, // If set, the VoIP flag should be set on the socket
-#endif
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#pragma mark -
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-@interface XMPPStream ()
-{
- dispatch_queue_t xmppQueue;
- void *xmppQueueTag;
-
- dispatch_queue_t willSendIqQueue;
- dispatch_queue_t willSendMessageQueue;
- dispatch_queue_t willSendPresenceQueue;
-
- dispatch_queue_t willReceiveIqQueue;
- dispatch_queue_t willReceiveMessageQueue;
- dispatch_queue_t willReceivePresenceQueue;
-
- dispatch_queue_t didReceiveIqQueue;
-
- dispatch_source_t connectTimer;
-
- GCDMulticastDelegate <XMPPStreamDelegate> *multicastDelegate;
-
- int state;
-
- GCDAsyncSocket *asyncSocket;
-
- UInt64 numberOfBytesSent;
- UInt64 numberOfBytesReceived;
-
- XMPPParser *parser;
- NSError *parserError;
-
- Byte flags;
- Byte config;
-
- NSString *hostName;
- UInt16 hostPort;
-
- BOOL autoStartTLS;
-
- id <XMPPSASLAuthentication> auth;
- NSDate *authenticationDate;
-
- XMPPJID *myJID_setByClient;
- XMPPJID *myJID_setByServer;
- XMPPJID *remoteJID;
-
- XMPPPresence *myPresence;
- NSXMLElement *rootElement;
-
- NSTimeInterval keepAliveInterval;
- dispatch_source_t keepAliveTimer;
- NSTimeInterval lastSendReceiveTime;
- NSData *keepAliveData;
-
- NSMutableArray *registeredModules;
- NSMutableDictionary *autoDelegateDict;
-
- XMPPSRVResolver *srvResolver;
- NSArray *srvResults;
- NSUInteger srvResultsIndex;
-
- NSMutableArray *receipts;
-
- NSThread *xmppUtilityThread;
- NSRunLoop *xmppUtilityRunLoop;
-
- id userTag;
-}
-
-- (void)setIsSecure:(BOOL)flag;
-- (void)setIsAuthenticated:(BOOL)flag;
-- (void)continueSendIQ:(XMPPIQ *)iq withTag:(long)tag;
-- (void)continueSendMessage:(XMPPMessage *)message withTag:(long)tag;
-- (void)continueSendPresence:(XMPPPresence *)presence withTag:(long)tag;
-- (void)startNegotiation;
-- (void)sendOpeningNegotiation;
-- (void)continueStartTLS:(NSMutableDictionary *)settings;
-- (void)continueHandleBinding:(NSString *)alternativeResource;
-- (void)setupKeepAliveTimer;
-- (void)keepAlive;
-
-- (void)startConnectTimeout:(NSTimeInterval)timeout;
-- (void)endConnectTimeout;
-- (void)doConnectTimeout;
-
-- (void)continueReceiveMessage:(XMPPMessage *)message;
-- (void)continueReceiveIQ:(XMPPIQ *)iq;
-- (void)continueReceivePresence:(XMPPPresence *)presence;
-
-@end
-
-@interface XMPPElementReceipt (PrivateAPI)
-
-- (void)signalSuccess;
-- (void)signalFailure;
-
-@end
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
@@ -214,6 +85,10 @@ - (void)commonInit
receipts = [[NSMutableArray alloc] init];
+ registeredFeatures = [[NSMutableArray alloc] init];
+ registeredStreamPreprocessors = [[NSMutableArray alloc] init];
+ registeredElementHandlers = [[NSMutableArray alloc] init];
+
// Setup and start the utility thread.
// We need to be careful to ensure the thread doesn't retain a reference to us longer than necessary.
@@ -1391,9 +1266,8 @@ - (void)disconnectAfterSending
NSData *termData = [termStr dataUsingEncoding:NSUTF8StringEncoding];
XMPPLogSend(@"SEND: %@", termStr);
- numberOfBytesSent += [termData length];
- [asyncSocket writeData:termData withTimeout:TIMEOUT_XMPP_WRITE tag:TAG_XMPP_WRITE_STREAM];
+ [self writeData:termData withTimeout:TIMEOUT_XMPP_WRITE tag:TAG_XMPP_WRITE_STREAM];
[asyncSocket disconnectAfterWriting];
// Everthing will be handled in socketDidDisconnect:withError:
@@ -1484,9 +1358,8 @@ - (void)sendStartTLSRequest
NSData *outgoingData = [starttls dataUsingEncoding:NSUTF8StringEncoding];
XMPPLogSend(@"SEND: %@", starttls);
- numberOfBytesSent += [outgoingData length];
- [asyncSocket writeData:outgoingData
+ [self writeData:outgoingData
withTimeout:TIMEOUT_XMPP_WRITE
tag:TAG_XMPP_WRITE_STREAM];
}
@@ -1641,9 +1514,8 @@ - (BOOL)registerWithElements:(NSArray *)elements error:(NSError **)errPtr
NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
XMPPLogSend(@"SEND: %@", outgoingStr);
- numberOfBytesSent += [outgoingData length];
- [asyncSocket writeData:outgoingData
+ [self writeData:outgoingData
withTimeout:TIMEOUT_XMPP_WRITE
tag:TAG_XMPP_WRITE_STREAM];
@@ -2362,9 +2234,8 @@ - (void)continueSendIQ:(XMPPIQ *)iq withTag:(long)tag
NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
XMPPLogSend(@"SEND: %@", outgoingStr);
- numberOfBytesSent += [outgoingData length];
- [asyncSocket writeData:outgoingData
+ [self writeData:outgoingData
withTimeout:TIMEOUT_XMPP_WRITE
tag:tag];
@@ -2380,9 +2251,8 @@ - (void)continueSendMessage:(XMPPMessage *)message withTag:(long)tag
NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
XMPPLogSend(@"SEND: %@", outgoingStr);
- numberOfBytesSent += [outgoingData length];
- [asyncSocket writeData:outgoingData
+ [self writeData:outgoingData
withTimeout:TIMEOUT_XMPP_WRITE
tag:tag];
@@ -2398,9 +2268,8 @@ - (void)continueSendPresence:(XMPPPresence *)presence withTag:(long)tag
NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
XMPPLogSend(@"SEND: %@", outgoingStr);
- numberOfBytesSent += [outgoingData length];
- [asyncSocket writeData:outgoingData
+ [self writeData:outgoingData
withTimeout:TIMEOUT_XMPP_WRITE
tag:tag];
@@ -2431,9 +2300,8 @@ - (void)continueSendElement:(NSXMLElement *)element withTag:(long)tag
NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
XMPPLogSend(@"SEND: %@", outgoingStr);
- numberOfBytesSent += [outgoingData length];
- [asyncSocket writeData:outgoingData
+ [self writeData:outgoingData
withTimeout:TIMEOUT_XMPP_WRITE
tag:tag];
}
@@ -2650,9 +2518,8 @@ - (void)sendAuthElement:(NSXMLElement *)element
NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
XMPPLogSend(@"SEND: %@", outgoingStr);
- numberOfBytesSent += [outgoingData length];
- [asyncSocket writeData:outgoingData
+ [self writeData:outgoingData
withTimeout:TIMEOUT_XMPP_WRITE
tag:TAG_XMPP_WRITE_STREAM];
}
@@ -2999,6 +2866,21 @@ - (void)injectElement:(NSXMLElement *)element
dispatch_async(xmppQueue, block);
}
+- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag
+{
+ NSData * newData = data;
+ for (id<XMPPStreamPreprocessor> f in registeredStreamPreprocessors) {
+ newData = [f processOutputData:newData];
+ }
+ numberOfBytesSent += [newData length];
+ [asyncSocket writeData:newData withTimeout:timeout tag:tag];
+}
+
+- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag
+{
+ [asyncSocket readDataWithTimeout:timeout tag:tag];
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Stream Negotiation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -3020,7 +2902,7 @@ - (void)startNegotiation
[multicastDelegate xmppStreamDidStartNegotiation:self];
// And start reading in the server's XML stream
- [asyncSocket readDataWithTimeout:TIMEOUT_XMPP_READ_START tag:TAG_XMPP_READ_START];
+ [self readDataWithTimeout:TIMEOUT_XMPP_READ_START tag:TAG_XMPP_READ_START];
}
/**
@@ -3040,9 +2922,8 @@ - (void)sendOpeningNegotiation
NSData *outgoingData = [s1 dataUsingEncoding:NSUTF8StringEncoding];
XMPPLogSend(@"SEND: %@", s1);
- numberOfBytesSent += [outgoingData length];
- [asyncSocket writeData:outgoingData
+ [self writeData:outgoingData
withTimeout:TIMEOUT_XMPP_WRITE
tag:TAG_XMPP_WRITE_START];
@@ -3113,9 +2994,8 @@ - (void)sendOpeningNegotiation
NSData *outgoingData = [s2 dataUsingEncoding:NSUTF8StringEncoding];
XMPPLogSend(@"SEND: %@", s2);
- numberOfBytesSent += [outgoingData length];
- [asyncSocket writeData:outgoingData
+ [self writeData:outgoingData
withTimeout:TIMEOUT_XMPP_WRITE
tag:TAG_XMPP_WRITE_START];
@@ -3219,7 +3099,7 @@ - (void)continueStartTLS:(NSMutableDictionary *)settings
// We paused reading from the socket.
// We're ready to continue now.
- [asyncSocket readDataWithTimeout:TIMEOUT_XMPP_READ_STREAM tag:TAG_XMPP_READ_STREAM];
+ [self readDataWithTimeout:TIMEOUT_XMPP_READ_STREAM tag:TAG_XMPP_READ_STREAM];
}
else
{
@@ -3267,6 +3147,23 @@ - (void)handleStreamFeatures
}
}
+ // According to XEP-0170: Recommended Order of Stream Feature Negotiation
+ // Stream Compression should be before resource binding.
+ // Currently only Stream Compression external feature is supported,
+ // so just put the for loop before the resource binding.
+ // When (if has more requirement) resource binding feature is also removed
+ // out of XMPStream as a separated Feature (module), we can use the sequence
+ // of the array items to control the order.
+
+ NSXMLElement *f_need_auth = [features elementForName:@"auth" xmlns:@"http://jabber.org/features/iq-auth"];
+ if (!f_need_auth) { // we must ensure authentication come first
+ for (XMPPFeature * feature in registeredFeatures) {
+ if ([feature handleFeatures:features]) {
+ return ;
+ }
+ }
+ }
+
// Check to see if resource binding is required
// Don't forget about that NSXMLElement bug you reported to apple (xmlns is required or element won't be found)
NSXMLElement *f_bind = [features elementForName:@"bind" xmlns:@"urn:ietf:params:xml:ns:xmpp-bind"];
@@ -3297,9 +3194,8 @@ - (void)handleStreamFeatures
NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
XMPPLogSend(@"SEND: %@", outgoingStr);
- numberOfBytesSent += [outgoingData length];
- [asyncSocket writeData:outgoingData
+ [self writeData:outgoingData
withTimeout:TIMEOUT_XMPP_WRITE
tag:TAG_XMPP_WRITE_STREAM];
}
@@ -3318,9 +3214,8 @@ - (void)handleStreamFeatures
NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
XMPPLogSend(@"SEND: %@", outgoingStr);
- numberOfBytesSent += [outgoingData length];
- [asyncSocket writeData:outgoingData
+ [self writeData:outgoingData
withTimeout:TIMEOUT_XMPP_WRITE
tag:TAG_XMPP_WRITE_STREAM];
}
@@ -3424,7 +3319,7 @@ - (void)handleAuth:(NSXMLElement *)authResponse
// So start read request here.
// The state is STATE_XMPP_OPENING, set via sendOpeningNegotiation method.
- [asyncSocket readDataWithTimeout:TIMEOUT_XMPP_READ_START tag:TAG_XMPP_READ_START];
+ [self readDataWithTimeout:TIMEOUT_XMPP_READ_START tag:TAG_XMPP_READ_START];
}
}
else
@@ -3504,9 +3399,8 @@ - (void)handleBinding:(NSXMLElement *)response
NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
XMPPLogSend(@"SEND: %@", outgoingStr);
- numberOfBytesSent += [outgoingData length];
- [asyncSocket writeData:outgoingData
+ [self writeData:outgoingData
withTimeout:TIMEOUT_XMPP_WRITE
tag:TAG_XMPP_WRITE_STREAM];
@@ -3610,9 +3504,8 @@ - (void)continueHandleBinding:(NSString *)alternativeResource
NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
XMPPLogSend(@"SEND: %@", outgoingStr);
- numberOfBytesSent += [outgoingData length];
- [asyncSocket writeData:outgoingData
+ [self writeData:outgoingData
withTimeout:TIMEOUT_XMPP_WRITE
tag:TAG_XMPP_WRITE_STREAM];
@@ -3631,9 +3524,8 @@ - (void)continueHandleBinding:(NSString *)alternativeResource
NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
XMPPLogSend(@"SEND: %@", outgoingStr);
- numberOfBytesSent += [outgoingData length];
- [asyncSocket writeData:outgoingData
+ [self writeData:outgoingData
withTimeout:TIMEOUT_XMPP_WRITE
tag:TAG_XMPP_WRITE_STREAM];
@@ -3819,21 +3711,26 @@ - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)t
lastSendReceiveTime = [NSDate timeIntervalSinceReferenceDate];
numberOfBytesReceived += [data length];
- XMPPLogRecvPre(@"RECV: %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
-
+ NSData * newData = data;
+ for (id<XMPPStreamPreprocessor> f in registeredStreamPreprocessors) {
+ newData = [f processInputData:newData];
+ }
+
+ XMPPLogRecvPre(@"RECV: %@", [[NSString alloc] initWithData:newData encoding:NSUTF8StringEncoding]);
+
// Asynchronously parse the xml data
- [parser parseData:data];
+ [parser parseData:newData];
if ([self isSecure])
{
// Continue reading for XML elements
if (state == STATE_XMPP_OPENING)
{
- [asyncSocket readDataWithTimeout:TIMEOUT_XMPP_READ_START tag:TAG_XMPP_READ_START];
+ [self readDataWithTimeout:TIMEOUT_XMPP_READ_START tag:TAG_XMPP_READ_START];
}
else
{
- [asyncSocket readDataWithTimeout:TIMEOUT_XMPP_READ_STREAM tag:TAG_XMPP_READ_STREAM];
+ [self readDataWithTimeout:TIMEOUT_XMPP_READ_STREAM tag:TAG_XMPP_READ_STREAM];
}
}
else
@@ -3988,9 +3885,8 @@ - (void)xmppParser:(XMPPParser *)sender didReadRoot:(NSXMLElement *)root
NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
XMPPLogSend(@"SEND: %@", outgoingStr);
- numberOfBytesSent += [outgoingData length];
- [asyncSocket writeData:outgoingData
+ [self writeData:outgoingData
withTimeout:TIMEOUT_XMPP_WRITE
tag:TAG_XMPP_WRITE_STREAM];
}
@@ -4032,9 +3928,8 @@ - (void)xmppParser:(XMPPParser *)sender didReadRoot:(NSXMLElement *)root
NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
XMPPLogSend(@"SEND: %@", outgoingStr);
- numberOfBytesSent += [outgoingData length];
- [asyncSocket writeData:outgoingData
+ [self writeData:outgoingData
withTimeout:TIMEOUT_XMPP_WRITE
tag:TAG_XMPP_WRITE_STREAM];
@@ -4063,10 +3958,17 @@ - (void)xmppParser:(XMPPParser *)sender didReadElement:(NSXMLElement *)element
if (state == STATE_XMPP_NEGOTIATING)
{
+
+ //During processing other features, we shall not set rootElement
+ for (id<XMPPElementHandler> handler in registeredElementHandlers) {
+ if ([handler handleElement:element]) {
+ return ;
+ }
+ }
+
// We've just read in the stream features
// We consider this part of the root element, so we'll add it (replacing any previously sent features)
[rootElement setChildren:[NSArray arrayWithObject:element]];
-
// Call a method to handle any requirements set forth in the features
[self handleStreamFeatures];
}
@@ -4134,11 +4036,11 @@ - (void)xmppParserDidParseData:(XMPPParser *)sender
// Continue reading for XML elements
if (state == STATE_XMPP_OPENING)
{
- [asyncSocket readDataWithTimeout:TIMEOUT_XMPP_READ_START tag:TAG_XMPP_READ_START];
+ [self readDataWithTimeout:TIMEOUT_XMPP_READ_START tag:TAG_XMPP_READ_START];
}
else if (state != STATE_XMPP_STARTTLS_2) // Don't queue read operation prior to [asyncSocket startTLS:]
{
- [asyncSocket readDataWithTimeout:TIMEOUT_XMPP_READ_STREAM tag:TAG_XMPP_READ_STREAM];
+ [self readDataWithTimeout:TIMEOUT_XMPP_READ_STREAM tag:TAG_XMPP_READ_STREAM];
}
}
}
@@ -4228,9 +4130,8 @@ - (void)keepAlive
if (elapsed < 0 || elapsed >= keepAliveInterval)
{
- numberOfBytesSent += [keepAliveData length];
- [asyncSocket writeData:keepAliveData
+ [self writeData:keepAliveData
withTimeout:TIMEOUT_XMPP_WRITE
tag:TAG_XMPP_WRITE_STREAM];
@@ -4479,6 +4380,48 @@ - (void)enumerateModulesOfClass:(Class)aClass withBlock:(void (^)(XMPPModule *mo
}];
}
+- (void)addFeature:(XMPPFeature *)feature
+{
+ if (![registeredFeatures containsObject:feature]) {
+ [registeredFeatures addObject:feature];
+ }
+}
+
+- (void)removeFeature:(XMPPFeature *)feature
+{
+ if ([registeredFeatures containsObject:feature]) {
+ [registeredFeatures removeObject:feature];
+ }
+}
+
+- (void)addStreamPreprocessor:(id<XMPPStreamPreprocessor>)preprocessor
+{
+ if (![registeredStreamPreprocessors containsObject:preprocessor]) {
+ [registeredStreamPreprocessors addObject:preprocessor];
+ }
+}
+
+- (void)removeStreamPreprocessor:(id<XMPPStreamPreprocessor>)preprocessor
+{
+ if ([registeredStreamPreprocessors containsObject:preprocessor]) {
+ [registeredStreamPreprocessors removeObject:preprocessor];
+ }
+}
+
+- (void)addElementHandler:(id<XMPPElementHandler>)handler
+{
+ if (![registeredElementHandlers containsObject:handler]) {
+ [registeredElementHandlers addObject:handler];
+ }
+}
+
+- (void)removeElementHandler:(id<XMPPElementHandler>)handler
+{
+ if ([registeredElementHandlers containsObject:handler]) {
+ [registeredElementHandlers removeObject:handler];
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Utilities
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
View
141 Core/XMPPStreamInternal.h
@@ -0,0 +1,141 @@
+// Define the timeouts (in seconds) for retreiving various parts of the XML stream
+#define TIMEOUT_XMPP_WRITE -1
+#define TIMEOUT_XMPP_READ_START 10
+#define TIMEOUT_XMPP_READ_STREAM -1
+
+// Define the tags we'll use to differentiate what it is we're currently reading or writing
+#define TAG_XMPP_READ_START 100
+#define TAG_XMPP_READ_STREAM 101
+#define TAG_XMPP_WRITE_START 200
+#define TAG_XMPP_WRITE_STREAM 201
+#define TAG_XMPP_WRITE_RECEIPT 202
+
+// Define the timeouts (in seconds) for SRV
+#define TIMEOUT_SRV_RESOLUTION 30.0
+
+enum XMPPStreamFlags
+{
+ kP2PInitiator = 1 << 0, // If set, we are the P2P initializer
+ kIsSecure = 1 << 1, // If set, connection has been secured via SSL/TLS
+ kIsAuthenticated = 1 << 2, // If set, authentication has succeeded
+ kDidStartNegotiation = 1 << 3, // If set, negotiation has started at least once
+};
+
+enum XMPPStreamConfig
+{
+ kP2PMode = 1 << 0, // If set, the XMPPStream was initialized in P2P mode
+ kResetByteCountPerConnection = 1 << 1, // If set, byte count should be reset per connection
+#if TARGET_OS_IPHONE
+ kEnableBackgroundingOnSocket = 1 << 2, // If set, the VoIP flag should be set on the socket
+#endif
+};
+
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface XMPPStream ()
+{
+ dispatch_queue_t xmppQueue;
+ void *xmppQueueTag;
+
+ dispatch_queue_t willSendIqQueue;
+ dispatch_queue_t willSendMessageQueue;
+ dispatch_queue_t willSendPresenceQueue;
+
+ dispatch_queue_t willReceiveIqQueue;
+ dispatch_queue_t willReceiveMessageQueue;
+ dispatch_queue_t willReceivePresenceQueue;
+
+ dispatch_queue_t didReceiveIqQueue;
+
+ dispatch_source_t connectTimer;
+
+ GCDMulticastDelegate <XMPPStreamDelegate> *multicastDelegate;
+
+ int state;
+
+ GCDAsyncSocket *asyncSocket;
+
+ UInt64 numberOfBytesSent;
+ UInt64 numberOfBytesReceived;
+
+ XMPPParser *parser;
+ NSError *parserError;
+
+ Byte flags;
+ Byte config;
+
+ NSString *hostName;
+ UInt16 hostPort;
+
+ BOOL autoStartTLS;
+
+ id <XMPPSASLAuthentication> auth;
+ NSDate *authenticationDate;
+
+ XMPPJID *myJID_setByClient;
+ XMPPJID *myJID_setByServer;
+ XMPPJID *remoteJID;
+
+ XMPPPresence *myPresence;
+ NSXMLElement *rootElement;
+
+ NSTimeInterval keepAliveInterval;
+ dispatch_source_t keepAliveTimer;
+ NSTimeInterval lastSendReceiveTime;
+ NSData *keepAliveData;
+
+ NSMutableArray *registeredModules;
+ NSMutableDictionary *autoDelegateDict;
+
+ XMPPSRVResolver *srvResolver;
+ NSArray *srvResults;
+ NSUInteger srvResultsIndex;
+
+ NSMutableArray *receipts;
+
+ NSThread *xmppUtilityThread;
+ NSRunLoop *xmppUtilityRunLoop;
+
+ id userTag;
+
+ NSMutableArray * registeredFeatures;
+ NSMutableArray * registeredStreamPreprocessors;
+ NSMutableArray * registeredElementHandlers;
+
+}
+
+- (void)setIsSecure:(BOOL)flag;
+- (void)setIsAuthenticated:(BOOL)flag;
+- (void)continueSendIQ:(XMPPIQ *)iq withTag:(long)tag;
+- (void)continueSendMessage:(XMPPMessage *)message withTag:(long)tag;
+- (void)continueSendPresence:(XMPPPresence *)presence withTag:(long)tag;
+- (void)startNegotiation;
+- (void)sendOpeningNegotiation;
+- (void)continueStartTLS:(NSMutableDictionary *)settings;
+- (void)continueHandleBinding:(NSString *)alternativeResource;
+- (void)setupKeepAliveTimer;
+- (void)keepAlive;
+
+- (void)startConnectTimeout:(NSTimeInterval)timeout;
+- (void)endConnectTimeout;
+- (void)doConnectTimeout;
+
+- (void)continueReceiveMessage:(XMPPMessage *)message;
+- (void)continueReceiveIQ:(XMPPIQ *)iq;
+- (void)continueReceivePresence:(XMPPPresence *)presence;
+
+// A Wrapper, prepared for stream compression
+- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
+- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag;
+@end
+
+@interface XMPPElementReceipt (PrivateAPI)
+
+- (void)signalSuccess;
+- (void)signalFailure;
+
+@end
View
6 Extensions/XEP-0138/XMPPCompression.h
@@ -0,0 +1,6 @@
+#import <Foundation/Foundation.h>
+#import "XMPPFeature.h"
+
+@interface XMPPCompression : XMPPFeature <XMPPStreamPreprocessor, XMPPElementHandler>
+@property (nonatomic, copy, readonly) NSString *compressionMethod;
+@end
View
347 Extensions/XEP-0138/XMPPCompression.m
@@ -0,0 +1,347 @@
+//
+// XMPPCompression.m
+// xmpp
+//
+// Created by YANG HONGBO on 2013-9-6.
+// Copyright (c) 2013年 YANG HONGBO. All rights reserved.
+//
+
+#import "XMPPCompression.h"
+#import "zlib.h"
+#import "XMPPLogging.h"
+#import "XMPPStreamInternal.h"
+
+#if DEBUG
+static const int xmppLogLevel = XMPP_LOG_LEVEL_INFO | XMPP_LOG_FLAG_SEND_RECV; // | XMPP_LOG_FLAG_TRACE;
+#else
+static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
+#endif
+
+typedef enum XMPPCompressionState {
+ XMPPCompressionStateNegotiating,
+ XMPPCompressionStateRequestingCompress,
+ XMPPCompressionStateCompressing,
+ XMPPCompressionStateFailureUnsupportedMethod,
+ XMPPCompressionStateFailureSetupFailed,
+}XMPPCompressionState;
+
+static NSString * const XMPPCompressionFeatureNS = @"http://jabber.org/features/compress";
+static NSString * const XMPPCompressionProtocolNS = @"http://jabber.org/protocol/compress";
+
+@interface XMPPCompression ()
+{
+ z_stream _inflation_strm;
+ z_stream _deflation_strm;
+}
+@property (atomic, copy, readwrite) NSString *compressionMethod;
+@property (assign, readwrite) XMPPCompressionState compressionState;
+@end
+
+@implementation XMPPCompression
+
++ (NSArray *)supportedCompressionMethods
+{
+ return @[@"zlib"];
+}
+
+- (id)init
+{
+ self = [super init];
+ if (self) {
+ memset(&_inflation_strm, 0, sizeof(z_stream));
+ memset(&_deflation_strm, 0, sizeof(z_stream));
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [self endCompression];
+}
+
+- (void)activate:(XMPPStream *)xmppStream
+{
+ [super activate:xmppStream];
+ [self.xmppStream addElementHandler:self];
+ [self.xmppStream addStreamPreprocessor:self];
+}
+
+- (void)deactivate
+{
+ [self.xmppStream removeElementHandler:self];
+ [self.xmppStream removeStreamPreprocessor:self];
+ [super deactivate];
+}
+
+- (BOOL)handleFeatures:(NSXMLElement *)features
+{
+ XMPPLogTrace();
+ if ([self handleCompressionMethods:features]) {
+ return YES;
+ }
+ return NO;
+}
+
+- (BOOL)handleElement:(NSXMLElement *)element
+{
+ if([[element xmlns] isEqualToString:XMPPCompressionProtocolNS]) {
+ if ([self handleCompressed:element]) {
+ return YES;
+ }
+ else if ([self handleFailure:element]) {
+ return YES;
+ }
+ }
+ return NO;
+}
+
+- (BOOL)handleCompressionMethods:(NSXMLElement *)features
+{
+ XMPPLogTrace();
+
+ if (XMPPCompressionStateNegotiating == self.compressionState) {
+ NSXMLElement *compression = [features elementForName:@"compression"
+ xmlns:XMPPCompressionFeatureNS];
+ if (compression) {
+ NSArray *methods = [compression elementsForName:@"method"];
+ for (NSString *clientSideMethod in [[self class] supportedCompressionMethods]) {
+ for (NSXMLElement *method in methods)
+ {
+ if ([[method stringValue] isEqualToString:clientSideMethod])
+ {
+
+ XMPPLogVerbose(@"Compression Method: %@", clientSideMethod);
+ self.compressionMethod = clientSideMethod;
+ self.compressionState = XMPPCompressionStateRequestingCompress;
+ NSXMLElement * compress = [NSXMLElement elementWithName:@"compress"
+ xmlns:XMPPCompressionProtocolNS];
+ NSXMLElement * methodChild = [NSXMLElement elementWithName:@"method" stringValue:clientSideMethod];
+ [compress addChild:methodChild];
+
+ NSString *outgoingStr = [compress compactXMLString];
+ NSData *outgoingData = [outgoingStr dataUsingEncoding:NSUTF8StringEncoding];
+
+ XMPPLogSend(@"SEND: %@", outgoingStr);
+
+ [self.xmppStream writeData:outgoingData
+ withTimeout:TIMEOUT_XMPP_WRITE
+ tag:TAG_XMPP_WRITE_STREAM];
+ return YES;
+ }
+ }
+ }
+ }
+ }
+ return NO;
+}
+
+- (BOOL)handleCompressed:(NSXMLElement *)element
+{
+ XMPPLogTrace();
+ //Using TLS is the first choice
+ if ([self.xmppStream isSecure]) {
+ return NO;
+ }
+ if (XMPPCompressionStateRequestingCompress == self.compressionState) {
+ if([[element name] isEqualToString:@"compressed"]) {
+ self.compressionState = XMPPCompressionStateCompressing;
+ [self prepareCompression];
+ [self startStream];
+ return YES;
+ }
+ }
+ return NO;
+}
+
+- (BOOL)handleFailure:(NSXMLElement *)element
+{
+ BOOL handled = NO;
+
+ if([[element name] isEqualToString:@"failure"]) {
+ if ([element elementForName:@"unsupported-method"]) {
+ self.compressionState = XMPPCompressionStateFailureUnsupportedMethod;
+ XMPPLogError(@"Compression Failure: %@", @"unsupported-method");
+ handled = YES;
+ }
+ else if ([element elementForName:@"setup-failed"]) {
+ self.compressionState = XMPPCompressionStateFailureSetupFailed;
+ XMPPLogError(@"Compression Failure: %@", @"setup-failed");
+ handled = YES;
+ }
+ }
+
+ if (handled) {
+ }
+ return handled;
+}
+
+- (void)prepareCompression
+{
+ [self endCompression];
+ inflateInit(&_inflation_strm);
+ deflateInit(&_deflation_strm, Z_BEST_COMPRESSION);
+}
+
+- (void)endCompression
+{
+ inflateEnd(&_inflation_strm);
+ deflateEnd(&_deflation_strm);
+
+ memset(&_inflation_strm, 0, sizeof(z_stream));
+ memset(&_deflation_strm, 0, sizeof(z_stream));
+}
+
+- (NSData *)processInputData:(NSData *)data
+{
+ NSData * returnData = data;
+ if ([self handleStreamError:data]) {
+ return returnData; //if it has an stream error, it is plain xml text
+ }
+ if (XMPPCompressionStateCompressing == self.compressionState) {
+ NSMutableData * newMutableData = nil; //if inflated buffer exceeds buffer size, then use NSMutableData
+ NSData * newData = nil; // else use a NSData instead -- to minize alloca operations
+ uLongf offset = 0;
+ Bytef output[1024];
+ const uLongf outputSz = sizeof(output);
+ int ret = Z_ERRNO;
+ while (offset < data.length) {
+ int flush = Z_NO_FLUSH;
+ uLongf blockSz = 256;
+
+ Bytef * buf = (Bytef *)data.bytes + offset;
+ if (offset + blockSz >= data.length ) {
+ blockSz = data.length - offset;
+ flush = Z_SYNC_FLUSH;
+ }
+
+ _inflation_strm.next_in = buf;
+ _inflation_strm.avail_in = blockSz;
+ _inflation_strm.avail_out = sizeof(output);
+ _inflation_strm.next_out = output;
+
+ ret = inflate(&_inflation_strm, flush);
+ if (Z_OK == ret || Z_STREAM_END == ret) {
+ uLongf sz = outputSz - _inflation_strm.avail_out;
+ if (sz) {
+ if (!newData && !newMutableData) {
+ if(Z_NO_FLUSH != flush) {
+ newData = [NSData dataWithBytes:output length:sz];
+ }
+ else {
+ // current output buffer is not enough, so double it
+ newMutableData = [NSMutableData dataWithCapacity:outputSz * 2];
+ }
+ }
+ if (newMutableData) {
+ [newMutableData appendBytes:output length:sz];
+ }
+ }
+ }
+ else {
+ _inflation_strm.total_out = 0;
+ XMPPLogError(@"Inflation failed: %d(%s)", ret, _inflation_strm.msg);
+ newData = [NSData data];
+ [self sendStreamError];
+ break;
+ }
+ offset += blockSz;
+ }
+ if (!newData) {
+ if (newMutableData) {
+ newData = [newMutableData copy];
+ }
+ else {
+ newData = nil;
+ }
+ }
+ if (ret >= Z_OK) {
+ XMPPLogRecvPost(@"RECV: Compression Rate:%.4f%%(%d/%d)", data.length * 1.0 / newData.length * 100.0f, data.length, newData.length);
+ }
+ returnData = newData;
+ }
+ return returnData;
+}
+
+- (NSData *)processOutputData:(NSData *)data
+{
+ NSData * returnData = data;
+ if (XMPPCompressionStateCompressing == self.compressionState) {
+ const uLongf bufferSize = deflateBound(&_deflation_strm, data.length);
+ Bytef * buffer = (Bytef *)malloc(bufferSize);
+ int ret = Z_ERRNO;
+ if (buffer) {
+ _deflation_strm.next_out = buffer;
+ _deflation_strm.avail_out = bufferSize;
+ _deflation_strm.next_in = (Bytef *)data.bytes;
+ _deflation_strm.avail_in = data.length;
+ ret = deflate(&_deflation_strm, Z_PARTIAL_FLUSH);
+
+ if (ret >= Z_OK) {
+ const uLongf len = bufferSize - _deflation_strm.avail_out;
+ XMPPLogSend(@"SEND: Compression Rate:%.4f%%(%ld/%d)", len * 1.0 / data.length * 100.0f, len, data.length);
+ returnData = [NSData dataWithBytesNoCopy:buffer length:len freeWhenDone:YES];
+ }
+ else {
+ XMPPLogError(@"Deflation failed:%d(%s)", ret, _deflation_strm.msg?_deflation_strm.msg:"");
+ returnData = [NSData data];
+ }
+
+ }
+ else {
+ // I think returning a space (keepalive) is better than returning a empty data
+ returnData = [NSData data];
+ XMPPLogError(@"Cannot alloca enough memory");
+ }
+ }
+
+ return returnData;
+}
+
+// return YES if this has handled stream error
+// NO, if not a stream error
+- (BOOL)handleStreamError:(NSData *)data
+{
+ const char * bytes = (const char *)data.bytes;
+ static const char stream_error_tag[] = "<stream:error>";
+ static const int len = sizeof(stream_error_tag);
+ int r = strncmp(stream_error_tag, bytes, len);
+ if (0 == r) { // restart stream
+ XMPPLogTrace();
+ // should restart stream, but current stream handler in XMPPStream will disconnectAfterSending ...
+ return YES;
+ }
+ else {
+ return NO;
+ }
+}
+
+- (void)sendStreamError
+{
+ NSXMLElement *streamErrorElement = [NSXMLElement elementWithName:@"stream:error"];
+ NSXMLElement *undefinedConditionElement = [NSXMLElement elementWithName:@"undefined-condition" xmlns:@"urn:ietf:params:xml:ns:xmpp-streams"];
+ [streamErrorElement addChild:undefinedConditionElement];
+
+ NSXMLElement *failure = [NSXMLElement elementWithName:@"failure" xmlns:@"http://jabber.org/protocol/compress"];
+ NSXMLElement *processFailed = [NSXMLElement elementWithName:@"processing-failed"];
+ [failure addChild:processFailed];
+ [streamErrorElement addChild:failure];
+ [self.xmppStream sendElement:streamErrorElement];
+
+ [self endStream];
+}
+
+- (void)startStream
+{
+ //This will reset parser, restart stream
+ [self.xmppStream sendOpeningNegotiation];
+ [self.xmppStream readDataWithTimeout:TIMEOUT_XMPP_READ_START tag:TAG_XMPP_READ_START];
+}
+
+- (void)endStream
+{
+ NSString *termStr = @"</stream:stream>";
+ NSData *termData = [termStr dataUsingEncoding:NSUTF8StringEncoding];
+ XMPPLogSend(@"SEND: %@", termStr);
+ [self.xmppStream writeData:termData withTimeout:TIMEOUT_XMPP_WRITE tag:TAG_XMPP_WRITE_STREAM];
+}
+@end
View
24 Features/XMPPFeature.h
@@ -0,0 +1,24 @@
+#import <Foundation/Foundation.h>
+#import "XMPPStream.h"
+#import "NSXMLElement+XMPP.h"
+
+@protocol XMPPStreamPreprocessor <NSObject>
+
+- (NSData *)processInputData:(NSData *)data;
+- (NSData *)processOutputData:(NSData *)data;
+
+@end
+
+@protocol XMPPElementHandler <NSObject>
+
+- (BOOL)handleElement:(NSXMLElement *)element;
+
+@end
+
+@interface XMPPFeature : NSObject
+@property (strong, readonly) XMPPStream * xmppStream;
+- (void)activate:(XMPPStream *)xmppStream;
+- (void)deactivate;
+- (BOOL)handleFeatures:(NSXMLElement *)features;
+
+@end
View
32 Features/XMPPFeature.m
@@ -0,0 +1,32 @@
+#import "XMPPFeature.h"
+
+@interface XMPPFeature ()
+{
+
+}
+@property (strong, readwrite) XMPPStream * xmppStream;
+@end
+
+@implementation XMPPFeature
+- (void)activate:(XMPPStream *)xmppStream
+{
+ self.xmppStream = xmppStream;
+ [self.xmppStream addFeature:self];
+}
+
+- (void)deactivate
+{
+ [self.xmppStream removeFeature:self];
+}
+
+- (BOOL)handleFeatures:(NSXMLElement *)features
+{
+ return NO;
+}
+
+- (BOOL)handleElement:(NSXMLElement *)element
+{
+ return NO;
+}
+
+@end
View
4 Xcode/DesktopXMPP/AppDelegate.h
@@ -14,7 +14,8 @@
XMPPCapabilitiesCoreDataStorage *xmppCapabilitiesStorage;
XMPPPing *xmppPing;
XMPPTime *xmppTime;
-
+ XMPPCompression *xmppCompression;
+
NSMutableArray *turnSockets;
IBOutlet RosterController *rosterController;
@@ -27,6 +28,7 @@
@property (nonatomic, readonly) XMPPCapabilities *xmppCapabilities;
@property (nonatomic, readonly) XMPPCapabilitiesCoreDataStorage *xmppCapabilitiesStorage;
@property (nonatomic, readonly) XMPPPing *xmppPing;
+@property (nonatomic, readonly) XMPPCompression *xmppCompression;
- (void)connectViaXEP65:(XMPPJID *)jid;
View
6 Xcode/DesktopXMPP/AppDelegate.m
@@ -16,6 +16,7 @@ @implementation AppDelegate
@synthesize xmppCapabilities;
@synthesize xmppCapabilitiesStorage;
@synthesize xmppPing;
+@synthesize xmppCompression;
- (id)init
{
@@ -51,6 +52,7 @@ - (id)init
// xmppPing = [[XMPPPing alloc] init];
// xmppTime = [[XMPPTime alloc] init];
+ xmppCompression = [[XMPPCompression alloc] init];
// Activate xmpp modules
[xmppReconnect activate:xmppStream];
@@ -58,12 +60,14 @@ - (id)init
[xmppCapabilities activate:xmppStream];
[xmppPing activate:xmppStream];
[xmppTime activate:xmppStream];
-
+
// Add ourself as a delegate to anything we may be interested in
[xmppReconnect addDelegate:self delegateQueue:dispatch_get_main_queue()];
[xmppCapabilities addDelegate:self delegateQueue:dispatch_get_main_queue()];
+ [xmppCompression activate:xmppStream];
+
// Initialize other stuff
turnSockets = [[NSMutableArray alloc] init];
View
2 Xcode/DesktopXMPP/XMPPFramework.h
@@ -51,3 +51,5 @@
#import "XMPPTime.h"
#import "XMPPAutoTime.h"
+
+#import "XMPPCompression.h"
View
34 Xcode/DesktopXMPP/XMPPStream.xcodeproj/project.pbxproj
@@ -8,6 +8,9 @@
/* Begin PBXBuildFile section */
07AF18A9134BC3C30084D82A /* XMPPSRVResolver.m in Sources */ = {isa = PBXBuildFile; fileRef = 07AF18A8134BC3C30084D82A /* XMPPSRVResolver.m */; };
+ 4392EDF517DB5BB800B8E64D /* XMPPFeature.m in Sources */ = {isa = PBXBuildFile; fileRef = 4392EDF417DB5BB800B8E64D /* XMPPFeature.m */; };
+ 4392EDF917DB7B2000B8E64D /* XMPPCompression.m in Sources */ = {isa = PBXBuildFile; fileRef = 4392EDF817DB7B2000B8E64D /* XMPPCompression.m */; };
+ 4392EDFB17DB7BCC00B8E64D /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 4392EDFA17DB7BCC00B8E64D /* libz.dylib */; };
8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = 29B97318FDCFA39411CA2CEA /* MainMenu.nib */; };
8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
@@ -133,6 +136,11 @@
29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
32CA4F630368D1EE00C91783 /* XMPPStream_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XMPPStream_Prefix.pch; sourceTree = "<group>"; };
+ 4392EDF317DB5BB800B8E64D /* XMPPFeature.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XMPPFeature.h; sourceTree = "<group>"; };
+ 4392EDF417DB5BB800B8E64D /* XMPPFeature.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XMPPFeature.m; sourceTree = "<group>"; };
+ 4392EDF717DB7B2000B8E64D /* XMPPCompression.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XMPPCompression.h; sourceTree = "<group>"; };
+ 4392EDF817DB7B2000B8E64D /* XMPPCompression.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XMPPCompression.m; sourceTree = "<group>"; };
+ 4392EDFA17DB7BCC00B8E64D /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
8D1107320486CEB800E47090 /* XMPPStream.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XMPPStream.app; sourceTree = BUILT_PRODUCTS_DIR; };
962031FE1735833E00C43080 /* NSXMLElement+XEP_0203.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSXMLElement+XEP_0203.h"; path = "../../Extensions/XEP-0203/NSXMLElement+XEP_0203.h"; sourceTree = "<group>"; };
@@ -358,6 +366,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 4392EDFB17DB7BCC00B8E64D /* libz.dylib in Frameworks */,
DCFE9FA513413C4E007C5391 /* CoreLocation.framework in Frameworks */,
DCFE9FA113413B5A007C5391 /* libidn.a in Frameworks */,
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
@@ -417,6 +426,7 @@
29B97314FDCFA39411CA2CEA /* XMPPStream */ = {
isa = PBXGroup;
children = (
+ 4392EDFA17DB7BCC00B8E64D /* libz.dylib */,
DCCE55760BDC8EC8000E2798 /* XMPP */,
DC34A2671486BD86004F0A03 /* SSKeychain */,
DCEC5E55115CF48B00CAA66F /* App - General */,
@@ -460,6 +470,26 @@
name = Frameworks;
sourceTree = "<group>";
};
+ 4392EDF217DB5BB800B8E64D /* Features */ = {
+ isa = PBXGroup;
+ children = (
+ 4392EDF317DB5BB800B8E64D /* XMPPFeature.h */,
+ 4392EDF417DB5BB800B8E64D /* XMPPFeature.m */,
+ );
+ name = Features;
+ path = ../../Features;
+ sourceTree = "<group>";
+ };
+ 4392EDF617DB7B2000B8E64D /* XEP-0138 */ = {
+ isa = PBXGroup;
+ children = (
+ 4392EDF717DB7B2000B8E64D /* XMPPCompression.h */,
+ 4392EDF817DB7B2000B8E64D /* XMPPCompression.m */,
+ );
+ name = "XEP-0138";
+ path = "../../Extensions/XEP-0138";
+ sourceTree = "<group>";
+ };
DC1C5CA2115BA78300F84438 /* Core Data Storage */ = {
isa = PBXGroup;
children = (
@@ -746,6 +776,7 @@
DCAD3FB91156DC0700FC568A /* XEP-0100 */,
DCAD3FBA1156DC1D00FC568A /* XEP-0115 */,
DC4C73F314EF161A002FD8CD /* XEP-0136 */,
+ 4392EDF617DB7B2000B8E64D /* XEP-0138 */,
DC4883FB13496EDB000F79C5 /* XEP-0153 */,
DCAD3FB31156DBE800FC568A /* XEP-0199 */,
DCA6151B1250067000C75522 /* XEP-0202 */,
@@ -971,6 +1002,7 @@
DCB9D56915C208F30063935D /* XMPPFramework.h */,
DC30E699153E099A001B9E6D /* Authentication */,
DCB9BBF30DDCEE6B002DA335 /* Categories */,
+ 4392EDF217DB5BB800B8E64D /* Features */,
DC8BA2F51141B9B40031C1F6 /* Core */,
DC63E91E0E65D4EF0076E6D2 /* Extensions */,
DCB9BAF80DDC962C002DA335 /* Utilities */,
@@ -1249,6 +1281,8 @@
DCD76AAB15C4EB9600FAC260 /* XMPPMessageArchiving.xcdatamodeld in Sources */,
DCD76AAF15C4EBFA00FAC260 /* XMPPMessage+XEP_0085.m in Sources */,
962032001735833E00C43080 /* NSXMLElement+XEP_0203.m in Sources */,
+ 4392EDF517DB5BB800B8E64D /* XMPPFeature.m in Sources */,
+ 4392EDF917DB7B2000B8E64D /* XMPPCompression.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
2 Xcode/iPhoneXMPP/Classes/XMPPFramework.h
@@ -40,3 +40,5 @@
#import "XMPPMUC.h"
#import "XMPPRoomCoreDataStorage.h"
+
+#import "XMPPCompression.h"
View
3 Xcode/iPhoneXMPP/Classes/iPhoneXMPPAppDelegate.h
@@ -17,7 +17,8 @@
XMPPvCardAvatarModule *xmppvCardAvatarModule;
XMPPCapabilities *xmppCapabilities;
XMPPCapabilitiesCoreDataStorage *xmppCapabilitiesStorage;
-
+ XMPPCompression *xmppCompression;
+
NSString *password;
BOOL allowSelfSignedCertificates;
View
6 Xcode/iPhoneXMPP/Classes/iPhoneXMPPAppDelegate.m
@@ -191,6 +191,7 @@ - (void)setupStream
xmppCapabilities.autoFetchHashedCapabilities = YES;
xmppCapabilities.autoFetchNonHashedCapabilities = NO;
+ xmppCompression = [[XMPPCompression alloc] init];
// Activate xmpp modules
[xmppReconnect activate:xmppStream];
@@ -198,7 +199,7 @@ - (void)setupStream
[xmppvCardTempModule activate:xmppStream];
[xmppvCardAvatarModule activate:xmppStream];
[xmppCapabilities activate:xmppStream];
-
+ [xmppCompression activate:xmppStream];
// Add ourself as a delegate to anything we may be interested in
[xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
@@ -234,7 +235,8 @@ - (void)teardownStream
[xmppvCardTempModule deactivate];
[xmppvCardAvatarModule deactivate];
[xmppCapabilities deactivate];
-
+ [xmppCompression deactivate];
+
[xmppStream disconnect];
xmppStream = nil;
View
37 Xcode/iPhoneXMPP/iPhoneXMPP.xcodeproj/project.pbxproj
@@ -41,6 +41,9 @@
28AD73600D9D9599002E5188 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28AD735F0D9D9599002E5188 /* MainWindow.xib */; };
28C286E10D94DF7D0034E888 /* RootViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 28C286E00D94DF7D0034E888 /* RootViewController.m */; };
28F335F11007B36200424DE2 /* RootViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28F335F01007B36200424DE2 /* RootViewController.xib */; };
+ 4392EDFF17DB7C8500B8E64D /* XMPPCompression.m in Sources */ = {isa = PBXBuildFile; fileRef = 4392EDFE17DB7C8500B8E64D /* XMPPCompression.m */; };
+ 4392EE0317DB7CA200B8E64D /* XMPPFeature.m in Sources */ = {isa = PBXBuildFile; fileRef = 4392EE0217DB7CA200B8E64D /* XMPPFeature.m */; };
+ 4392EE0517DB7D0200B8E64D /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 4392EE0417DB7D0200B8E64D /* libz.dylib */; };
962031FD1735831C00C43080 /* NSXMLElement+XEP_0203.m in Sources */ = {isa = PBXBuildFile; fileRef = 962031FC1735831C00C43080 /* NSXMLElement+XEP_0203.m */; };
DC19DDA1170B4138000D2178 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DC19DDA0170B4138000D2178 /* Default-568h@2x.png */; };
DC1CF89B1361F28000E71363 /* NSString+DDXML.m in Sources */ = {isa = PBXBuildFile; fileRef = DC1CF8901361F28000E71363 /* NSString+DDXML.m */; };
@@ -196,6 +199,12 @@
28C286E00D94DF7D0034E888 /* RootViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = RootViewController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
28F335F01007B36200424DE2 /* RootViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = RootViewController.xib; sourceTree = "<group>"; };
29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ 4392EDFD17DB7C8500B8E64D /* XMPPCompression.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XMPPCompression.h; sourceTree = "<group>"; };
+ 4392EDFE17DB7C8500B8E64D /* XMPPCompression.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XMPPCompression.m; sourceTree = "<group>"; };
+ 4392EE0117DB7CA200B8E64D /* XMPPFeature.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XMPPFeature.h; sourceTree = "<group>"; };
+ 4392EE0217DB7CA200B8E64D /* XMPPFeature.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XMPPFeature.m; sourceTree = "<group>"; };
+ 4392EE0417DB7D0200B8E64D /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
+ 4392EE0617DB7D1000B8E64D /* XMPPStreamInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XMPPStreamInternal.h; path = ../../Core/XMPPStreamInternal.h; sourceTree = "<group>"; };
8D1107310486CEB800E47090 /* iPhoneXMPP-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "iPhoneXMPP-Info.plist"; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = "<group>"; };
962031FB1735831C00C43080 /* NSXMLElement+XEP_0203.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSXMLElement+XEP_0203.h"; sourceTree = "<group>"; };
962031FC1735831C00C43080 /* NSXMLElement+XEP_0203.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSXMLElement+XEP_0203.m"; sourceTree = "<group>"; };
@@ -276,7 +285,7 @@
DC84BBD512440A8E0055A459 /* XMPPPresence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XMPPPresence.h; path = ../../Core/XMPPPresence.h; sourceTree = SOURCE_ROOT; };
DC84BBD612440A8E0055A459 /* XMPPPresence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XMPPPresence.m; path = ../../Core/XMPPPresence.m; sourceTree = SOURCE_ROOT; };
DC84BBD912440A8E0055A459 /* XMPPStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = XMPPStream.h; path = ../../Core/XMPPStream.h; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
- DC84BBDA12440A8E0055A459 /* XMPPStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; name = XMPPStream.m; path = ../../Core/XMPPStream.m; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
+ DC84BBDA12440A8E0055A459 /* XMPPStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; name = XMPPStream.m; path = ../../Core/XMPPStream.m; sourceTree = SOURCE_ROOT; };
DC84BC1712440C500055A459 /* libidn.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libidn.a; path = ../../Vendor/libidn/libidn.a; sourceTree = SOURCE_ROOT; };
DC90AC56147C28850022DF52 /* DDAbstractDatabaseLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DDAbstractDatabaseLogger.h; path = ../../Vendor/CocoaLumberjack/DDAbstractDatabaseLogger.h; sourceTree = "<group>"; };
DC90AC57147C28850022DF52 /* DDAbstractDatabaseLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DDAbstractDatabaseLogger.m; path = ../../Vendor/CocoaLumberjack/DDAbstractDatabaseLogger.m; sourceTree = "<group>"; };
@@ -392,6 +401,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 4392EE0517DB7D0200B8E64D /* libz.dylib in Frameworks */,
DC26A52D152A3E82004B71C0 /* Security.framework in Frameworks */,
070400B91347D09A0092A737 /* CoreLocation.framework in Frameworks */,
1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */,
@@ -427,6 +437,7 @@
DCB215B71715ED7000719845 /* XEP-0100 */,
070400831347CB520092A737 /* XEP-0115 */,
DCB215BB1715ED8700719845 /* XEP-0136 */,
+ 4392EDFC17DB7C8500B8E64D /* XEP-0138 */,
07AF16B6134812E20084D82A /* XEP-0153 */,
DCB215CC1715EDB100719845 /* XEP-0184 */,
DCB215D01715EDC500719845 /* XEP-0199 */,
@@ -557,6 +568,7 @@
29B97314FDCFA39411CA2CEA /* CustomTemplate */ = {
isa = PBXGroup;
children = (
+ 4392EE0417DB7D0200B8E64D /* libz.dylib */,
DC1F98001152CAF300138A8F /* XMPP */,
080E96DDFE201D6D7F000001 /* Classes */,
29B97315FDCFA39411CA2CEA /* Other Sources */,
@@ -606,6 +618,25 @@
name = Frameworks;
sourceTree = "<group>";
};
+ 4392EDFC17DB7C8500B8E64D /* XEP-0138 */ = {
+ isa = PBXGroup;
+ children = (
+ 4392EDFD17DB7C8500B8E64D /* XMPPCompression.h */,
+ 4392EDFE17DB7C8500B8E64D /* XMPPCompression.m */,
+ );
+ path = "XEP-0138";
+ sourceTree = "<group>";
+ };
+ 4392EE0017DB7CA200B8E64D /* Features */ = {
+ isa = PBXGroup;
+ children = (
+ 4392EE0117DB7CA200B8E64D /* XMPPFeature.h */,
+ 4392EE0217DB7CA200B8E64D /* XMPPFeature.m */,
+ );
+ name = Features;
+ path = ../../Features;
+ sourceTree = "<group>";
+ };
DC1CF88D1361F28000E71363 /* KissXML */ = {
isa = PBXGroup;
children = (
@@ -654,6 +685,7 @@
children = (
DCE11268140C5798007A2A46 /* XMPPFramework.h */,
DC30E6B8153E09D2001B9E6D /* Authentication */,
+ 4392EE0017DB7CA200B8E64D /* Features */,
DC1F98011152CAFD00138A8F /* Categories */,
DC1F98031152CB0C00138A8F /* Core */,
070400471347CB510092A737 /* Extensions */,
@@ -704,6 +736,7 @@
DC84BBD312440A8E0055A459 /* XMPPParser.h */,
DC84BBD412440A8E0055A459 /* XMPPParser.m */,
DC84BBD912440A8E0055A459 /* XMPPStream.h */,
+ 4392EE0617DB7D1000B8E64D /* XMPPStreamInternal.h */,
DC84BBDA12440A8E0055A459 /* XMPPStream.m */,
DC84BBCD12440A8E0055A459 /* XMPPJID.h */,
DC84BBCE12440A8E0055A459 /* XMPPJID.m */,
@@ -1303,6 +1336,8 @@
DC319A2217289FDA00AD8EF8 /* XEP_0223.m in Sources */,
DC319A2517289FF900AD8EF8 /* XMPPIQ+XEP_0060.m in Sources */,
962031FD1735831C00C43080 /* NSXMLElement+XEP_0203.m in Sources */,
+ 4392EDFF17DB7C8500B8E64D /* XMPPCompression.m in Sources */,
+ 4392EE0317DB7CA200B8E64D /* XMPPFeature.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Something went wrong with that request. Please try again.