diff --git a/.gitignore b/.gitignore index 89c499e..5c767be 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,5 @@ profile *.moved-aside DerivedData .idea/ + +Pods/ diff --git a/Podfile b/Podfile new file mode 100644 index 0000000..20bbc65 --- /dev/null +++ b/Podfile @@ -0,0 +1,8 @@ +source 'https://github.com/CocoaPods/Specs.git' + +platform :ios, '7.0' + +inhibit_all_warnings! + +pod 'socket.IO', '0.5.2' +pod 'JSQMessagesViewController', '7.1.0' diff --git a/Podfile.lock b/Podfile.lock new file mode 100644 index 0000000..221a70c --- /dev/null +++ b/Podfile.lock @@ -0,0 +1,19 @@ +PODS: + - JSQMessagesViewController (7.1.0): + - JSQSystemSoundPlayer (~> 2.0.1) + - JSQSystemSoundPlayer (2.0.1) + - socket.IO (0.5.2): + - SocketRocket (~> 0.2) + - SocketRocket (0.4) + +DEPENDENCIES: + - JSQMessagesViewController (= 7.1.0) + - socket.IO (= 0.5.2) + +SPEC CHECKSUMS: + JSQMessagesViewController: ca11f86fa68ca70835f05e169df9244147c1dc40 + JSQSystemSoundPlayer: c5850e77a4363ffd374cd851154b9af93264ed8d + socket.IO: eaaaea33481d8a81d8f78e331f0c72ba44758036 + SocketRocket: 01443933b7bc362621325f4607b80b4286a99707 + +COCOAPODS: 0.37.2 diff --git a/PomeloChat/Pomelo/Pomelo.h b/Pomelo/Pomelo.h similarity index 100% rename from PomeloChat/Pomelo/Pomelo.h rename to Pomelo/Pomelo.h diff --git a/PomeloChat/Pomelo/Pomelo.m b/Pomelo/Pomelo.m similarity index 100% rename from PomeloChat/Pomelo/Pomelo.m rename to Pomelo/Pomelo.m diff --git a/PomeloChat/Pomelo/PomeloProtocol.h b/Pomelo/PomeloProtocol.h similarity index 100% rename from PomeloChat/Pomelo/PomeloProtocol.h rename to Pomelo/PomeloProtocol.h diff --git a/PomeloChat/Pomelo/PomeloProtocol.m b/Pomelo/PomeloProtocol.m similarity index 75% rename from PomeloChat/Pomelo/PomeloProtocol.m rename to Pomelo/PomeloProtocol.m index 2d0b9d1..fc6879d 100644 --- a/PomeloChat/Pomelo/PomeloProtocol.m +++ b/Pomelo/PomeloProtocol.m @@ -20,11 +20,11 @@ + (NSString *)encodeWithId:(NSInteger)id andRoute:(NSString *)route andBody:(NSS } NSString *msg = [NSString stringWithFormat:@"%C%C%C%C%C%@%@", - (id >> 24) & 0xFF, - (id >> 16) & 0xFF, - (id >> 8) & 0xFF, - id & 0xFF, - [route length], + (unichar)((id >> 24) & 0xFF), + (unichar)((id >> 16) & 0xFF), + (unichar)((id >> 8) & 0xFF), + (unichar)(id & 0xFF), + (unichar)[route length], route, body]; diff --git a/PomeloChat.xcodeproj/project.pbxproj b/PomeloChat.xcodeproj/project.pbxproj index c43c0af..c736d4a 100644 --- a/PomeloChat.xcodeproj/project.pbxproj +++ b/PomeloChat.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 43503C869DD62A3C04146D0B /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A9ADE8201222BEED3810122 /* libPods.a */; }; B20B256D16773678005F5A82 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B20B256C16773678005F5A82 /* UIKit.framework */; }; B20B256F16773678005F5A82 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B20B256E16773678005F5A82 /* Foundation.framework */; }; B20B257116773678005F5A82 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B20B257016773678005F5A82 /* CoreGraphics.framework */; }; @@ -16,27 +17,21 @@ B20B257F16773678005F5A82 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = B20B257E16773678005F5A82 /* Default.png */; }; B20B258116773678005F5A82 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B20B258016773678005F5A82 /* Default@2x.png */; }; B20B258316773678005F5A82 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B20B258216773678005F5A82 /* Default-568h@2x.png */; }; - B20B25AE16773A9B005F5A82 /* base64.c in Sources */ = {isa = PBXBuildFile; fileRef = B20B25A716773A9B005F5A82 /* base64.c */; }; - B20B25AF16773A9B005F5A82 /* NSData+SRB64Additions.m in Sources */ = {isa = PBXBuildFile; fileRef = B20B25AA16773A9B005F5A82 /* NSData+SRB64Additions.m */; }; - B20B25B016773A9B005F5A82 /* SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = B20B25AD16773A9B005F5A82 /* SRWebSocket.m */; }; B20B25BE167745F0005F5A82 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B20B25BD167745F0005F5A82 /* libicucore.dylib */; }; B20B25C016774600005F5A82 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B20B25BF16774600005F5A82 /* CFNetwork.framework */; }; B20B25C216774608005F5A82 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B20B25C116774607005F5A82 /* Security.framework */; }; - B245D833168E876400879092 /* Pomelo.m in Sources */ = {isa = PBXBuildFile; fileRef = B245D82F168E871300879092 /* Pomelo.m */; }; - B245D835168E876400879092 /* PomeloProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = B245D831168E871300879092 /* PomeloProtocol.m */; }; - B26482EA16898055006D39A1 /* SocketIO.m in Sources */ = {isa = PBXBuildFile; fileRef = B26482DF168949FC006D39A1 /* SocketIO.m */; }; - B26482EC16898055006D39A1 /* SocketIOJSONSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = B26482E1168949FC006D39A1 /* SocketIOJSONSerialization.m */; }; - B26482EE16898055006D39A1 /* SocketIOPacket.m in Sources */ = {isa = PBXBuildFile; fileRef = B26482E3168949FC006D39A1 /* SocketIOPacket.m */; }; - B26482F116898055006D39A1 /* SocketIOTransportWebsocket.m in Sources */ = {isa = PBXBuildFile; fileRef = B26482E6168949FC006D39A1 /* SocketIOTransportWebsocket.m */; }; - B26482F316898055006D39A1 /* SocketIOTransportXHR.m in Sources */ = {isa = PBXBuildFile; fileRef = B26482E8168949FC006D39A1 /* SocketIOTransportXHR.m */; }; B280B8BD168C4806002B48AF /* LoginViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B280B8BB168C4806002B48AF /* LoginViewController.m */; }; B280B8BE168C4806002B48AF /* LoginViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B280B8BC168C4806002B48AF /* LoginViewController.xib */; }; B280B8C1168C506B002B48AF /* ContactsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B280B8C0168C506B002B48AF /* ContactsViewController.m */; }; - B280B8C5168C5248002B48AF /* ChatViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B280B8C3168C5248002B48AF /* ChatViewController.m */; }; - B280B8C6168C5248002B48AF /* ChatViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B280B8C4168C5248002B48AF /* ChatViewController.xib */; }; + E05134AA1B5BA05A00297475 /* Pomelo.m in Sources */ = {isa = PBXBuildFile; fileRef = E05134A71B5BA05A00297475 /* Pomelo.m */; }; + E05134AB1B5BA05A00297475 /* PomeloProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = E05134A91B5BA05A00297475 /* PomeloProtocol.m */; }; + E0CA4CA81A84D585001F2F30 /* DemoMessagesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E0CA4CA51A84D585001F2F30 /* DemoMessagesViewController.m */; }; + E0CA4CA91A84D585001F2F30 /* DemoModelData.m in Sources */ = {isa = PBXBuildFile; fileRef = E0CA4CA71A84D585001F2F30 /* DemoModelData.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 8A9ADE8201222BEED3810122 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; + A84DDB00CA54D58C476A8963 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; B20B256816773678005F5A82 /* PomeloChat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PomeloChat.app; sourceTree = BUILT_PRODUCTS_DIR; }; B20B256C16773678005F5A82 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; B20B256E16773678005F5A82 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; @@ -50,39 +45,23 @@ B20B257E16773678005F5A82 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; B20B258016773678005F5A82 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; B20B258216773678005F5A82 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; - B20B25A716773A9B005F5A82 /* base64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = base64.c; path = SocketRocket/base64.c; sourceTree = ""; }; - B20B25A816773A9B005F5A82 /* base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = base64.h; path = SocketRocket/base64.h; sourceTree = ""; }; - B20B25A916773A9B005F5A82 /* NSData+SRB64Additions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSData+SRB64Additions.h"; path = "SocketRocket/NSData+SRB64Additions.h"; sourceTree = ""; }; - B20B25AA16773A9B005F5A82 /* NSData+SRB64Additions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSData+SRB64Additions.m"; path = "SocketRocket/NSData+SRB64Additions.m"; sourceTree = ""; }; - B20B25AB16773A9B005F5A82 /* SocketRocket-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "SocketRocket-Prefix.pch"; path = "SocketRocket/SocketRocket-Prefix.pch"; sourceTree = ""; }; - B20B25AC16773A9B005F5A82 /* SRWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SRWebSocket.h; path = SocketRocket/SRWebSocket.h; sourceTree = ""; }; - B20B25AD16773A9B005F5A82 /* SRWebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SRWebSocket.m; path = SocketRocket/SRWebSocket.m; sourceTree = ""; }; B20B25BD167745F0005F5A82 /* libicucore.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libicucore.dylib; path = usr/lib/libicucore.dylib; sourceTree = SDKROOT; }; B20B25BF16774600005F5A82 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; }; B20B25C116774607005F5A82 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; - B245D82E168E871300879092 /* Pomelo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Pomelo.h; path = Pomelo/Pomelo.h; sourceTree = ""; }; - B245D82F168E871300879092 /* Pomelo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = Pomelo.m; path = Pomelo/Pomelo.m; sourceTree = ""; }; - B245D830168E871300879092 /* PomeloProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PomeloProtocol.h; path = Pomelo/PomeloProtocol.h; sourceTree = ""; }; - B245D831168E871300879092 /* PomeloProtocol.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = PomeloProtocol.m; path = Pomelo/PomeloProtocol.m; sourceTree = ""; }; - B26482DE168949FC006D39A1 /* SocketIO.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SocketIO.h; path = SocketIO/SocketIO.h; sourceTree = ""; }; - B26482DF168949FC006D39A1 /* SocketIO.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SocketIO.m; path = SocketIO/SocketIO.m; sourceTree = ""; }; - B26482E0168949FC006D39A1 /* SocketIOJSONSerialization.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SocketIOJSONSerialization.h; path = SocketIO/SocketIOJSONSerialization.h; sourceTree = ""; }; - B26482E1168949FC006D39A1 /* SocketIOJSONSerialization.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SocketIOJSONSerialization.m; path = SocketIO/SocketIOJSONSerialization.m; sourceTree = ""; }; - B26482E2168949FC006D39A1 /* SocketIOPacket.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SocketIOPacket.h; path = SocketIO/SocketIOPacket.h; sourceTree = ""; }; - B26482E3168949FC006D39A1 /* SocketIOPacket.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SocketIOPacket.m; path = SocketIO/SocketIOPacket.m; sourceTree = ""; }; - B26482E4168949FC006D39A1 /* SocketIOTransport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SocketIOTransport.h; path = SocketIO/SocketIOTransport.h; sourceTree = ""; }; - B26482E5168949FC006D39A1 /* SocketIOTransportWebsocket.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SocketIOTransportWebsocket.h; path = SocketIO/SocketIOTransportWebsocket.h; sourceTree = ""; }; - B26482E6168949FC006D39A1 /* SocketIOTransportWebsocket.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SocketIOTransportWebsocket.m; path = SocketIO/SocketIOTransportWebsocket.m; sourceTree = ""; }; - B26482E7168949FC006D39A1 /* SocketIOTransportXHR.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SocketIOTransportXHR.h; path = SocketIO/SocketIOTransportXHR.h; sourceTree = ""; }; - B26482E8168949FC006D39A1 /* SocketIOTransportXHR.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SocketIOTransportXHR.m; path = SocketIO/SocketIOTransportXHR.m; sourceTree = ""; }; B280B8BA168C4806002B48AF /* LoginViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LoginViewController.h; sourceTree = ""; }; B280B8BB168C4806002B48AF /* LoginViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LoginViewController.m; sourceTree = ""; }; B280B8BC168C4806002B48AF /* LoginViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LoginViewController.xib; sourceTree = ""; }; B280B8BF168C506B002B48AF /* ContactsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContactsViewController.h; sourceTree = ""; }; B280B8C0168C506B002B48AF /* ContactsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ContactsViewController.m; sourceTree = ""; }; - B280B8C2168C5248002B48AF /* ChatViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ChatViewController.h; sourceTree = ""; }; - B280B8C3168C5248002B48AF /* ChatViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ChatViewController.m; sourceTree = ""; }; - B280B8C4168C5248002B48AF /* ChatViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ChatViewController.xib; sourceTree = ""; }; + E05134A61B5BA05A00297475 /* Pomelo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Pomelo.h; sourceTree = ""; }; + E05134A71B5BA05A00297475 /* Pomelo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Pomelo.m; sourceTree = ""; }; + E05134A81B5BA05A00297475 /* PomeloProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PomeloProtocol.h; sourceTree = ""; }; + E05134A91B5BA05A00297475 /* PomeloProtocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PomeloProtocol.m; sourceTree = ""; }; + E0CA4CA41A84D585001F2F30 /* DemoMessagesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoMessagesViewController.h; sourceTree = ""; }; + E0CA4CA51A84D585001F2F30 /* DemoMessagesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoMessagesViewController.m; sourceTree = ""; }; + E0CA4CA61A84D585001F2F30 /* DemoModelData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoModelData.h; sourceTree = ""; }; + E0CA4CA71A84D585001F2F30 /* DemoModelData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoModelData.m; sourceTree = ""; }; + EAB78C3BA8F4CD9EED803987 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -96,6 +75,7 @@ B20B256D16773678005F5A82 /* UIKit.framework in Frameworks */, B20B256F16773678005F5A82 /* Foundation.framework in Frameworks */, B20B257116773678005F5A82 /* CoreGraphics.framework in Frameworks */, + 43503C869DD62A3C04146D0B /* libPods.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -105,9 +85,11 @@ B20B255D16773678005F5A82 = { isa = PBXGroup; children = ( + E05134A51B5BA05A00297475 /* Pomelo */, B20B257216773678005F5A82 /* PomeloChat */, B20B256B16773678005F5A82 /* Frameworks */, B20B256916773678005F5A82 /* Products */, + D27376296EA092B6637BDA71 /* Pods */, ); sourceTree = ""; }; @@ -128,6 +110,7 @@ B20B256C16773678005F5A82 /* UIKit.framework */, B20B256E16773678005F5A82 /* Foundation.framework */, B20B257016773678005F5A82 /* CoreGraphics.framework */, + 8A9ADE8201222BEED3810122 /* libPods.a */, ); name = Frameworks; sourceTree = ""; @@ -135,9 +118,6 @@ B20B257216773678005F5A82 /* PomeloChat */ = { isa = PBXGroup; children = ( - B2933D761682BA1500A15757 /* Pomelo */, - B20B25901677369F005F5A82 /* SocketRocket */, - B20B258F1677368A005F5A82 /* SocketIO */, B20B257B16773678005F5A82 /* AppDelegate.h */, B20B257C16773678005F5A82 /* AppDelegate.m */, B20B257316773678005F5A82 /* Supporting Files */, @@ -146,9 +126,10 @@ B280B8BC168C4806002B48AF /* LoginViewController.xib */, B280B8BF168C506B002B48AF /* ContactsViewController.h */, B280B8C0168C506B002B48AF /* ContactsViewController.m */, - B280B8C2168C5248002B48AF /* ChatViewController.h */, - B280B8C3168C5248002B48AF /* ChatViewController.m */, - B280B8C4168C5248002B48AF /* ChatViewController.xib */, + E0CA4CA41A84D585001F2F30 /* DemoMessagesViewController.h */, + E0CA4CA51A84D585001F2F30 /* DemoMessagesViewController.m */, + E0CA4CA61A84D585001F2F30 /* DemoModelData.h */, + E0CA4CA71A84D585001F2F30 /* DemoModelData.m */, ); path = PomeloChat; sourceTree = ""; @@ -167,47 +148,24 @@ name = "Supporting Files"; sourceTree = ""; }; - B20B258F1677368A005F5A82 /* SocketIO */ = { + D27376296EA092B6637BDA71 /* Pods */ = { isa = PBXGroup; children = ( - B26482DE168949FC006D39A1 /* SocketIO.h */, - B26482DF168949FC006D39A1 /* SocketIO.m */, - B26482E0168949FC006D39A1 /* SocketIOJSONSerialization.h */, - B26482E1168949FC006D39A1 /* SocketIOJSONSerialization.m */, - B26482E2168949FC006D39A1 /* SocketIOPacket.h */, - B26482E3168949FC006D39A1 /* SocketIOPacket.m */, - B26482E4168949FC006D39A1 /* SocketIOTransport.h */, - B26482E5168949FC006D39A1 /* SocketIOTransportWebsocket.h */, - B26482E6168949FC006D39A1 /* SocketIOTransportWebsocket.m */, - B26482E7168949FC006D39A1 /* SocketIOTransportXHR.h */, - B26482E8168949FC006D39A1 /* SocketIOTransportXHR.m */, + EAB78C3BA8F4CD9EED803987 /* Pods.debug.xcconfig */, + A84DDB00CA54D58C476A8963 /* Pods.release.xcconfig */, ); - name = SocketIO; + name = Pods; sourceTree = ""; }; - B20B25901677369F005F5A82 /* SocketRocket */ = { + E05134A51B5BA05A00297475 /* Pomelo */ = { isa = PBXGroup; children = ( - B20B25A716773A9B005F5A82 /* base64.c */, - B20B25A816773A9B005F5A82 /* base64.h */, - B20B25A916773A9B005F5A82 /* NSData+SRB64Additions.h */, - B20B25AA16773A9B005F5A82 /* NSData+SRB64Additions.m */, - B20B25AB16773A9B005F5A82 /* SocketRocket-Prefix.pch */, - B20B25AC16773A9B005F5A82 /* SRWebSocket.h */, - B20B25AD16773A9B005F5A82 /* SRWebSocket.m */, + E05134A61B5BA05A00297475 /* Pomelo.h */, + E05134A71B5BA05A00297475 /* Pomelo.m */, + E05134A81B5BA05A00297475 /* PomeloProtocol.h */, + E05134A91B5BA05A00297475 /* PomeloProtocol.m */, ); - name = SocketRocket; - sourceTree = ""; - }; - B2933D761682BA1500A15757 /* Pomelo */ = { - isa = PBXGroup; - children = ( - B245D82E168E871300879092 /* Pomelo.h */, - B245D82F168E871300879092 /* Pomelo.m */, - B245D830168E871300879092 /* PomeloProtocol.h */, - B245D831168E871300879092 /* PomeloProtocol.m */, - ); - name = Pomelo; + path = Pomelo; sourceTree = ""; }; /* End PBXGroup section */ @@ -217,9 +175,11 @@ isa = PBXNativeTarget; buildConfigurationList = B20B258C16773678005F5A82 /* Build configuration list for PBXNativeTarget "PomeloChat" */; buildPhases = ( + 3B7964204F99D1081F7F54EA /* Check Pods Manifest.lock */, B20B256416773678005F5A82 /* Sources */, B20B256516773678005F5A82 /* Frameworks */, B20B256616773678005F5A82 /* Resources */, + 5212E0B2429F1CF02F7C5F6F /* Copy Pods Resources */, ); buildRules = ( ); @@ -266,32 +226,57 @@ B20B258116773678005F5A82 /* Default@2x.png in Resources */, B20B258316773678005F5A82 /* Default-568h@2x.png in Resources */, B280B8BE168C4806002B48AF /* LoginViewController.xib in Resources */, - B280B8C6168C5248002B48AF /* ChatViewController.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 3B7964204F99D1081F7F54EA /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + 5212E0B2429F1CF02F7C5F6F /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ B20B256416773678005F5A82 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - B245D833168E876400879092 /* Pomelo.m in Sources */, - B245D835168E876400879092 /* PomeloProtocol.m in Sources */, - B26482EA16898055006D39A1 /* SocketIO.m in Sources */, - B26482EC16898055006D39A1 /* SocketIOJSONSerialization.m in Sources */, - B26482EE16898055006D39A1 /* SocketIOPacket.m in Sources */, - B26482F116898055006D39A1 /* SocketIOTransportWebsocket.m in Sources */, - B26482F316898055006D39A1 /* SocketIOTransportXHR.m in Sources */, B20B257916773678005F5A82 /* main.m in Sources */, + E05134AB1B5BA05A00297475 /* PomeloProtocol.m in Sources */, B20B257D16773678005F5A82 /* AppDelegate.m in Sources */, - B20B25AE16773A9B005F5A82 /* base64.c in Sources */, - B20B25AF16773A9B005F5A82 /* NSData+SRB64Additions.m in Sources */, - B20B25B016773A9B005F5A82 /* SRWebSocket.m in Sources */, B280B8BD168C4806002B48AF /* LoginViewController.m in Sources */, B280B8C1168C506B002B48AF /* ContactsViewController.m in Sources */, - B280B8C5168C5248002B48AF /* ChatViewController.m in Sources */, + E0CA4CA81A84D585001F2F30 /* DemoMessagesViewController.m in Sources */, + E0CA4CA91A84D585001F2F30 /* DemoModelData.m in Sources */, + E05134AA1B5BA05A00297475 /* Pomelo.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -331,7 +316,7 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 5.1; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; }; @@ -352,7 +337,7 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 5.1; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; @@ -361,11 +346,12 @@ }; B20B258D16773678005F5A82 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = EAB78C3BA8F4CD9EED803987 /* Pods.debug.xcconfig */; buildSettings = { GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "PomeloChat/PomeloChat-Prefix.pch"; INFOPLIST_FILE = "PomeloChat/PomeloChat-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 5.1; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; @@ -373,11 +359,12 @@ }; B20B258E16773678005F5A82 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = A84DDB00CA54D58C476A8963 /* Pods.release.xcconfig */; buildSettings = { GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "PomeloChat/PomeloChat-Prefix.pch"; INFOPLIST_FILE = "PomeloChat/PomeloChat-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 5.1; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; diff --git a/PomeloChat/ChatViewController.h b/PomeloChat/ChatViewController.h deleted file mode 100644 index 03e8f3c..0000000 --- a/PomeloChat/ChatViewController.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// ChatViewController.h -// PomeloChat -// -// Created by Johnny on 12-12-27. -// Copyright (c) 2012年 netease pomelo team. All rights reserved. -// - -#import -#import "Pomelo.h" - -@interface ChatViewController : UIViewController -@property (weak, nonatomic) IBOutlet UITextView *chatTextField; -@property (weak, nonatomic) IBOutlet UITextField *inputTextField; -@property (weak, nonatomic) NSString *target; - -@property (weak, nonatomic) Pomelo *pomelo; - -@end diff --git a/PomeloChat/ChatViewController.m b/PomeloChat/ChatViewController.m deleted file mode 100644 index b3833c5..0000000 --- a/PomeloChat/ChatViewController.m +++ /dev/null @@ -1,99 +0,0 @@ -// -// ChatViewController.m -// PomeloChat -// -// Created by Johnny on 12-12-27. -// Copyright (c) 2012年 netease pomelo team. All rights reserved. -// - -#import "ChatViewController.h" - -@interface ChatViewController () -@property (strong, nonatomic) NSMutableString *chatStr; -- (void)initEvents; -@end - -@implementation ChatViewController - -@synthesize inputTextField; -@synthesize chatTextField; -@synthesize chatStr; -@synthesize pomelo; - -- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil -{ - self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; - if (self) { - // Custom initialization - self.chatStr = [[NSMutableString alloc] initWithCapacity:10]; - } - return self; -} - -- (IBAction)send:(id)sender -{ - NSDictionary *data = [NSDictionary dictionaryWithObjectsAndKeys: - inputTextField.text, @"content", - self.target, @"target", - nil]; - - if ([self.target isEqualToString:@"*"]) { - [pomelo notifyWithRoute:@"chat.chatHandler.send" andParams:data]; - } else { - [pomelo requestWithRoute:@"chat.chatHandler.send" andParams:data andCallback:^(NSDictionary *result){ - [chatStr appendFormat:@"you says to %@: %@\n", self.target, [data objectForKey:@"content"]]; - [self updateChat]; - }]; - } - inputTextField.text = @""; - [inputTextField resignFirstResponder]; -} - -- (void)setTitle:(NSString *)title -{ - [super setTitle:title]; - - if ([self.title isEqualToString:@"All"]) { - self.target = @"*"; - } else { - self.target = self.title; - } -} - -- (void)updateChat -{ - self.chatTextField.text = chatStr; -} - -- (void)initEvents -{ - [pomelo onRoute:@"onChat" withCallback:^(NSDictionary *data){ - NSLog(@"onChat------"); - NSString *target = [[data objectForKey:@"target"] isEqualToString:@"*"] ? @"" : @" to you"; - [chatStr appendFormat:@"%@ says%@: %@\n", [data objectForKey:@"from"], target, [data objectForKey:@"msg"] ]; - [self updateChat]; - }]; -} - -- (void)viewDidLoad -{ - [super viewDidLoad]; - [self initEvents]; - // Do any additional setup after loading the view from its nib. -} - -- (void)didReceiveMemoryWarning -{ - [super didReceiveMemoryWarning]; - // Dispose of any resources that can be recreated. -} - -- (void)viewDidUnload { - [self setChatTextField:nil]; - [self setInputTextField:nil]; - self.chatStr = nil; - self.target = nil; - [self.pomelo offRoute:@"onChat"]; - [super viewDidUnload]; -} -@end diff --git a/PomeloChat/ChatViewController.xib b/PomeloChat/ChatViewController.xib deleted file mode 100644 index 08278e4..0000000 --- a/PomeloChat/ChatViewController.xib +++ /dev/null @@ -1,318 +0,0 @@ - - - - 1296 - 12C3006 - 2844 - 1187.34 - 625.00 - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 1930 - - - IBProxyObject - IBUIButton - IBUITextField - IBUITextView - IBUIView - - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - PluginDependencyRecalculationVersion - - - - - IBFilesOwner - IBCocoaTouchFramework - - - IBFirstResponder - IBCocoaTouchFramework - - - - 274 - - - - 292 - {{13, 20}, {219, 30}} - - - - _NS:9 - NO - YES - IBCocoaTouchFramework - 0 - - 3 - say something ... - - 3 - MAA - - 2 - - - YES - 17 - - IBCocoaTouchFramework - - - 1 - 14 - - - Helvetica - 14 - 16 - - - - - 292 - {{240, 20}, {68, 31}} - - - - _NS:9 - NO - IBCocoaTouchFramework - 0 - 0 - 1 - Send - - 3 - MQA - - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - - 3 - MC41AA - - - 2 - 15 - - - Helvetica-Bold - 15 - 16 - - - - - 292 - {{13, 58}, {295, 354}} - - - _NS:9 - - 1 - MSAxIDEAA - - YES - YES - IBCocoaTouchFramework - NO - - - 2 - IBCocoaTouchFramework - - - - - - {{0, 20}, {320, 460}} - - - - - 3 - MQA - - - - - IBUIScreenMetrics - - YES - - - - - - {320, 480} - {480, 320} - - - IBCocoaTouchFramework - Retina 3.5 Full Screen - 0 - - IBCocoaTouchFramework - - - - - - - view - - - - 3 - - - - chatTextField - - - - 77 - - - - inputTextField - - - - 78 - - - - send: - - - 7 - - 79 - - - - - - 0 - - - - - - 1 - - - - - - - - - - -1 - - - File's Owner - - - -2 - - - - - 4 - - - - - - 9 - - - - - - 29 - - - - - - - ChatViewController - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - UIResponder - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - - - - 79 - - - - - ChatViewController - UIViewController - - send: - id - - - send: - - send: - id - - - - UITextView - UITextField - - - - chatTextField - UITextView - - - inputTextField - UITextField - - - - IBProjectSource - ./Classes/ChatViewController.h - - - - - 0 - IBCocoaTouchFramework - - com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS - - - - com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 - - - YES - 3 - 1930 - - diff --git a/PomeloChat/ContactsViewController.m b/PomeloChat/ContactsViewController.m index 0ef48cc..a11c581 100644 --- a/PomeloChat/ContactsViewController.m +++ b/PomeloChat/ContactsViewController.m @@ -7,11 +7,11 @@ // #import "ContactsViewController.h" -#import "ChatViewController.h" +#import "DemoMessagesViewController.h" @interface ContactsViewController () -@property (strong, nonatomic) ChatViewController *chatViewController; +@property (strong, nonatomic) DemoMessagesViewController *chatViewController; - (void)initEvents; @end @@ -41,7 +41,7 @@ - (void)viewDidLoad self.title = @"Contacts"; self.navigationItem.hidesBackButton = YES; - self.chatViewController = [[ChatViewController alloc] initWithNibName:@"ChatViewController" bundle:nil]; + self.chatViewController = [DemoMessagesViewController new]; self.chatViewController.pomelo = pomelo; [self initEvents]; } @@ -50,16 +50,19 @@ - (void)initEvents { [pomelo onRoute:@"onAdd" withCallback:^(NSDictionary *data){ NSLog(@"user add -----"); - NSString *name = [data objectForKey:@"user"]; + NSDictionary *body = data[@"body"]; + NSString *name = [body objectForKey:@"user"]; [self.tableView beginUpdates]; [contactList addObject:name]; NSArray *paths = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:[contactList count]-1 inSection:0]]; [self.tableView insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationTop]; [self.tableView endUpdates]; + [self.tableView reloadData]; }]; [pomelo onRoute:@"onLeave" withCallback:^(NSDictionary *data){ NSLog(@"user leave ----"); - NSString *name = [data objectForKey:@"user"]; + NSDictionary *body = data[@"body"]; + NSString *name = [body objectForKey:@"user"]; if ([contactList containsObject:name]) { NSUInteger index = [contactList indexOfObject:name]; [contactList removeObjectAtIndex:index]; @@ -103,45 +106,6 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N return cell; } -/* -// Override to support conditional editing of the table view. -- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath -{ - // Return NO if you do not want the specified item to be editable. - return YES; -} -*/ - -/* -// Override to support editing the table view. -- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath -{ - if (editingStyle == UITableViewCellEditingStyleDelete) { - // Delete the row from the data source - [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; - } - else if (editingStyle == UITableViewCellEditingStyleInsert) { - // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view - } -} -*/ - -/* -// Override to support rearranging the table view. -- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath -{ -} -*/ - -/* -// Override to support conditional rearranging of the table view. -- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath -{ - // Return NO if you do not want the item to be re-orderable. - return YES; -} -*/ - #pragma mark - Table view delegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath diff --git a/PomeloChat/DemoMessagesViewController.h b/PomeloChat/DemoMessagesViewController.h new file mode 100644 index 0000000..d208fee --- /dev/null +++ b/PomeloChat/DemoMessagesViewController.h @@ -0,0 +1,35 @@ +// +// Created by Jesse Squires +// http://www.jessesquires.com +// +// +// Documentation +// http://cocoadocs.org/docsets/JSQMessagesViewController +// +// +// GitHub +// https://github.com/jessesquires/JSQMessagesViewController +// +// +// License +// Copyright (c) 2014 Jesse Squires +// Released under an MIT license: http://opensource.org/licenses/MIT +// + + +// Import all the things +#import "JSQMessages.h" + +#import "DemoModelData.h" +#import "Pomelo.h" + + +@class DemoMessagesViewController; + +@interface DemoMessagesViewController : JSQMessagesViewController + +@property (strong, nonatomic) DemoModelData *demoData; + +@property (weak, nonatomic) Pomelo *pomelo; + +@end diff --git a/PomeloChat/DemoMessagesViewController.m b/PomeloChat/DemoMessagesViewController.m new file mode 100644 index 0000000..16abfea --- /dev/null +++ b/PomeloChat/DemoMessagesViewController.m @@ -0,0 +1,257 @@ +// +// Created by Jesse Squires +// http://www.jessesquires.com +// +// +// Documentation +// http://cocoadocs.org/docsets/JSQMessagesViewController +// +// +// GitHub +// https://github.com/jessesquires/JSQMessagesViewController +// +// +// License +// Copyright (c) 2014 Jesse Squires +// Released under an MIT license: http://opensource.org/licenses/MIT +// + +#import "DemoMessagesViewController.h" + +@interface DemoMessagesViewController () + +@property (strong, nonatomic) NSString *target; + +@end + + +@implementation DemoMessagesViewController + +#pragma mark - View lifecycle + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.senderId = [[NSUserDefaults standardUserDefaults] objectForKey:@"login_user"]; + self.senderDisplayName = self.senderId; + + self.demoData = [[DemoModelData alloc] init]; + + self.collectionView.collectionViewLayout.incomingAvatarViewSize = CGSizeZero; + self.collectionView.collectionViewLayout.outgoingAvatarViewSize = CGSizeZero; + + self.inputToolbar.contentView.leftBarButtonItem = nil; +} + +- (void)viewWillAppear:(BOOL)animated +{ + [super viewWillAppear:animated]; + + [self.inputToolbar.contentView.textView resignFirstResponder]; + + [self initEvents]; +} + +- (void)viewWillDisappear:(BOOL)animated +{ + [self.pomelo offRoute:@"onChat"]; + + [self.demoData.messages removeAllObjects]; + [self.collectionView reloadData]; + + [super viewWillDisappear:animated]; +} + +#pragma mark - Setters + +- (void)setTitle:(NSString *)title +{ + [super setTitle:title]; + + if ([self.title isEqualToString:@"All"]) { + self.target = @"*"; + } else { + self.target = self.title; + } +} + +#pragma mark - Events + +- (void)initEvents +{ + __weak typeof(self)weakSelf = self; + [self.pomelo onRoute:@"onChat" withCallback:^(NSDictionary *data) { + __strong typeof(weakSelf)strongSelf = weakSelf; + + NSLog(@"onChat------"); + NSDictionary *body = data[@"body"]; + NSString *from = body[@"from"]; + + if ([self.target isEqualToString:@"*"]) { + if ([from isEqualToString:self.senderId]) { + return; + } + } else { + if (![from isEqualToString:self.target]) { + return; + } + } + + NSString *msg = body[@"msg"]; + + [JSQSystemSoundPlayer jsq_playMessageReceivedSound]; + + JSQMessage *message = [[JSQMessage alloc] initWithSenderId:from + senderDisplayName:from + date:[NSDate date] + text:msg]; + + [strongSelf.demoData.messages addObject:message]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [strongSelf finishReceivingMessageAnimated:YES]; + }); + }]; +} + +#pragma mark - JSQMessagesViewController method overrides + +- (void)didPressSendButton:(UIButton *)button + withMessageText:(NSString *)text + senderId:(NSString *)senderId + senderDisplayName:(NSString *)senderDisplayName + date:(NSDate *)date +{ + // update UI + [JSQSystemSoundPlayer jsq_playMessageSentSound]; + + JSQMessage *message = [[JSQMessage alloc] initWithSenderId:senderId + senderDisplayName:senderDisplayName + date:date + text:text]; + + [self.demoData.messages addObject:message]; + + [self finishSendingMessageAnimated:YES]; + + // send to network + NSDictionary *data = @{@"content":text, + @"target":self.target}; + + if ([self.target isEqualToString:@"*"]) { + [self.pomelo notifyWithRoute:@"chat.chatHandler.send" andParams:data]; + } else { + [self.pomelo requestWithRoute:@"chat.chatHandler.send" andParams:data andCallback:nil]; + } +} + +#pragma mark - JSQMessages CollectionView DataSource + +- (id)collectionView:(JSQMessagesCollectionView *)collectionView messageDataForItemAtIndexPath:(NSIndexPath *)indexPath +{ + return [self.demoData.messages objectAtIndex:indexPath.item]; +} + +- (id)collectionView:(JSQMessagesCollectionView *)collectionView messageBubbleImageDataForItemAtIndexPath:(NSIndexPath *)indexPath +{ + JSQMessage *message = [self.demoData.messages objectAtIndex:indexPath.item]; + + if ([message.senderId isEqualToString:self.senderId]) { + return self.demoData.outgoingBubbleImageData; + } + + return self.demoData.incomingBubbleImageData; +} + +- (id)collectionView:(JSQMessagesCollectionView *)collectionView avatarImageDataForItemAtIndexPath:(NSIndexPath *)indexPath +{ + return nil; +} + +- (NSAttributedString *)collectionView:(JSQMessagesCollectionView *)collectionView attributedTextForCellTopLabelAtIndexPath:(NSIndexPath *)indexPath +{ + if (indexPath.item % 3 == 0) { + JSQMessage *message = [self.demoData.messages objectAtIndex:indexPath.item]; + return [[JSQMessagesTimestampFormatter sharedFormatter] attributedTimestampForDate:message.date]; + } + + return nil; +} + +- (NSAttributedString *)collectionView:(JSQMessagesCollectionView *)collectionView attributedTextForMessageBubbleTopLabelAtIndexPath:(NSIndexPath *)indexPath +{ + JSQMessage *message = [self.demoData.messages objectAtIndex:indexPath.item]; + + if (indexPath.item - 1 > 0) { + JSQMessage *previousMessage = [self.demoData.messages objectAtIndex:indexPath.item - 1]; + if ([[previousMessage senderId] isEqualToString:message.senderId]) { + return nil; + } + } + + return [[NSAttributedString alloc] initWithString:message.senderDisplayName]; +} + + +#pragma mark - UICollectionView DataSource + +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section +{ + return [self.demoData.messages count]; +} + +- (UICollectionViewCell *)collectionView:(JSQMessagesCollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath +{ + JSQMessagesCollectionViewCell *cell = (JSQMessagesCollectionViewCell *)[super collectionView:collectionView cellForItemAtIndexPath:indexPath]; + + JSQMessage *msg = [self.demoData.messages objectAtIndex:indexPath.item]; + + if (!msg.isMediaMessage) { + + if ([msg.senderId isEqualToString:self.senderId]) { + cell.textView.textColor = [UIColor whiteColor]; + } + else { + cell.textView.textColor = [UIColor blackColor]; + } + + cell.textView.linkTextAttributes = @{ NSForegroundColorAttributeName : cell.textView.textColor, + NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle | NSUnderlinePatternSolid) }; + } + + return cell; +} + + + +#pragma mark - JSQMessages collection view flow layout delegate + +#pragma mark - Adjusting cell label heights + +- (CGFloat)collectionView:(JSQMessagesCollectionView *)collectionView + layout:(JSQMessagesCollectionViewFlowLayout *)collectionViewLayout heightForCellTopLabelAtIndexPath:(NSIndexPath *)indexPath +{ + if (indexPath.item % 3 == 0) { + return kJSQMessagesCollectionViewCellLabelHeightDefault; + } + + return 0.0f; +} + +- (CGFloat)collectionView:(JSQMessagesCollectionView *)collectionView + layout:(JSQMessagesCollectionViewFlowLayout *)collectionViewLayout heightForMessageBubbleTopLabelAtIndexPath:(NSIndexPath *)indexPath +{ + JSQMessage *currentMessage = [self.demoData.messages objectAtIndex:indexPath.item]; + + if (indexPath.item - 1 > 0) { + JSQMessage *previousMessage = [self.demoData.messages objectAtIndex:indexPath.item - 1]; + if ([[previousMessage senderId] isEqualToString:[currentMessage senderId]]) { + return 0.0f; + } + } + + return kJSQMessagesCollectionViewCellLabelHeightDefault; +} + +@end diff --git a/PomeloChat/DemoModelData.h b/PomeloChat/DemoModelData.h new file mode 100644 index 0000000..0c7dfe0 --- /dev/null +++ b/PomeloChat/DemoModelData.h @@ -0,0 +1,33 @@ +// +// Created by Jesse Squires +// http://www.jessesquires.com +// +// +// Documentation +// http://cocoadocs.org/docsets/JSQMessagesViewController +// +// +// GitHub +// https://github.com/jessesquires/JSQMessagesViewController +// +// +// License +// Copyright (c) 2014 Jesse Squires +// Released under an MIT license: http://opensource.org/licenses/MIT +// + +#import +#import + +#import "JSQMessages.h" + +@interface DemoModelData : NSObject + +@property (strong, nonatomic) NSMutableArray *messages; + +@property (strong, nonatomic) JSQMessagesBubbleImage *outgoingBubbleImageData; + +@property (strong, nonatomic) JSQMessagesBubbleImage *incomingBubbleImageData; + + +@end diff --git a/PomeloChat/DemoModelData.m b/PomeloChat/DemoModelData.m new file mode 100644 index 0000000..95d01df --- /dev/null +++ b/PomeloChat/DemoModelData.m @@ -0,0 +1,39 @@ +// +// Created by Jesse Squires +// http://www.jessesquires.com +// +// +// Documentation +// http://cocoadocs.org/docsets/JSQMessagesViewController +// +// +// GitHub +// https://github.com/jessesquires/JSQMessagesViewController +// +// +// License +// Copyright (c) 2014 Jesse Squires +// Released under an MIT license: http://opensource.org/licenses/MIT +// + +#import "DemoModelData.h" + + +@implementation DemoModelData + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.messages = [NSMutableArray new]; + + JSQMessagesBubbleImageFactory *bubbleFactory = [[JSQMessagesBubbleImageFactory alloc] init]; + + self.outgoingBubbleImageData = [bubbleFactory outgoingMessagesBubbleImageWithColor:[UIColor jsq_messageBubbleBlueColor]]; + self.incomingBubbleImageData = [bubbleFactory incomingMessagesBubbleImageWithColor:[UIColor jsq_messageBubbleLightGrayColor]]; + } + + return self; +} + +@end diff --git a/PomeloChat/LoginViewController.m b/PomeloChat/LoginViewController.m index bf0c428..04230bb 100644 --- a/PomeloChat/LoginViewController.m +++ b/PomeloChat/LoginViewController.m @@ -38,6 +38,8 @@ - (IBAction)login:(id)sender NSString *name = nameText.text; NSString *channel = channelText.text; + [[NSUserDefaults standardUserDefaults] setObject:name forKey:@"login_user"]; + if (([name length] > 0) && ([channel length] > 0)) { [pomelo connectToHost:@"127.0.0.1" onPort:3014 withCallback:^(Pomelo *p){ NSDictionary *params = [NSDictionary dictionaryWithObject:name forKey:@"uid"]; diff --git a/PomeloChat/SocketIO/SocketIO.h b/PomeloChat/SocketIO/SocketIO.h deleted file mode 100755 index cd68321..0000000 --- a/PomeloChat/SocketIO/SocketIO.h +++ /dev/null @@ -1,114 +0,0 @@ -// -// SocketIO.h -// v0.3.0 ARC -// -// based on -// socketio-cocoa https://github.com/fpotter/socketio-cocoa -// by Fred Potter -// -// using -// https://github.com/square/SocketRocket -// https://github.com/stig/json-framework/ -// -// reusing some parts of -// /socket.io/socket.io.js -// -// Created by Philipp Kyeck http://beta-interactive.de -// -// Updated by -// samlown https://github.com/samlown -// kayleg https://github.com/kayleg -// taiyangc https://github.com/taiyangc -// - -#import - -#import "SocketIOTransport.h" - -@class SocketIO; -@class SocketIOPacket; - -typedef void(^SocketIOCallback)(id argsData); - -extern NSString* const SocketIOError; - -typedef enum { - SocketIOServerRespondedWithInvalidConnectionData = -1, - SocketIOServerRespondedWithDisconnect = -2, - SocketIOHeartbeatTimeout = -3, - SocketIOWebSocketClosed = -4, - SocketIOTransportsNotSupported = -5, - SocketIOHandshakeFailed = -6, - SocketIODataCouldNotBeSend = -7 -} SocketIOErrorCodes; - - -@protocol SocketIODelegate -@optional -- (void) socketIODidConnect:(SocketIO *)socket; -- (void) socketIODidDisconnect:(SocketIO *)socket disconnectedWithError:(NSError *)error; -- (void) socketIO:(SocketIO *)socket didReceiveMessage:(SocketIOPacket *)packet; -- (void) socketIO:(SocketIO *)socket didReceiveJSON:(SocketIOPacket *)packet; -- (void) socketIO:(SocketIO *)socket didReceiveEvent:(SocketIOPacket *)packet; -- (void) socketIO:(SocketIO *)socket didSendMessage:(SocketIOPacket *)packet; -- (void) socketIO:(SocketIO *)socket onError:(NSError *)error; - -// TODO: deprecated -> to be removed -- (void) socketIO:(SocketIO *)socket failedToConnectWithError:(NSError *)error __attribute__((deprecated)); -- (void) socketIOHandshakeFailed:(SocketIO *)socket __attribute__((deprecated)); -@end - - -@interface SocketIO : NSObject -{ - NSString *_host; - NSInteger _port; - NSString *_sid; - NSString *_endpoint; - NSDictionary *_params; - - __unsafe_unretained id _delegate; - - NSObject *_transport; - - BOOL _isConnected; - BOOL _isConnecting; - BOOL _useSecure; - - // heartbeat - NSTimeInterval _heartbeatTimeout; - NSTimer *_timeout; - - NSMutableArray *_queue; - - // acknowledge - NSMutableDictionary *_acks; - NSInteger _ackCount; - - // http request - NSMutableData *_httpRequestData; -} - -@property (nonatomic, readonly) NSString *host; -@property (nonatomic, readonly) NSInteger port; -@property (nonatomic, readonly) NSString *sid; -@property (nonatomic, readonly) NSTimeInterval heartbeatTimeout; -@property (nonatomic) BOOL useSecure; -@property (nonatomic, readonly) BOOL isConnected, isConnecting; -@property (nonatomic, unsafe_unretained) id delegate; - -- (id) initWithDelegate:(id)delegate; -- (void) connectToHost:(NSString *)host onPort:(NSInteger)port; -- (void) connectToHost:(NSString *)host onPort:(NSInteger)port withParams:(NSDictionary *)params; -- (void) connectToHost:(NSString *)host onPort:(NSInteger)port withParams:(NSDictionary *)params withNamespace:(NSString *)endpoint; -- (void) disconnect; - -- (void) sendMessage:(NSString *)data; -- (void) sendMessage:(NSString *)data withAcknowledge:(SocketIOCallback)function; -- (void) sendJSON:(NSDictionary *)data; -- (void) sendJSON:(NSDictionary *)data withAcknowledge:(SocketIOCallback)function; -- (void) sendEvent:(NSString *)eventName withData:(id)data; -- (void) sendEvent:(NSString *)eventName withData:(id)data andAcknowledge:(SocketIOCallback)function; -- (void) sendAcknowledgement:(NSString*)pId withArgs:(NSArray *)data; - -@end \ No newline at end of file diff --git a/PomeloChat/SocketIO/SocketIO.m b/PomeloChat/SocketIO/SocketIO.m deleted file mode 100755 index 33b415c..0000000 --- a/PomeloChat/SocketIO/SocketIO.m +++ /dev/null @@ -1,762 +0,0 @@ -// -// SocketIO.m -// v0.3.0 ARC -// -// based on -// socketio-cocoa https://github.com/fpotter/socketio-cocoa -// by Fred Potter -// -// using -// https://github.com/square/SocketRocket -// https://github.com/stig/json-framework/ -// -// reusing some parts of -// /socket.io/socket.io.js -// -// Created by Philipp Kyeck http://beta-interactive.de -// -// Updated by -// samlown https://github.com/samlown -// kayleg https://github.com/kayleg -// taiyangc https://github.com/taiyangc -// - -#import "SocketIO.h" -#import "SocketIOPacket.h" -#import "SocketIOJSONSerialization.h" - -#import "SocketIOTransportWebsocket.h" -#import "SocketIOTransportXHR.h" - -#define DEBUG_LOGS 1 -#define DEBUG_CERTIFICATE 1 - -#if DEBUG_LOGS -#define DEBUGLOG(...) NSLog(__VA_ARGS__) -#else -#define DEBUGLOG(...) -#endif - -static NSString* kInsecureHandshakeURL = @"http://%@/socket.io/1/?t=%d%@"; -static NSString* kInsecureHandshakePortURL = @"http://%@:%d/socket.io/1/?t=%d%@"; -static NSString* kSecureHandshakePortURL = @"https://%@:%d/socket.io/1/?t=%d%@"; -static NSString* kSecureHandshakeURL = @"https://%@/socket.io/1/?t=%d%@"; - -NSString* const SocketIOError = @"SocketIOError"; -NSString* const SocketIOException = @"SocketIOException"; - -# pragma mark - -# pragma mark SocketIO's private interface - -@interface SocketIO (Private) - -- (NSArray*) arrayOfCaptureComponentsMatchedByRegex:(NSString*)regex; - -- (void) setTimeout; -- (void) onTimeout; - -- (void) onConnect:(SocketIOPacket *)packet; -- (void) onDisconnect:(NSError *)error; - -- (void) sendDisconnect; -- (void) sendHearbeat; -- (void) send:(SocketIOPacket *)packet; - -- (NSString *) addAcknowledge:(SocketIOCallback)function; -- (void) removeAcknowledgeForKey:(NSString *)key; -- (NSMutableArray*) getMatchesFrom:(NSString*)data with:(NSString*)regex; - -@end - -# pragma mark - -# pragma mark SocketIO implementation - -@implementation SocketIO - -@synthesize isConnected = _isConnected, - isConnecting = _isConnecting, - useSecure = _useSecure, - delegate = _delegate, - heartbeatTimeout = _heartbeatTimeout; - -- (id) initWithDelegate:(id)delegate -{ - self = [super init]; - if (self) { - _delegate = delegate; - _queue = [[NSMutableArray alloc] init]; - _ackCount = 0; - _acks = [[NSMutableDictionary alloc] init]; - } - return self; -} - -- (void) connectToHost:(NSString *)host onPort:(NSInteger)port -{ - [self connectToHost:host onPort:port withParams:nil withNamespace:@""]; -} - -- (void) connectToHost:(NSString *)host onPort:(NSInteger)port withParams:(NSDictionary *)params -{ - [self connectToHost:host onPort:port withParams:params withNamespace:@""]; -} - -- (void) connectToHost:(NSString *)host onPort:(NSInteger)port withParams:(NSDictionary *)params withNamespace:(NSString *)endpoint -{ - if (!_isConnected && !_isConnecting) { - _isConnecting = YES; - - _host = host; - _port = port; - _params = params; - _endpoint = [endpoint copy]; - - // create a query parameters string - NSMutableString *query = [[NSMutableString alloc] initWithString:@""]; - [params enumerateKeysAndObjectsUsingBlock: ^(id key, id value, BOOL *stop) { - [query appendFormat:@"&%@=%@", key, value]; - }]; - - // do handshake via HTTP request - NSString *s; - NSString *format; - if (_port) { - format = _useSecure ? kSecureHandshakePortURL : kInsecureHandshakePortURL; - s = [NSString stringWithFormat:format, _host, _port, rand(), query]; - } - else { - format = _useSecure ? kSecureHandshakeURL : kInsecureHandshakeURL; - s = [NSString stringWithFormat:format, _host, rand(), query]; - } - DEBUGLOG(@"Connecting to socket with URL: %@", s); - NSURL *url = [NSURL URLWithString:s]; - query = nil; - - - // make a request - NSURLRequest *request = [NSURLRequest requestWithURL:url - cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData - timeoutInterval:10.0]; - - NSURLConnection *connection = [NSURLConnection connectionWithRequest:request - delegate:self]; - if (connection) { - _httpRequestData = [NSMutableData data]; - } - else { - // connection failed - [self connection:connection didFailWithError:nil]; - } - } -} - -- (void) disconnect -{ - [self sendDisconnect]; -} - -- (void) sendMessage:(NSString *)data -{ - [self sendMessage:data withAcknowledge:nil]; -} - -- (void) sendMessage:(NSString *)data withAcknowledge:(SocketIOCallback)function -{ - SocketIOPacket *packet = [[SocketIOPacket alloc] initWithType:@"message"]; - packet.data = data; - packet.pId = [self addAcknowledge:function]; - [self send:packet]; -} - -- (void) sendJSON:(NSDictionary *)data -{ - [self sendJSON:data withAcknowledge:nil]; -} - -- (void) sendJSON:(NSDictionary *)data withAcknowledge:(SocketIOCallback)function -{ - SocketIOPacket *packet = [[SocketIOPacket alloc] initWithType:@"json"]; - packet.data = [SocketIOJSONSerialization JSONStringFromObject:data error:nil]; - packet.pId = [self addAcknowledge:function]; - [self send:packet]; -} - -- (void) sendEvent:(NSString *)eventName withData:(id)data -{ - [self sendEvent:eventName withData:data andAcknowledge:nil]; -} - -- (void) sendEvent:(NSString *)eventName withData:(id)data andAcknowledge:(SocketIOCallback)function -{ - NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObject:eventName forKey:@"name"]; - - // do not require arguments - if (data != nil) { - [dict setObject:[NSArray arrayWithObject:data] forKey:@"args"]; - } - - SocketIOPacket *packet = [[SocketIOPacket alloc] initWithType:@"event"]; - packet.data = [SocketIOJSONSerialization JSONStringFromObject:dict error:nil]; - packet.pId = [self addAcknowledge:function]; - if (function) { - packet.ack = @"data"; - } - [self send:packet]; -} - -- (void) sendAcknowledgement:(NSString *)pId withArgs:(NSArray *)data -{ - SocketIOPacket *packet = [[SocketIOPacket alloc] initWithType:@"ack"]; - packet.data = [SocketIOJSONSerialization JSONStringFromObject:data error:nil]; - packet.pId = pId; - packet.ack = @"data"; - - [self send:packet]; -} - -# pragma mark - -# pragma mark private methods - -- (void) sendDisconnect -{ - SocketIOPacket *packet = [[SocketIOPacket alloc] initWithType:@"disconnect"]; - [self send:packet]; -} - -- (void) sendConnect -{ - SocketIOPacket *packet = [[SocketIOPacket alloc] initWithType:@"connect"]; - [self send:packet]; -} - -- (void) sendHeartbeat -{ - SocketIOPacket *packet = [[SocketIOPacket alloc] initWithType:@"heartbeat"]; - [self send:packet]; -} - -- (void) send:(SocketIOPacket *)packet -{ - DEBUGLOG(@"send()"); - NSNumber *type = [packet typeAsNumber]; - NSMutableArray *encoded = [NSMutableArray arrayWithObject:type]; - - NSString *pId = packet.pId != nil ? packet.pId : @""; - if ([packet.ack isEqualToString:@"data"]) { - pId = [pId stringByAppendingString:@"+"]; - } - - // Do not write pid for acknowledgements - if ([type intValue] != 6) { - [encoded addObject:pId]; - } - - // Add the end point for the namespace to be used, as long as it is not - // an ACK, heartbeat, or disconnect packet - if ([type intValue] != 6 && [type intValue] != 2 && [type intValue] != 0) { - [encoded addObject:_endpoint]; - } - else { - [encoded addObject:@""]; - } - - if (packet.data != nil) { - NSString *ackpId = @""; - // This is an acknowledgement packet, so, prepend the ack pid to the data - if ([type intValue] == 6) { - ackpId = [NSString stringWithFormat:@":%@%@", packet.pId, @"+"]; - } - [encoded addObject:[NSString stringWithFormat:@"%@%@", ackpId, packet.data]]; - } - - NSString *req = [encoded componentsJoinedByString:@":"]; - if (![_transport isReady]) { - DEBUGLOG(@"queue >>> %@", req); - [_queue addObject:packet]; - } - else { - DEBUGLOG(@"send() >>> %@", req); - [_transport send:req]; - - if ([_delegate respondsToSelector:@selector(socketIO:didSendMessage:)]) { - [_delegate socketIO:self didSendMessage:packet]; - } - } -} - -- (void) doQueue -{ - DEBUGLOG(@"doQueue() >> %lu", (unsigned long)[_queue count]); - - // TODO send all packets at once ... not as seperate packets - while ([_queue count] > 0) { - SocketIOPacket *packet = [_queue objectAtIndex:0]; - [self send:packet]; - [_queue removeObject:packet]; - } -} - -- (void) onConnect:(SocketIOPacket *)packet -{ - DEBUGLOG(@"onConnect()"); - - _isConnected = YES; - - // Send the connected packet so the server knows what it's dealing with. - // Only required when endpoint/namespace is present - if ([_endpoint length] > 0) { - // Make sure the packet we received has an endpoint, otherwise send it again - if (![packet.endpoint isEqualToString:_endpoint]) { - DEBUGLOG(@"onConnect() >> End points do not match, resending connect packet"); - [self sendConnect]; - return; - } - } - - _isConnecting = NO; - - if ([_delegate respondsToSelector:@selector(socketIODidConnect:)]) { - [_delegate socketIODidConnect:self]; - } - - // send any queued packets - [self doQueue]; - - [self setTimeout]; -} - -# pragma mark - -# pragma mark Acknowledge methods - -- (NSString *) addAcknowledge:(SocketIOCallback)function -{ - if (function) { - ++_ackCount; - NSString *ac = [NSString stringWithFormat:@"%ld", (long)_ackCount]; - [_acks setObject:[function copy] forKey:ac]; - return ac; - } - return nil; -} - -- (void) removeAcknowledgeForKey:(NSString *)key -{ - [_acks removeObjectForKey:key]; -} - -# pragma mark - -# pragma mark Heartbeat methods - -- (void) onTimeout -{ - DEBUGLOG(@"Timed out waiting for heartbeat."); - [self onDisconnect:[NSError errorWithDomain:SocketIOError - code:SocketIOHeartbeatTimeout - userInfo:nil]]; -} - -- (void) setTimeout -{ - DEBUGLOG(@"start/reset timeout"); - if (_timeout != nil) { - [_timeout invalidate]; - _timeout = nil; - } - - _timeout = [NSTimer scheduledTimerWithTimeInterval:_heartbeatTimeout - target:self - selector:@selector(onTimeout) - userInfo:nil - repeats:NO]; -} - - -# pragma mark - -# pragma mark Regex helper method -- (NSMutableArray*) getMatchesFrom:(NSString*)data with:(NSString*)regex -{ - NSRegularExpression *nsregexTest = [NSRegularExpression regularExpressionWithPattern:regex options:0 error:nil]; - NSArray *nsmatchesTest = [nsregexTest matchesInString:data options:0 range:NSMakeRange(0, [data length])]; - NSMutableArray *arr = [NSMutableArray array]; - - for (NSTextCheckingResult *nsmatchTest in nsmatchesTest) { - NSMutableArray *localMatch = [NSMutableArray array]; - for (NSUInteger i = 0, l = [nsmatchTest numberOfRanges]; i < l; i++) { - NSRange range = [nsmatchTest rangeAtIndex:i]; - NSString *nsmatchStr = nil; - if (range.location != NSNotFound && NSMaxRange(range) <= [data length]) { - nsmatchStr = [data substringWithRange:[nsmatchTest rangeAtIndex:i]]; - } - else { - nsmatchStr = @""; - } - [localMatch addObject:nsmatchStr]; - } - [arr addObject:localMatch]; - } - - return arr; -} - - -#pragma mark - -#pragma mark SocketIOTransport callbacks - -- (void) onData:(NSString *)data -{ - DEBUGLOG(@"onData %@", data); - - // data arrived -> reset timeout - [self setTimeout]; - - // check if data is valid (from socket.io.js) - NSString *regex = @"^([^:]+):([0-9]+)?(\\+)?:([^:]+)?:?(.*)?$"; - NSString *regexPieces = @"^([0-9]+)(\\+)?(.*)"; - - // create regex result - NSMutableArray *test = [self getMatchesFrom:data with:regex]; - - // valid data-string arrived - if ([test count] > 0) { - NSArray *result = [test objectAtIndex:0]; - - int idx = [[result objectAtIndex:1] intValue]; - SocketIOPacket *packet = [[SocketIOPacket alloc] initWithTypeIndex:idx]; - - packet.pId = [result objectAtIndex:2]; - - packet.ack = [result objectAtIndex:3]; - packet.endpoint = [result objectAtIndex:4]; - packet.data = [result objectAtIndex:5]; - - // - switch (idx) { - case 0: { - DEBUGLOG(@"disconnect"); - [self onDisconnect:[NSError errorWithDomain:SocketIOError - code:SocketIOServerRespondedWithDisconnect - userInfo:nil]]; - break; - } - case 1: { - DEBUGLOG(@"connected"); - // from socket.io.js ... not sure when data will contain sth?! - // packet.qs = data || ''; - [self onConnect:packet]; - break; - } - case 2: { - DEBUGLOG(@"heartbeat"); - [self sendHeartbeat]; - break; - } - case 3: { - DEBUGLOG(@"message"); - if (packet.data && ![packet.data isEqualToString:@""]) { - if ([_delegate respondsToSelector:@selector(socketIO:didReceiveMessage:)]) { - [_delegate socketIO:self didReceiveMessage:packet]; - } - } - break; - } - case 4: { - DEBUGLOG(@"json"); - if (packet.data && ![packet.data isEqualToString:@""]) { - if ([_delegate respondsToSelector:@selector(socketIO:didReceiveJSON:)]) { - [_delegate socketIO:self didReceiveJSON:packet]; - } - } - break; - } - case 5: { - DEBUGLOG(@"event"); - if (packet.data && ![packet.data isEqualToString:@""]) { - NSDictionary *json = [packet dataAsJSON]; - packet.name = [json objectForKey:@"name"]; - packet.args = [json objectForKey:@"args"]; - if ([_delegate respondsToSelector:@selector(socketIO:didReceiveEvent:)]) { - [_delegate socketIO:self didReceiveEvent:packet]; - } - } - break; - } - case 6: { - DEBUGLOG(@"ack"); - - // create regex result - NSMutableArray *pieces = [self getMatchesFrom:packet.data with:regexPieces]; - - if ([pieces count] > 0) { - NSArray *piece = [pieces objectAtIndex:0]; - int ackId = [[piece objectAtIndex:1] intValue]; - DEBUGLOG(@"ack id found: %d", ackId); - - NSString *argsStr = [piece objectAtIndex:3]; - id argsData = nil; - if (argsStr && ![argsStr isEqualToString:@""]) { - argsData = [SocketIOJSONSerialization objectFromJSONData:[argsStr dataUsingEncoding:NSUTF8StringEncoding] error:nil]; - if ([argsData count] > 0) { - argsData = [argsData objectAtIndex:0]; - } - } - - // get selector for ackId - NSString *key = [NSString stringWithFormat:@"%d", ackId]; - SocketIOCallback callbackFunction = [_acks objectForKey:key]; - if (callbackFunction != nil) { - callbackFunction(argsData); - [self removeAcknowledgeForKey:key]; - } - } - - break; - } - case 7: { - DEBUGLOG(@"error"); - break; - } - case 8: { - DEBUGLOG(@"noop"); - break; - } - default: { - DEBUGLOG(@"command not found or not yet supported"); - break; - } - } - - packet = nil; - } - else { - DEBUGLOG(@"ERROR: data that has arrived wasn't valid"); - } -} - -- (void) onDisconnect:(NSError *)error -{ - DEBUGLOG(@"onDisconnect()"); - BOOL wasConnected = _isConnected; - BOOL wasConnecting = _isConnecting; - - _isConnected = NO; - _isConnecting = NO; - _sid = nil; - - [_queue removeAllObjects]; - - // Kill the heartbeat timer - if (_timeout != nil) { - [_timeout invalidate]; - _timeout = nil; - } - - // Disconnect the websocket, just in case - if (_transport != nil) { - // clear websocket's delegate - otherwise crashes - _transport.delegate = nil; - [_transport close]; - } - - if ((wasConnected || wasConnecting)) { - if ([_delegate respondsToSelector:@selector(socketIODidDisconnect:disconnectedWithError:)]) { - [_delegate socketIODidDisconnect:self disconnectedWithError:error]; - } - } -} - -- (void) onError:(NSError *)error -{ - if ([_delegate respondsToSelector:@selector(socketIO:onError:)]) { - [_delegate socketIO:self onError:error]; - } -} - - -# pragma mark - -# pragma mark Handshake callbacks (NSURLConnectionDataDelegate) -- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response -{ - // check for server status code (http://gigliwood.com/weblog/Cocoa/Q__When_is_an_conne.html) - if ([response respondsToSelector:@selector(statusCode)]) { - int statusCode = [((NSHTTPURLResponse *)response) statusCode]; - DEBUGLOG(@"didReceiveResponse() %i", statusCode); - - if (statusCode >= 400) { - // stop connecting; no more delegate messages - [connection cancel]; - - NSString *error = [NSString stringWithFormat:NSLocalizedString(@"Server returned status code %d", @""), statusCode]; - NSDictionary *errorInfo = [NSDictionary dictionaryWithObject:error forKey:NSLocalizedDescriptionKey]; - NSError *statusError = [NSError errorWithDomain:SocketIOError - code:statusCode - userInfo:errorInfo]; - // call error callback manually - [self connection:connection didFailWithError:statusError]; - } - } - - [_httpRequestData setLength:0]; -} - -- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data -{ - [_httpRequestData appendData:data]; -} - -- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error -{ - NSLog(@"ERROR: handshake failed ... %@", [error localizedDescription]); - - _isConnected = NO; - _isConnecting = NO; - - if ([_delegate respondsToSelector:@selector(socketIO:onError:)]) { - NSMutableDictionary *errorInfo = [NSDictionary dictionaryWithObject:error forKey:NSLocalizedDescriptionKey]; - - NSError *err = [NSError errorWithDomain:SocketIOError - code:SocketIOHandshakeFailed - userInfo:errorInfo]; - - [_delegate socketIO:self onError:err]; - } - // TODO: deprecated - to be removed - else if ([_delegate respondsToSelector:@selector(socketIOHandshakeFailed:)]) { - [_delegate socketIOHandshakeFailed:self]; - } -} - -- (void) connectionDidFinishLoading:(NSURLConnection *)connection -{ - NSString *responseString = [[NSString alloc] initWithData:_httpRequestData encoding:NSASCIIStringEncoding]; - - DEBUGLOG(@"connectionDidFinishLoading() %@", responseString); - NSArray *data = [responseString componentsSeparatedByString:@":"]; - // should be SID : heartbeat timeout : connection timeout : supported transports - - // check each returned value (thanks for the input https://github.com/taiyangc) - BOOL connectionFailed = false; - NSError* error; - - _sid = [data objectAtIndex:0]; - if ([_sid length] < 1 || [data count] < 4) { - // did not receive valid data, possibly missing a useSecure? - connectionFailed = true; - } - else { - // check SID - DEBUGLOG(@"sid: %@", _sid); - NSString *regex = @"[^0-9]"; - NSPredicate *regexTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex]; - if ([_sid rangeOfString:@"error"].location != NSNotFound || [regexTest evaluateWithObject:_sid]) { - [self connectToHost:_host onPort:_port withParams:_params withNamespace:_endpoint]; - return; - } - - // check heartbeat timeout - _heartbeatTimeout = [[data objectAtIndex:1] floatValue]; - if (_heartbeatTimeout == 0.0) { - // couldn't find float value -> fail - connectionFailed = true; - } - else { - // add small buffer of 7sec (magic xD) - _heartbeatTimeout += 7.0; - } - DEBUGLOG(@"heartbeatTimeout: %f", _heartbeatTimeout); - - // index 2 => connection timeout - - // get transports - NSString *t = [data objectAtIndex:3]; - NSArray *transports = [t componentsSeparatedByString:@","]; - DEBUGLOG(@"transports: %@", transports); - - if ([transports indexOfObject:@"websocket"] != NSNotFound) { - DEBUGLOG(@"websocket supported -> using it now"); - _transport = [[SocketIOTransportWebsocket alloc] initWithDelegate:self]; - } - else if ([transports indexOfObject:@"xhr-polling"] != NSNotFound) { - DEBUGLOG(@"xhr polling supported -> using it now"); - _transport = [[SocketIOTransportXHR alloc] initWithDelegate:self]; - } - else { - DEBUGLOG(@"no transport found that is supported :( -> fail"); - connectionFailed = true; - error = [NSError errorWithDomain:SocketIOError - code:SocketIOTransportsNotSupported - userInfo:nil]; - } - } - - // if connection didn't return the values we need -> fail - if (connectionFailed) { - // error already set!? - if (error == nil) { - error = [NSError errorWithDomain:SocketIOError - code:SocketIOServerRespondedWithInvalidConnectionData - userInfo:nil]; - } - - if ([_delegate respondsToSelector:@selector(socketIO:onError:)]) { - [_delegate socketIO:self onError:error]; - } - // TODO: deprecated - to be removed - else if ([_delegate respondsToSelector:@selector(socketIO:failedToConnectWithError:)]) { - [_delegate socketIO:self failedToConnectWithError:error]; - } - - // make sure to do call all cleanup code - [self onDisconnect:error]; - - return; - } - - [_transport open]; -} - -#if DEBUG_CERTIFICATE - -// to deal with self-signed certificates -- (BOOL) connection:(NSURLConnection *)connection -canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace -{ - return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]; -} - -- (void) connection:(NSURLConnection *)connection -didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge -{ - if ([challenge.protectionSpace.authenticationMethod - isEqualToString:NSURLAuthenticationMethodServerTrust]) { - // we only trust our own domain - if ([challenge.protectionSpace.host isEqualToString:_host]) { - SecTrustRef trust = challenge.protectionSpace.serverTrust; - NSURLCredential *credential = [NSURLCredential credentialForTrust:trust]; - [challenge.sender useCredential:credential forAuthenticationChallenge:challenge]; - } - } - - [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge]; -} -#endif - - -# pragma mark - - -- (void) dealloc -{ - _host = nil; - _sid = nil; - _endpoint = nil; - - _transport = nil; - - [_timeout invalidate]; - _timeout = nil; - - _queue = nil; - _acks = nil; -} - - -@end \ No newline at end of file diff --git a/PomeloChat/SocketIO/SocketIOJSONSerialization.h b/PomeloChat/SocketIO/SocketIOJSONSerialization.h deleted file mode 100644 index 0f7818c..0000000 --- a/PomeloChat/SocketIO/SocketIOJSONSerialization.h +++ /dev/null @@ -1,31 +0,0 @@ -// -// SocketIOJSONSerialization.h -// v0.3.0 ARC -// -// based on -// socketio-cocoa https://github.com/fpotter/socketio-cocoa -// by Fred Potter -// -// using -// https://github.com/square/SocketRocket -// https://github.com/stig/json-framework/ -// -// reusing some parts of -// /socket.io/socket.io.js -// -// Created by Philipp Kyeck http://beta-interactive.de -// -// Updated by -// samlown https://github.com/samlown -// kayleg https://github.com/kayleg -// taiyangc https://github.com/taiyangc -// - -#import - -@interface SocketIOJSONSerialization : NSObject - -+ (id) objectFromJSONData:(NSData *)data error:(NSError **)error; -+ (NSString *) JSONStringFromObject:(id)object error:(NSError **)error; - -@end diff --git a/PomeloChat/SocketIO/SocketIOJSONSerialization.m b/PomeloChat/SocketIO/SocketIOJSONSerialization.m deleted file mode 100644 index 3a2743f..0000000 --- a/PomeloChat/SocketIO/SocketIOJSONSerialization.m +++ /dev/null @@ -1,115 +0,0 @@ -// -// SocketIOJSONSerialization.m -// v0.3.0 ARC -// -// based on -// socketio-cocoa https://github.com/fpotter/socketio-cocoa -// by Fred Potter -// -// using -// https://github.com/square/SocketRocket -// https://github.com/stig/json-framework/ -// -// reusing some parts of -// /socket.io/socket.io.js -// -// Created by Philipp Kyeck http://beta-interactive.de -// -// Updated by -// samlown https://github.com/samlown -// kayleg https://github.com/kayleg -// taiyangc https://github.com/taiyangc -// - -#import "SocketIOJSONSerialization.h" - -extern NSString * const SocketIOException; - -// covers the methods in SBJson and JSONKit -@interface NSObject (SocketIOJSONSerialization) - -// used by both JSONKit and SBJson -- (id) objectWithData:(NSData *)data; - -// Use by JSONKit serialization -- (NSString *) JSONString; -- (id) decoder; - -// Used by SBJsonWriter -- (NSString *) stringWithObject:(id)object; - -@end - -@implementation SocketIOJSONSerialization - -+ (id) objectFromJSONData:(NSData *)data error:(NSError **)error { - Class serializer; - - // try SBJson first - serializer = NSClassFromString(@"SBJsonParser"); - if (serializer) { - id parser; - id object; - - parser = [[serializer alloc] init]; - object = [parser objectWithData:data]; - - return object; - } - - // try Foundation's JSON coder, available in OS X 10.7/iOS 5.0 - serializer = NSClassFromString(@"NSJSONSerialization"); - if (serializer) { - return [serializer JSONObjectWithData:data options:0 error:error]; - } - - // lastly, try JSONKit - serializer = NSClassFromString(@"JSONDecoder"); - if (serializer) { - return [[serializer decoder] objectWithData:data]; - } - - // unable to find a suitable JSON deseralizer - [NSException raise:SocketIOException format:@"socket.IO-objc requires SBJson, JSONKit or an OS that has NSJSONSerialization."]; - - return nil; -} - -+ (NSString *) JSONStringFromObject:(id)object error:(NSError **)error { - Class serializer; - NSString *jsonString; - - jsonString = nil; - serializer = NSClassFromString(@"SBJsonWriter"); - if (serializer) { - id writer; - - writer = [[serializer alloc] init]; - jsonString = [writer stringWithObject:object]; - - return jsonString; - } - - serializer = NSClassFromString(@"NSJSONSerialization"); - if (serializer) { - NSData *data; - - data = [serializer dataWithJSONObject:object options:0 error:error]; - - jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - - return jsonString; - } - - // lastly, try JSONKit - if ([object respondsToSelector:@selector(JSONString)]) { - return [object JSONString]; - } - - // unable to find a suitable JSON seralizer - [NSException raise:SocketIOException format:@"socket.IO-objc requires SBJson, JSONKit or an OS that has NSJSONSerialization."]; - - return nil; -} - -@end diff --git a/PomeloChat/SocketIO/SocketIOPacket.h b/PomeloChat/SocketIO/SocketIOPacket.h deleted file mode 100644 index e201cf6..0000000 --- a/PomeloChat/SocketIO/SocketIOPacket.h +++ /dev/null @@ -1,52 +0,0 @@ -// -// SocketIOPacket.h -// v0.3.0 ARC -// -// based on -// socketio-cocoa https://github.com/fpotter/socketio-cocoa -// by Fred Potter -// -// using -// https://github.com/square/SocketRocket -// https://github.com/stig/json-framework/ -// -// reusing some parts of -// /socket.io/socket.io.js -// -// Created by Philipp Kyeck http://beta-interactive.de -// -// Updated by -// samlown https://github.com/samlown -// kayleg https://github.com/kayleg -// taiyangc https://github.com/taiyangc -// - -#import - -@interface SocketIOPacket : NSObject -{ - NSString *type; - NSString *pId; - NSString *ack; - NSString *name; - NSString *data; - NSArray *args; - NSString *endpoint; - NSArray *_types; -} - -@property (nonatomic, copy) NSString *type; -@property (nonatomic, copy) NSString *pId; -@property (nonatomic, copy) NSString *ack; -@property (nonatomic, copy) NSString *name; -@property (nonatomic, copy) NSString *data; -@property (nonatomic, copy) NSString *endpoint; -@property (nonatomic, copy) NSArray *args; - -- (id) initWithType:(NSString *)packetType; -- (id) initWithTypeIndex:(int)index; -- (id) dataAsJSON; -- (NSNumber *) typeAsNumber; -- (NSString *) typeForIndex:(int)index; - -@end diff --git a/PomeloChat/SocketIO/SocketIOPacket.m b/PomeloChat/SocketIO/SocketIOPacket.m deleted file mode 100644 index 7532255..0000000 --- a/PomeloChat/SocketIO/SocketIOPacket.m +++ /dev/null @@ -1,104 +0,0 @@ -// -// SocketIOPacket.h -// v0.3.0 ARC -// -// based on -// socketio-cocoa https://github.com/fpotter/socketio-cocoa -// by Fred Potter -// -// using -// https://github.com/square/SocketRocket -// https://github.com/stig/json-framework/ -// -// reusing some parts of -// /socket.io/socket.io.js -// -// Created by Philipp Kyeck http://beta-interactive.de -// -// Updated by -// samlown https://github.com/samlown -// kayleg https://github.com/kayleg -// taiyangc https://github.com/taiyangc -// - -#import "SocketIOPacket.h" -#import "SocketIOJSONSerialization.h" - -@implementation SocketIOPacket - -@synthesize type, pId, name, ack, data, args, endpoint; - -- (id) init -{ - self = [super init]; - if (self) { - _types = [NSArray arrayWithObjects: @"disconnect", - @"connect", - @"heartbeat", - @"message", - @"json", - @"event", - @"ack", - @"error", - @"noop", - nil]; - } - return self; -} - -- (id) initWithType:(NSString *)packetType -{ - self = [self init]; - if (self) { - self.type = packetType; - } - return self; -} - -- (id) initWithTypeIndex:(int)index -{ - self = [self init]; - if (self) { - self.type = [self typeForIndex:index]; - } - return self; -} - -- (id) dataAsJSON -{ - if (self.data) { - NSData *utf8Data = [self.data dataUsingEncoding:NSUTF8StringEncoding]; - return [SocketIOJSONSerialization objectFromJSONData:utf8Data error:nil]; - } - else { - return nil; - } -} - -- (NSNumber *) typeAsNumber -{ - NSUInteger index = [_types indexOfObject:self.type]; - NSNumber *num = [NSNumber numberWithUnsignedInteger:index]; - return num; -} - -- (NSString *) typeForIndex:(int)index -{ - return [_types objectAtIndex:index]; -} - -- (void) dealloc -{ - _types = nil; - - type = nil; - pId = nil; - name = nil; - ack = nil; - data = nil; - args = nil; - endpoint = nil; -} - -@end - diff --git a/PomeloChat/SocketIO/SocketIOTransport.h b/PomeloChat/SocketIO/SocketIOTransport.h deleted file mode 100644 index d2ac580..0000000 --- a/PomeloChat/SocketIO/SocketIOTransport.h +++ /dev/null @@ -1,50 +0,0 @@ -// -// SocketIOTransport.h -// v0.3.0 ARC -// -// based on -// socketio-cocoa https://github.com/fpotter/socketio-cocoa -// by Fred Potter -// -// using -// https://github.com/square/SocketRocket -// https://github.com/stig/json-framework/ -// -// reusing some parts of -// /socket.io/socket.io.js -// -// Created by Philipp Kyeck http://beta-interactive.de -// -// Updated by -// samlown https://github.com/samlown -// kayleg https://github.com/kayleg -// taiyangc https://github.com/taiyangc -// - -#import - -@protocol SocketIOTransportDelegate - -- (void) onData:(id)message; -- (void) onDisconnect:(NSError*)error; -- (void) onError:(NSError*)error; - -@property (nonatomic, readonly) NSString *host; -@property (nonatomic, readonly) NSInteger port; -@property (nonatomic, readonly) NSString *sid; -@property (nonatomic, readonly) NSTimeInterval heartbeatTimeout; -@property (nonatomic) BOOL useSecure; - -@end - -@protocol SocketIOTransport - -- (id) initWithDelegate:(id )delegate; -- (void) open; -- (void) close; -- (BOOL) isReady; -- (void) send:(NSString *)request; - -@property (nonatomic, unsafe_unretained) id delegate; - -@end diff --git a/PomeloChat/SocketIO/SocketIOTransportWebsocket.h b/PomeloChat/SocketIO/SocketIOTransportWebsocket.h deleted file mode 100644 index b79ad39..0000000 --- a/PomeloChat/SocketIO/SocketIOTransportWebsocket.h +++ /dev/null @@ -1,36 +0,0 @@ -// -// SocketIOTransportWebsocket.h -// v0.3.0 ARC -// -// based on -// socketio-cocoa https://github.com/fpotter/socketio-cocoa -// by Fred Potter -// -// using -// https://github.com/square/SocketRocket -// https://github.com/stig/json-framework/ -// -// reusing some parts of -// /socket.io/socket.io.js -// -// Created by Philipp Kyeck http://beta-interactive.de -// -// Updated by -// samlown https://github.com/samlown -// kayleg https://github.com/kayleg -// taiyangc https://github.com/taiyangc -// - -#import - -#import "SRWebSocket.h" -#import "SocketIOTransport.h" - -@interface SocketIOTransportWebsocket : NSObject -{ - SRWebSocket *_webSocket; -} - -@property (nonatomic, unsafe_unretained) id delegate; - -@end diff --git a/PomeloChat/SocketIO/SocketIOTransportWebsocket.m b/PomeloChat/SocketIO/SocketIOTransportWebsocket.m deleted file mode 100644 index 04d10e7..0000000 --- a/PomeloChat/SocketIO/SocketIOTransportWebsocket.m +++ /dev/null @@ -1,123 +0,0 @@ -// -// SocketIOTransportWebsocket.m -// v0.3.0 ARC -// -// based on -// socketio-cocoa https://github.com/fpotter/socketio-cocoa -// by Fred Potter -// -// using -// https://github.com/square/SocketRocket -// https://github.com/stig/json-framework/ -// -// reusing some parts of -// /socket.io/socket.io.js -// -// Created by Philipp Kyeck http://beta-interactive.de -// -// Updated by -// samlown https://github.com/samlown -// kayleg https://github.com/kayleg -// taiyangc https://github.com/taiyangc -// - -#import "SocketIOTransportWebsocket.h" -#import "SocketIO.h" - -#define DEBUG_LOGS 0 - -#if DEBUG_LOGS -#define DEBUGLOG(...) NSLog(__VA_ARGS__) -#else -#define DEBUGLOG(...) -#endif - -static NSString* kInsecureSocketURL = @"ws://%@/socket.io/1/websocket/%@"; -static NSString* kSecureSocketURL = @"wss://%@/socket.io/1/websocket/%@"; -static NSString* kInsecureSocketPortURL = @"ws://%@:%d/socket.io/1/websocket/%@"; -static NSString* kSecureSocketPortURL = @"wss://%@:%d/socket.io/1/websocket/%@"; - -@implementation SocketIOTransportWebsocket - -@synthesize delegate; - -- (id) initWithDelegate:(id)delegate_ -{ - self = [super init]; - if (self) { - self.delegate = delegate_; - } - return self; -} - -- (BOOL) isReady -{ - return _webSocket.readyState == SR_OPEN; -} - -- (void) open -{ - NSString *urlStr; - NSString *format; - if (delegate.port) { - format = delegate.useSecure ? kSecureSocketPortURL : kInsecureSocketPortURL; - urlStr = [NSString stringWithFormat:format, delegate.host, delegate.port, delegate.sid]; - } - else { - format = delegate.useSecure ? kSecureSocketURL : kInsecureSocketURL; - urlStr = [NSString stringWithFormat:format, delegate.host, delegate.sid]; - } - NSURL *url = [NSURL URLWithString:urlStr]; - - _webSocket = nil; - - _webSocket = [[SRWebSocket alloc] initWithURL:url]; - _webSocket.delegate = self; - DEBUGLOG(@"Opening %@", url); - [_webSocket open]; -} - -- (void) close -{ - [_webSocket close]; -} - -- (void) send:(NSString*)request -{ - [_webSocket send:request]; -} - - - -# pragma mark - -# pragma mark WebSocket Delegate Methods - -- (void) webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message -{ - [delegate onData:message]; -} - -- (void) webSocketDidOpen:(SRWebSocket *)webSocket -{ - DEBUGLOG(@"Socket opened."); -} - -- (void) webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error -{ - DEBUGLOG(@"Socket failed with error ... %@", [error localizedDescription]); - // Assuming this resulted in a disconnect - [delegate onDisconnect:error]; -} - -- (void) webSocket:(SRWebSocket *)webSocket - didCloseWithCode:(NSInteger)code - reason:(NSString *)reason - wasClean:(BOOL)wasClean -{ - DEBUGLOG(@"Socket closed. %@", reason); - [delegate onDisconnect:[NSError errorWithDomain:SocketIOError - code:SocketIOWebSocketClosed - userInfo:nil]]; -} - -@end diff --git a/PomeloChat/SocketIO/SocketIOTransportXHR.h b/PomeloChat/SocketIO/SocketIOTransportXHR.h deleted file mode 100644 index 189401c..0000000 --- a/PomeloChat/SocketIO/SocketIOTransportXHR.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// SocketIOTransportXHR.h -// v0.3.0 ARC -// -// based on -// socketio-cocoa https://github.com/fpotter/socketio-cocoa -// by Fred Potter -// -// using -// https://github.com/square/SocketRocket -// https://github.com/stig/json-framework/ -// -// reusing some parts of -// /socket.io/socket.io.js -// -// Created by Philipp Kyeck http://beta-interactive.de -// -// Updated by -// samlown https://github.com/samlown -// kayleg https://github.com/kayleg -// taiyangc https://github.com/taiyangc -// - -#import - -#import "SocketIOTransport.h" - -@interface SocketIOTransportXHR : NSObject -{ - NSString *_url; - NSMutableData *_data; - NSMutableDictionary *_polls; -} - -@property (nonatomic, unsafe_unretained) id delegate; - -@end diff --git a/PomeloChat/SocketIO/SocketIOTransportXHR.m b/PomeloChat/SocketIO/SocketIOTransportXHR.m deleted file mode 100644 index dc44405..0000000 --- a/PomeloChat/SocketIO/SocketIOTransportXHR.m +++ /dev/null @@ -1,221 +0,0 @@ -// -// SocketIOTransportXHR.m -// v0.3.0 ARC -// -// based on -// socketio-cocoa https://github.com/fpotter/socketio-cocoa -// by Fred Potter -// -// using -// https://github.com/square/SocketRocket -// https://github.com/stig/json-framework/ -// -// reusing some parts of -// /socket.io/socket.io.js -// -// Created by Philipp Kyeck http://beta-interactive.de -// -// Updated by -// samlown https://github.com/samlown -// kayleg https://github.com/kayleg -// taiyangc https://github.com/taiyangc -// - -#import "SocketIOTransportXHR.h" -#import "SocketIO.h" - -#define DEBUG_LOGS 0 - -#if DEBUG_LOGS -#define DEBUGLOG(...) NSLog(__VA_ARGS__) -#else -#define DEBUGLOG(...) -#endif - -static NSString* kInsecureXHRURL = @"http://%@/socket.io/1/xhr-polling/%@"; -static NSString* kSecureXHRURL = @"https://%@/socket.io/1/xhr-polling/%@"; -static NSString* kInsecureXHRPortURL = @"http://%@:%d/socket.io/1/xhr-polling/%@"; -static NSString* kSecureXHRPortURL = @"https://%@:%d/socket.io/1/xhr-polling/%@"; - -@interface SocketIOTransportXHR (Private) -- (void) checkAndStartPoll; -- (void) poll:(NSString *)data; -- (void) poll:(NSString *)data retryNumber:(int)retry; -@end - -@implementation SocketIOTransportXHR - -@synthesize delegate; - -- (id) initWithDelegate:(id)delegate_ -{ - self = [super init]; - if (self) { - self.delegate = delegate_; - _data = [[NSMutableData alloc] init]; - _polls = [[NSMutableDictionary alloc] init]; - } - return self; -} - -- (void) open -{ - NSString *format; - if (delegate.port) { - format = delegate.useSecure ? kSecureXHRPortURL : kInsecureXHRPortURL; - _url = [NSString stringWithFormat:format, delegate.host, delegate.port, delegate.sid]; - } - else { - format = delegate.useSecure ? kSecureXHRURL : kInsecureXHRURL; - _url = [NSString stringWithFormat:format, delegate.host, delegate.sid]; - } - DEBUGLOG(@"Opening XHR @ %@", _url); - [self poll:nil]; -} - -- (void) close -{ - NSMutableDictionary *pollData; - NSURLConnection *conn; - for (NSString *key in _polls) { - pollData = [_polls objectForKey:key]; - conn = [pollData objectForKey:@"connection"]; - [conn cancel]; - } - [_polls removeAllObjects]; -} - -- (BOOL) isReady -{ - return YES; -} - -- (void) send:(NSString *)request -{ - [self poll:request]; -} - - -#pragma mark - -#pragma mark private methods - -- (void) checkAndStartPoll -{ - BOOL restart = NO; - // no polls currently running -> start one - if ([_polls count] == 0) { - restart = YES; - } - else { - restart = YES; - // look for polls w/o data -> if there, no need to restart - for (NSString *key in _polls) { - NSMutableDictionary *pollData = [_polls objectForKey:key]; - if ([pollData objectForKey:@"data"] == nil) { - restart = NO; - break; - } - } - } - - if (restart) { - [self poll:nil]; - } -} - -- (void) poll:(NSString *)data -{ - [self poll:data retryNumber:0]; -} - -- (void) poll:(NSString *)data retryNumber:(int)retry -{ - NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970]; - double unix = timeStamp * 1000; - NSString *url = [_url stringByAppendingString:[NSString stringWithFormat:@"?t=%.0f", unix]]; - - DEBUGLOG(@"---------------------------------------------------------------------------------------"); - DEBUGLOG(@"poll() %@", url); - - NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] - cachePolicy:NSURLRequestUseProtocolCachePolicy - timeoutInterval:[delegate heartbeatTimeout]]; - if (data != nil) { - DEBUGLOG(@"poll() %@", data); - [req setHTTPMethod:@"POST"]; - [req setValue:@"text/plain; charset=UTF-8" forHTTPHeaderField:@"Content-Type"]; - [req setHTTPBody:[data dataUsingEncoding:NSUTF8StringEncoding]]; - } - [req setValue:@"Keep-Alive" forHTTPHeaderField:@"Connection"]; - - NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:req delegate:self]; - - // add pollData to polls dictionary - NSMutableDictionary *pollData = [[NSMutableDictionary alloc] init]; - [pollData setObject:[NSNumber numberWithInt:retry] forKey:@"retries"]; - [pollData setObject:conn forKey:@"connection"]; - [pollData setValue:data forKey:@"data"]; - [_polls setObject:pollData forKey:conn.description]; - - [conn start]; -} - - -#pragma mark - -#pragma mark NSURLConnection delegate methods - - -- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response -{ - [_data setLength:0]; -} - -- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data -{ - DEBUGLOG(@"didReceiveData(): %@", data); - [_data appendData:data]; -} - -- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error -{ - DEBUGLOG(@"didFailWithError: %@", [error localizedDescription]); - - // retry 3 times or throw error - NSMutableDictionary *pollData = [_polls objectForKey:connection.description]; - NSString *data = [pollData objectForKey:@"data"]; - [_polls removeObjectForKey:connection.description]; - - NSNumber *retries = [pollData objectForKey:@"retries"]; - if ([retries intValue] < 2) { - [self poll:data retryNumber:[retries intValue] + 1]; - } - else { - NSMutableDictionary *errorInfo = [[NSMutableDictionary alloc] init]; - [errorInfo setValue:[error localizedDescription] forKey:@"reason"]; - [errorInfo setValue:data forKey:@"data"]; - - if ([delegate respondsToSelector:@selector(onError:)]) { - [delegate onError:[NSError errorWithDomain:SocketIOError - code:SocketIODataCouldNotBeSend - userInfo:errorInfo]]; - } - } -} - -- (void) connectionDidFinishLoading:(NSURLConnection *)connection -{ - NSString *message = [[NSString alloc] initWithData:_data encoding:NSUTF8StringEncoding]; - DEBUGLOG(@"response: __%@__", message); - - if (![message isEqualToString:@"1"]) { - [delegate onData:message]; - } - - // remove current connection from pool - [_polls removeObjectForKey:connection.description]; - - [self checkAndStartPoll]; -} - - -@end diff --git a/PomeloChat/SocketRocket/NSData+SRB64Additions.h b/PomeloChat/SocketRocket/NSData+SRB64Additions.h deleted file mode 100644 index 115108d..0000000 --- a/PomeloChat/SocketRocket/NSData+SRB64Additions.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// Copyright 2012 Square Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import - -@interface NSData (SRB64Additions) - -- (NSString *)SR_stringByBase64Encoding; - -@end diff --git a/PomeloChat/SocketRocket/NSData+SRB64Additions.m b/PomeloChat/SocketRocket/NSData+SRB64Additions.m deleted file mode 100644 index 71bae3b..0000000 --- a/PomeloChat/SocketRocket/NSData+SRB64Additions.m +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright 2012 Square Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "NSData+SRB64Additions.h" -#import "base64.h" - -@implementation NSData (SRB64Additions) - -- (NSString *)SR_stringByBase64Encoding; -{ - size_t buffer_size = (([self length] * 3 + 2) / 2); - - char *buffer = (char *)malloc(buffer_size); - - int len = b64_ntop([self bytes], [self length], buffer, buffer_size); - - if (len == -1) { - free(buffer); - return nil; - } else{ - return [[NSString alloc] initWithBytesNoCopy:buffer length:len encoding:NSUTF8StringEncoding freeWhenDone:YES]; - } -} - -@end diff --git a/PomeloChat/SocketRocket/SRWebSocket.h b/PomeloChat/SocketRocket/SRWebSocket.h deleted file mode 100644 index a7e1851..0000000 --- a/PomeloChat/SocketRocket/SRWebSocket.h +++ /dev/null @@ -1,90 +0,0 @@ -// -// Copyright 2012 Square Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import -#import - -typedef enum { - SR_CONNECTING = 0, - SR_OPEN = 1, - SR_CLOSING = 2, - SR_CLOSED = 3, - -} SRReadyState; - -@class SRWebSocket; - -extern NSString *const SRWebSocketErrorDomain; - -@protocol SRWebSocketDelegate; - -@interface SRWebSocket : NSObject - -@property (nonatomic, assign) id delegate; - -@property (nonatomic, readonly) SRReadyState readyState; -@property (nonatomic, readonly, retain) NSURL *url; - -// This returns the negotiated protocol. -// It will be niluntil after the handshake completes. -@property (nonatomic, readonly, copy) NSString *protocol; - -// Protocols should be an array of strings that turn into Sec-WebSocket-Protocol -- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols; -- (id)initWithURLRequest:(NSURLRequest *)request; - -// Some helper constructors -- (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols; -- (id)initWithURL:(NSURL *)url; - -// SRWebSockets are intended one-time-use only. Open should be called once and only once -- (void)open; - -- (void)close; -- (void)closeWithCode:(NSInteger)code reason:(NSString *)reason; - -// Send a UTF8 String or Data -- (void)send:(id)data; - -@end - -@protocol SRWebSocketDelegate - -// message will either be an NSString if the server is using text -// or NSData if the server is using binary -- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message; - -@optional - -- (void)webSocketDidOpen:(SRWebSocket *)webSocket; -- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error; -- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean; - -@end - - -@interface NSURLRequest (CertificateAdditions) - -@property (nonatomic, retain, readonly) NSArray *SR_SSLPinnedCertificates; - -@end - - -@interface NSMutableURLRequest (CertificateAdditions) - -@property (nonatomic, retain) NSArray *SR_SSLPinnedCertificates; - -@end diff --git a/PomeloChat/SocketRocket/SRWebSocket.m b/PomeloChat/SocketRocket/SRWebSocket.m deleted file mode 100644 index ef7c65c..0000000 --- a/PomeloChat/SocketRocket/SRWebSocket.m +++ /dev/null @@ -1,1553 +0,0 @@ -// -// Copyright 2012 Square Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - - -#import "SRWebSocket.h" - -#if TARGET_OS_IPHONE -#define HAS_ICU -#endif - -#ifdef HAS_ICU -#import -#endif - -#if TARGET_OS_IPHONE -#import -#else -#import -#endif - -#import -#import -#import "base64.h" -#import "NSData+SRB64Additions.h" - -typedef enum { - SROpCodeTextFrame = 0x1, - SROpCodeBinaryFrame = 0x2, - //3-7Reserved - SROpCodeConnectionClose = 0x8, - SROpCodePing = 0x9, - SROpCodePong = 0xA, - //B-F reserved -} SROpCode; - -typedef enum { - SRStatusCodeNormal = 1000, - SRStatusCodeGoingAway = 1001, - SRStatusCodeProtocolError = 1002, - SRStatusCodeUnhandledType = 1003, - // 1004 reserved - SRStatusNoStatusReceived = 1005, - // 1004-1006 reserved - SRStatusCodeInvalidUTF8 = 1007, - SRStatusCodePolicyViolated = 1008, - SRStatusCodeMessageTooBig = 1009, -} SRStatusCode; - -typedef struct { - BOOL fin; -// BOOL rsv1; -// BOOL rsv2; -// BOOL rsv3; - uint8_t opcode; - BOOL masked; - uint64_t payload_length; -} frame_header; - -static NSString *const SRWebSocketAppendToSecKeyString = @"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - -static inline int32_t validate_dispatch_data_partial_string(NSData *data); -static inline dispatch_queue_t log_queue(); -static inline void SRFastLog(NSString *format, ...); - -@interface NSData (SRWebSocket) - -- (NSString *)stringBySHA1ThenBase64Encoding; - -@end - - -@interface NSString (SRWebSocket) - -- (NSString *)stringBySHA1ThenBase64Encoding; - -@end - - -@interface NSURL (SRWebSocket) - -// The origin isn't really applicable for a native application -// So instead, just map ws -> http and wss -> https -- (NSString *)SR_origin; - -@end - - -static NSString *newSHA1String(const char *bytes, size_t length) { - uint8_t md[CC_SHA1_DIGEST_LENGTH]; - - CC_SHA1(bytes, length, md); - - size_t buffer_size = ((sizeof(md) * 3 + 2) / 2); - - char *buffer = (char *)malloc(buffer_size); - - int len = b64_ntop(md, CC_SHA1_DIGEST_LENGTH, buffer, buffer_size); - if (len == -1) { - free(buffer); - return nil; - } else{ - return [[NSString alloc] initWithBytesNoCopy:buffer length:len encoding:NSASCIIStringEncoding freeWhenDone:YES]; - } -} - -@implementation NSData (SRWebSocket) - -- (NSString *)stringBySHA1ThenBase64Encoding; -{ - return newSHA1String(self.bytes, self.length); -} - -@end - - -@implementation NSString (SRWebSocket) - -- (NSString *)stringBySHA1ThenBase64Encoding; -{ - return newSHA1String(self.UTF8String, self.length); -} - -@end - -NSString *const SRWebSocketErrorDomain = @"SRWebSocketErrorDomain"; - -// Returns number of bytes consumed. returning 0 means you didn't match. -// Sends bytes to callback handler; -typedef size_t (^stream_scanner)(NSData *collected_data); - -typedef void (^data_callback)(SRWebSocket *webSocket, NSData *data); - -@interface SRIOConsumer : NSObject { - stream_scanner _scanner; - data_callback _handler; - size_t _bytesNeeded; - BOOL _readToCurrentFrame; - BOOL _unmaskBytes; -} - -- (id)initWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; - -@property (nonatomic, copy, readonly) stream_scanner consumer; -@property (nonatomic, copy, readonly) data_callback handler; -@property (nonatomic, assign) size_t bytesNeeded; -@property (nonatomic, assign, readonly) BOOL readToCurrentFrame; -@property (nonatomic, assign, readonly) BOOL unmaskBytes; - -@end - - -@interface SRWebSocket () - -- (void)_writeData:(NSData *)data; -- (void)_closeWithProtocolError:(NSString *)message; -- (void)_failWithError:(NSError *)error; - -- (void)_disconnect; - -- (void)_readFrameNew; -- (void)_readFrameContinue; - -- (void)_pumpScanner; - -- (void)_pumpWriting; - -- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback; -- (void)_addConsumerWithDataLength:(size_t)dataLength callback:(data_callback)callback readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; -- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback dataLength:(size_t)dataLength; -- (void)_readUntilBytes:(const void *)bytes length:(size_t)length callback:(data_callback)dataHandler; -- (void)_readUntilHeaderCompleteWithCallback:(data_callback)dataHandler; - -- (void)_sendFrameWithOpcode:(SROpCode)opcode data:(id)data; - -- (BOOL)_checkHandshake:(CFHTTPMessageRef)httpMessage; -- (void)_SR_commonInit; - -- (void)_connectToHost:(NSString *)host port:(NSInteger)port; - -@property (nonatomic) SRReadyState readyState; - -@end - - -@implementation SRWebSocket { - NSInteger _webSocketVersion; - dispatch_queue_t _callbackQueue; - dispatch_queue_t _workQueue; - NSMutableArray *_consumers; - - NSInputStream *_inputStream; - NSOutputStream *_outputStream; - - NSMutableData *_readBuffer; - NSInteger _readBufferOffset; - - NSMutableData *_outputBuffer; - NSInteger _outputBufferOffset; - - uint8_t _currentFrameOpcode; - size_t _currentFrameCount; - size_t _readOpCount; - uint32_t _currentStringScanPosition; - NSMutableData *_currentFrameData; - - NSString *_closeReason; - - NSString *_secKey; - - BOOL _pinnedCertFound; - - uint8_t _currentReadMaskKey[4]; - size_t _currentReadMaskOffset; - - BOOL _consumerStopped; - - BOOL _closeWhenFinishedWriting; - BOOL _failed; - - BOOL _secure; - NSURLRequest *_urlRequest; - - CFHTTPMessageRef _receivedHTTPHeaders; - - BOOL _sentClose; - BOOL _didFail; - int _closeCode; - - BOOL _isPumping; - - // We use this to retain ourselves. - __strong SRWebSocket *_selfRetain; - - NSArray *_requestedProtocols; -} - -@synthesize delegate = _delegate; -@synthesize url = _url; -@synthesize readyState = _readyState; -@synthesize protocol = _protocol; - -static __strong NSData *CRLFCRLF; - -+ (void)initialize; -{ - CRLFCRLF = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4]; -} - -- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols; -{ - self = [super init]; - if (self) { - assert(request.URL); - _url = request.URL; - NSString *scheme = [_url scheme]; - - _requestedProtocols = [protocols copy]; - - assert([scheme isEqualToString:@"ws"] || [scheme isEqualToString:@"http"] || [scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]); - _urlRequest = request; - - if ([scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]) { - _secure = YES; - } - - [self _SR_commonInit]; - } - - return self; -} - -- (id)initWithURLRequest:(NSURLRequest *)request; -{ - return [self initWithURLRequest:request protocols:nil]; -} - -- (id)initWithURL:(NSURL *)url; -{ - return [self initWithURL:url protocols:nil]; -} - -- (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols; -{ - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; - return [self initWithURLRequest:request protocols:protocols]; -} - -- (void)_SR_commonInit; -{ - _readyState = SR_CONNECTING; - - _consumerStopped = YES; - - _webSocketVersion = 13; - - _workQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); - - _callbackQueue = dispatch_get_main_queue(); - dispatch_retain(_callbackQueue); - - _readBuffer = [[NSMutableData alloc] init]; - _outputBuffer = [[NSMutableData alloc] init]; - - _currentFrameData = [[NSMutableData alloc] init]; - - _consumers = [[NSMutableArray alloc] init]; - - // default handlers -} - -- (void)dealloc -{ - _inputStream.delegate = nil; - _outputStream.delegate = nil; - - [_inputStream close]; - [_outputStream close]; - - dispatch_release(_callbackQueue); - dispatch_release(_workQueue); - - if (_receivedHTTPHeaders) { - CFRelease(_receivedHTTPHeaders); - _receivedHTTPHeaders = NULL; - } -} - -#ifndef NDEBUG - -- (void)setReadyState:(SRReadyState)aReadyState; -{ - [self willChangeValueForKey:@"readyState"]; - assert(aReadyState > _readyState); - _readyState = aReadyState; - [self didChangeValueForKey:@"readyState"]; -} - -#endif - -- (void)open; -{ - assert(_url); - NSAssert(_readyState == SR_CONNECTING && _inputStream == nil && _outputStream == nil, @"Cannot call -(void)open on SRWebSocket more than once"); - - _selfRetain = self; - - NSInteger port = _url.port.integerValue; - if (port == 0) { - if (!_secure) { - port = 80; - } else { - port = 443; - } - } - - [self _connectToHost:_url.host port:port]; -} - - - -- (BOOL)_checkHandshake:(CFHTTPMessageRef)httpMessage; -{ - NSString *acceptHeader = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(httpMessage, CFSTR("Sec-WebSocket-Accept"))); - - if (acceptHeader == nil) { - return NO; - } - - NSString *concattedString = [_secKey stringByAppendingString:SRWebSocketAppendToSecKeyString]; - NSString *expectedAccept = [concattedString stringBySHA1ThenBase64Encoding]; - - return [acceptHeader isEqualToString:expectedAccept]; -} - -- (void)_HTTPHeadersDidFinish; -{ - NSInteger responseCode = CFHTTPMessageGetResponseStatusCode(_receivedHTTPHeaders); - - if (responseCode >= 400) { - SRFastLog(@"Request failed with response code %d", responseCode); - [self _failWithError:[NSError errorWithDomain:@"org.lolrus.SocketRocket" code:2132 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"received bad response code from server %d", responseCode] forKey:NSLocalizedDescriptionKey]]]; - return; - - } - - if(![self _checkHandshake:_receivedHTTPHeaders]) { - [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2133 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Invalid Sec-WebSocket-Accept response"] forKey:NSLocalizedDescriptionKey]]]; - return; - } - - NSString *negotiatedProtocol = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(_receivedHTTPHeaders, CFSTR("Sec-WebSocket-Protocol"))); - if (negotiatedProtocol) { - // Make sure we requested the protocol - if ([_requestedProtocols indexOfObject:negotiatedProtocol] == NSNotFound) { - [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2133 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Server specified Sec-WebSocket-Protocol that wasn't requested"] forKey:NSLocalizedDescriptionKey]]]; - return; - } - - _protocol = negotiatedProtocol; - } - - self.readyState = SR_OPEN; - - if (!_didFail) { - [self _readFrameNew]; - } - - dispatch_async(_callbackQueue, ^{ - if ([self.delegate respondsToSelector:@selector(webSocketDidOpen:)]) { - [self.delegate webSocketDidOpen:self]; - } - }); -} - - -- (void)_readHTTPHeader; -{ - if (_receivedHTTPHeaders == NULL) { - _receivedHTTPHeaders = CFHTTPMessageCreateEmpty(NULL, NO); - } - - [self _readUntilHeaderCompleteWithCallback:^(SRWebSocket *self, NSData *data) { - CFHTTPMessageAppendBytes(_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length); - - if (CFHTTPMessageIsHeaderComplete(_receivedHTTPHeaders)) { - SRFastLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_receivedHTTPHeaders))); - [self _HTTPHeadersDidFinish]; - } else { - [self _readHTTPHeader]; - } - }]; -} - -- (void)didConnect -{ - SRFastLog(@"Connected"); - CFHTTPMessageRef request = CFHTTPMessageCreateRequest(NULL, CFSTR("GET"), (__bridge CFURLRef)_url, kCFHTTPVersion1_1); - - // Set host first so it defaults - CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Host"), (__bridge CFStringRef)(_url.port ? [NSString stringWithFormat:@"%@:%@", _url.host, _url.port] : _url.host)); - - NSMutableData *keyBytes = [[NSMutableData alloc] initWithLength:16]; - SecRandomCopyBytes(kSecRandomDefault, keyBytes.length, keyBytes.mutableBytes); - _secKey = [keyBytes SR_stringByBase64Encoding]; - assert([_secKey length] == 24); - - CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Upgrade"), CFSTR("websocket")); - CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Connection"), CFSTR("Upgrade")); - CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Key"), (__bridge CFStringRef)_secKey); - CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Version"), (__bridge CFStringRef)[NSString stringWithFormat:@"%d", _webSocketVersion]); - - CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Origin"), (__bridge CFStringRef)_url.SR_origin); - - if (_requestedProtocols) { - CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Protocol"), (__bridge CFStringRef)[_requestedProtocols componentsJoinedByString:@", "]); - } - - [_urlRequest.allHTTPHeaderFields enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { - CFHTTPMessageSetHeaderFieldValue(request, (__bridge CFStringRef)key, (__bridge CFStringRef)obj); - }]; - - NSData *message = CFBridgingRelease(CFHTTPMessageCopySerializedMessage(request)); - - CFRelease(request); - - [self _writeData:message]; - [self _readHTTPHeader]; -} - -- (void)_connectToHost:(NSString *)host port:(NSInteger)port; -{ - CFReadStreamRef readStream = NULL; - CFWriteStreamRef writeStream = NULL; - - CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &readStream, &writeStream); - - _outputStream = CFBridgingRelease(writeStream); - _inputStream = CFBridgingRelease(readStream); - - - if (_secure) { - NSMutableDictionary *SSLOptions = [[NSMutableDictionary alloc] init]; - - [_outputStream setProperty:(__bridge id)kCFStreamSocketSecurityLevelNegotiatedSSL forKey:(__bridge id)kCFStreamPropertySocketSecurityLevel]; - - // If we're using pinned certs, don't validate the certificate chain - if ([_urlRequest SR_SSLPinnedCertificates].count) { - [SSLOptions setValue:[NSNumber numberWithBool:NO] forKey:(__bridge id)kCFStreamSSLValidatesCertificateChain]; - } - - #if DEBUG - [SSLOptions setValue:[NSNumber numberWithBool:NO] forKey:(__bridge id)kCFStreamSSLValidatesCertificateChain]; - NSLog(@"SocketRocket: In debug mode. Allowing connection to any root cert"); - #endif - - [_outputStream setProperty:SSLOptions - forKey:(__bridge id)kCFStreamPropertySSLSettings]; - } - - _inputStream.delegate = self; - _outputStream.delegate = self; - - // TODO schedule in a better run loop - [_outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; - [_inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; - - - [_outputStream open]; - [_inputStream open]; -} - -- (void)close; -{ - [self closeWithCode:-1 reason:nil]; -} - -- (void)closeWithCode:(NSInteger)code reason:(NSString *)reason; -{ - assert(code); - if (self.readyState == SR_CLOSING || self.readyState == SR_CLOSED) { - return; - } - - BOOL wasConnecting = self.readyState == SR_CONNECTING; - - self.readyState = SR_CLOSING; - - SRFastLog(@"Closing with code %d reason %@", code, reason); - dispatch_async(_workQueue, ^{ - if (wasConnecting) { - [self _disconnect]; - return; - } - - size_t maxMsgSize = [reason maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding]; - NSMutableData *mutablePayload = [[NSMutableData alloc] initWithLength:sizeof(uint16_t) + maxMsgSize]; - NSData *payload = mutablePayload; - - ((uint16_t *)mutablePayload.mutableBytes)[0] = EndianU16_BtoN(code); - - if (reason) { - NSRange remainingRange = {0}; - - NSUInteger usedLength = 0; - - BOOL success = [reason getBytes:(char *)mutablePayload.mutableBytes + sizeof(uint16_t) maxLength:payload.length - sizeof(uint16_t) usedLength:&usedLength encoding:NSUTF8StringEncoding options:NSStringEncodingConversionExternalRepresentation range:NSMakeRange(0, reason.length) remainingRange:&remainingRange]; - - assert(success); - assert(remainingRange.length == 0); - - if (usedLength != maxMsgSize) { - payload = [payload subdataWithRange:NSMakeRange(0, usedLength + sizeof(uint16_t))]; - } - } - - - [self _sendFrameWithOpcode:SROpCodeConnectionClose data:payload]; - }); -} - -- (void)_closeWithProtocolError:(NSString *)message; -{ - // Need to shunt this on the _callbackQueue first to see if they received any messages - dispatch_async(_callbackQueue, ^{ - [self closeWithCode:SRStatusCodeProtocolError reason:message]; - dispatch_async(_workQueue, ^{ - [self _disconnect]; - }); - }); -} - -- (void)_failWithError:(NSError *)error; -{ - dispatch_async(_workQueue, ^{ - if (self.readyState != SR_CLOSED) { - _failed = YES; - dispatch_async(_callbackQueue, ^{ - if ([self.delegate respondsToSelector:@selector(webSocket:didFailWithError:)]) { - [self.delegate webSocket:self didFailWithError:error]; - } - }); - - self.readyState = SR_CLOSED; - _selfRetain = nil; - - SRFastLog(@"Failing with error %@", error.localizedDescription); - - [self _disconnect]; - } - }); -} - -- (void)_writeData:(NSData *)data; -{ - assert(dispatch_get_current_queue() == _workQueue); - - if (_closeWhenFinishedWriting) { - return; - } - [_outputBuffer appendData:data]; - [self _pumpWriting]; -} -- (void)send:(id)data; -{ - NSAssert(self.readyState != SR_CONNECTING, @"Invalid State: Cannot call send: until connection is open"); - // TODO: maybe not copy this for performance - data = [data copy]; - dispatch_async(_workQueue, ^{ - if ([data isKindOfClass:[NSString class]]) { - [self _sendFrameWithOpcode:SROpCodeTextFrame data:[(NSString *)data dataUsingEncoding:NSUTF8StringEncoding]]; - } else if ([data isKindOfClass:[NSData class]]) { - [self _sendFrameWithOpcode:SROpCodeBinaryFrame data:data]; - } else if (data == nil) { - [self _sendFrameWithOpcode:SROpCodeTextFrame data:data]; - } else { - assert(NO); - } - }); -} - -- (void)handlePing:(NSData *)pingData; -{ - // Need to pingpong this off _callbackQueue first to make sure messages happen in order - dispatch_async(_callbackQueue, ^{ - dispatch_async(_workQueue, ^{ - [self _sendFrameWithOpcode:SROpCodePong data:pingData]; - }); - }); -} - -- (void)handlePong; -{ - // NOOP -} - -- (void)_handleMessage:(id)message -{ - SRFastLog(@"Received message"); - dispatch_async(_callbackQueue, ^{ - [self.delegate webSocket:self didReceiveMessage:message]; - }); -} - - -static inline BOOL closeCodeIsValid(int closeCode) { - if (closeCode < 1000) { - return NO; - } - - if (closeCode >= 1000 && closeCode <= 1011) { - if (closeCode == 1004 || - closeCode == 1005 || - closeCode == 1006) { - return NO; - } - return YES; - } - - if (closeCode >= 3000 && closeCode <= 3999) { - return YES; - } - - if (closeCode >= 4000 && closeCode <= 4999) { - return YES; - } - - return NO; -} - -// Note from RFC: -// -// If there is a body, the first two -// bytes of the body MUST be a 2-byte unsigned integer (in network byte -// order) representing a status code with value /code/ defined in -// Section 7.4. Following the 2-byte integer the body MAY contain UTF-8 -// encoded data with value /reason/, the interpretation of which is not -// defined by this specification. - -- (void)handleCloseWithData:(NSData *)data; -{ - size_t dataSize = data.length; - __block uint16_t closeCode = 0; - - SRFastLog(@"Received close frame"); - - if (dataSize == 1) { - // TODO handle error - [self _closeWithProtocolError:@"Payload for close must be larger than 2 bytes"]; - return; - } else if (dataSize >= 2) { - [data getBytes:&closeCode length:sizeof(closeCode)]; - _closeCode = EndianU16_BtoN(closeCode); - if (!closeCodeIsValid(_closeCode)) { - [self _closeWithProtocolError:[NSString stringWithFormat:@"Cannot have close code of %d", _closeCode]]; - return; - } - if (dataSize > 2) { - _closeReason = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(2, dataSize - 2)] encoding:NSUTF8StringEncoding]; - if (!_closeReason) { - [self _closeWithProtocolError:@"Close reason MUST be valid UTF-8"]; - return; - } - } - } else { - _closeCode = SRStatusNoStatusReceived; - } - - assert(dispatch_get_current_queue() == _workQueue); - - if (self.readyState == SR_OPEN) { - [self closeWithCode:1000 reason:nil]; - } - dispatch_async(_workQueue, ^{ - [self _disconnect]; - }); -} - -- (void)_disconnect; -{ - assert(dispatch_get_current_queue() == _workQueue); - SRFastLog(@"Trying to disconnect"); - _closeWhenFinishedWriting = YES; - [self _pumpWriting]; -} - -- (void)_handleFrameWithData:(NSData *)frameData opCode:(NSInteger)opcode; -{ - // Check that the current data is valid UTF8 - - BOOL isControlFrame = (opcode == SROpCodePing || opcode == SROpCodePong || opcode == SROpCodeConnectionClose); - if (!isControlFrame) { - [self _readFrameNew]; - } else { - dispatch_async(_workQueue, ^{ - [self _readFrameContinue]; - }); - } - - switch (opcode) { - case SROpCodeTextFrame: { - NSString *str = [[NSString alloc] initWithData:frameData encoding:NSUTF8StringEncoding]; - if (str == nil && frameData) { - [self closeWithCode:SRStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"]; - dispatch_async(_workQueue, ^{ - [self _disconnect]; - }); - - return; - } - [self _handleMessage:str]; - break; - } - case SROpCodeBinaryFrame: - [self _handleMessage:[frameData copy]]; - break; - case SROpCodeConnectionClose: - [self handleCloseWithData:frameData]; - break; - case SROpCodePing: - [self handlePing:frameData]; - break; - case SROpCodePong: - [self handlePong]; - break; - default: - [self _closeWithProtocolError:[NSString stringWithFormat:@"Unknown opcode %d", opcode]]; - // TODO: Handle invalid opcode - break; - } -} - -- (void)_handleFrameHeader:(frame_header)frame_header curData:(NSData *)curData; -{ - assert(frame_header.opcode != 0); - - if (self.readyState != SR_OPEN) { - return; - } - - - BOOL isControlFrame = (frame_header.opcode == SROpCodePing || frame_header.opcode == SROpCodePong || frame_header.opcode == SROpCodeConnectionClose); - - if (isControlFrame && !frame_header.fin) { - [self _closeWithProtocolError:@"Fragmented control frames not allowed"]; - return; - } - - if (isControlFrame && frame_header.payload_length >= 126) { - [self _closeWithProtocolError:@"Control frames cannot have payloads larger than 126 bytes"]; - return; - } - - if (!isControlFrame) { - _currentFrameOpcode = frame_header.opcode; - _currentFrameCount += 1; - } - - if (frame_header.payload_length == 0) { - if (isControlFrame) { - [self _handleFrameWithData:curData opCode:frame_header.opcode]; - } else { - if (frame_header.fin) { - [self _handleFrameWithData:_currentFrameData opCode:frame_header.opcode]; - } else { - // TODO add assert that opcode is not a control; - [self _readFrameContinue]; - } - } - } else { - [self _addConsumerWithDataLength:frame_header.payload_length callback:^(SRWebSocket *self, NSData *newData) { - if (isControlFrame) { - [self _handleFrameWithData:newData opCode:frame_header.opcode]; - } else { - if (frame_header.fin) { - [self _handleFrameWithData:self->_currentFrameData opCode:frame_header.opcode]; - } else { - // TODO add assert that opcode is not a control; - [self _readFrameContinue]; - } - - } - } readToCurrentFrame:!isControlFrame unmaskBytes:frame_header.masked]; - } -} - -/* From RFC: - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-------+-+-------------+-------------------------------+ - |F|R|R|R| opcode|M| Payload len | Extended payload length | - |I|S|S|S| (4) |A| (7) | (16/64) | - |N|V|V|V| |S| | (if payload len==126/127) | - | |1|2|3| |K| | | - +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + - | Extended payload length continued, if payload len == 127 | - + - - - - - - - - - - - - - - - +-------------------------------+ - | |Masking-key, if MASK set to 1 | - +-------------------------------+-------------------------------+ - | Masking-key (continued) | Payload Data | - +-------------------------------- - - - - - - - - - - - - - - - + - : Payload Data continued ... : - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - | Payload Data continued ... | - +---------------------------------------------------------------+ - */ - -static const uint8_t SRFinMask = 0x80; -static const uint8_t SROpCodeMask = 0x0F; -static const uint8_t SRRsvMask = 0x70; -static const uint8_t SRMaskMask = 0x80; -static const uint8_t SRPayloadLenMask = 0x7F; - - -- (void)_readFrameContinue; -{ - assert((_currentFrameCount == 0 && _currentFrameOpcode == 0) || (_currentFrameCount > 0 && _currentFrameOpcode > 0)); - - [self _addConsumerWithDataLength:2 callback:^(SRWebSocket *self, NSData *data) { - __block frame_header header = {0}; - - const uint8_t *headerBuffer = data.bytes; - assert(data.length >= 2); - - if (headerBuffer[0] & SRRsvMask) { - [self _closeWithProtocolError:@"Server used RSV bits"]; - return; - } - - uint8_t receivedOpcode = (SROpCodeMask & headerBuffer[0]); - - BOOL isControlFrame = (receivedOpcode == SROpCodePing || receivedOpcode == SROpCodePong || receivedOpcode == SROpCodeConnectionClose); - - if (!isControlFrame && receivedOpcode != 0 && self->_currentFrameCount > 0) { - [self _closeWithProtocolError:@"all data frames after the initial data frame must have opcode 0"]; - return; - } - - if (receivedOpcode == 0 && self->_currentFrameCount == 0) { - [self _closeWithProtocolError:@"cannot continue a message"]; - return; - } - - header.opcode = receivedOpcode == 0 ? self->_currentFrameOpcode : receivedOpcode; - - header.fin = !!(SRFinMask & headerBuffer[0]); - - - header.masked = !!(SRMaskMask & headerBuffer[1]); - header.payload_length = SRPayloadLenMask & headerBuffer[1]; - - headerBuffer = NULL; - - if (header.masked) { - [self _closeWithProtocolError:@"Client must receive unmasked data"]; - } - - size_t extra_bytes_needed = header.masked ? sizeof(_currentReadMaskKey) : 0; - - if (header.payload_length == 126) { - extra_bytes_needed += sizeof(uint16_t); - } else if (header.payload_length == 127) { - extra_bytes_needed += sizeof(uint64_t); - } - - if (extra_bytes_needed == 0) { - [self _handleFrameHeader:header curData:self->_currentFrameData]; - } else { - [self _addConsumerWithDataLength:extra_bytes_needed callback:^(SRWebSocket *self, NSData *data) { - size_t mapped_size = data.length; - const void *mapped_buffer = data.bytes; - size_t offset = 0; - - if (header.payload_length == 126) { - assert(mapped_size >= sizeof(uint16_t)); - uint16_t newLen = EndianU16_BtoN(*(uint16_t *)(mapped_buffer)); - header.payload_length = newLen; - offset += sizeof(uint16_t); - } else if (header.payload_length == 127) { - assert(mapped_size >= sizeof(uint64_t)); - header.payload_length = EndianU64_BtoN(*(uint64_t *)(mapped_buffer)); - offset += sizeof(uint64_t); - } else { - assert(header.payload_length < 126 && header.payload_length >= 0); - } - - - if (header.masked) { - assert(mapped_size >= sizeof(_currentReadMaskOffset) + offset); - memcpy(self->_currentReadMaskKey, ((uint8_t *)mapped_buffer) + offset, sizeof(self->_currentReadMaskKey)); - } - - [self _handleFrameHeader:header curData:self->_currentFrameData]; - } readToCurrentFrame:NO unmaskBytes:NO]; - } - } readToCurrentFrame:NO unmaskBytes:NO]; -} - -- (void)_readFrameNew; -{ - dispatch_async(_workQueue, ^{ - [_currentFrameData setLength:0]; - - _currentFrameOpcode = 0; - _currentFrameCount = 0; - _readOpCount = 0; - _currentStringScanPosition = 0; - - [self _readFrameContinue]; - }); -} - -- (void)_pumpWriting; -{ - assert(dispatch_get_current_queue() == _workQueue); - - NSUInteger dataLength = _outputBuffer.length; - if (dataLength - _outputBufferOffset > 0 && _outputStream.hasSpaceAvailable) { - NSUInteger bytesWritten = [_outputStream write:_outputBuffer.bytes + _outputBufferOffset maxLength:dataLength - _outputBufferOffset]; - if (bytesWritten == -1) { - [self _failWithError:[NSError errorWithDomain:@"org.lolrus.SocketRocket" code:2145 userInfo:[NSDictionary dictionaryWithObject:@"Error writing to stream" forKey:NSLocalizedDescriptionKey]]]; - return; - } - - _outputBufferOffset += bytesWritten; - - if (_outputBufferOffset > 4096 && _outputBufferOffset > (_outputBuffer.length >> 1)) { - _outputBuffer = [[NSMutableData alloc] initWithBytes:(char *)_outputBuffer.bytes + _outputBufferOffset length:_outputBuffer.length - _outputBufferOffset]; - _outputBufferOffset = 0; - } - } - - if (_closeWhenFinishedWriting && - _outputBuffer.length - _outputBufferOffset == 0 && - (_inputStream.streamStatus != NSStreamStatusNotOpen && - _inputStream.streamStatus != NSStreamStatusClosed) && - !_sentClose) { - _sentClose = YES; - - [_outputStream close]; - [_inputStream close]; - - if (!_failed) { - dispatch_async(_callbackQueue, ^{ - if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) { - [self.delegate webSocket:self didCloseWithCode:_closeCode reason:_closeReason wasClean:YES]; - } - }); - } - - _selfRetain = nil; - } -} - -- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback; -{ - assert(dispatch_get_current_queue() == _workQueue); - [self _addConsumerWithScanner:consumer callback:callback dataLength:0]; -} - -- (void)_addConsumerWithDataLength:(size_t)dataLength callback:(data_callback)callback readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; -{ - assert(dispatch_get_current_queue() == _workQueue); - assert(dataLength); - - [_consumers addObject:[[SRIOConsumer alloc] initWithScanner:nil handler:callback bytesNeeded:dataLength readToCurrentFrame:readToCurrentFrame unmaskBytes:unmaskBytes]]; - [self _pumpScanner]; -} - -- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback dataLength:(size_t)dataLength; -{ - assert(dispatch_get_current_queue() == _workQueue); - [_consumers addObject:[[SRIOConsumer alloc] initWithScanner:consumer handler:callback bytesNeeded:dataLength readToCurrentFrame:NO unmaskBytes:NO]]; - [self _pumpScanner]; -} - - -static const char CRLFCRLFBytes[] = {'\r', '\n', '\r', '\n'}; - -- (void)_readUntilHeaderCompleteWithCallback:(data_callback)dataHandler; -{ - [self _readUntilBytes:CRLFCRLFBytes length:sizeof(CRLFCRLFBytes) callback:dataHandler]; -} - -- (void)_readUntilBytes:(const void *)bytes length:(size_t)length callback:(data_callback)dataHandler; -{ - // TODO optimize so this can continue from where we last searched - stream_scanner consumer = ^size_t(NSData *data) { - __block size_t found_size = 0; - __block size_t match_count = 0; - - size_t size = data.length; - const unsigned char *buffer = data.bytes; - for (int i = 0; i < size; i++ ) { - if (((const unsigned char *)buffer)[i] == ((const unsigned char *)bytes)[match_count]) { - match_count += 1; - if (match_count == length) { - found_size = i + 1; - break; - } - } else { - match_count = 0; - } - } - return found_size; - }; - [self _addConsumerWithScanner:consumer callback:dataHandler]; -} - - -// Returns true if did work -- (BOOL)_innerPumpScanner { - - BOOL didWork = NO; - - if (self.readyState >= SR_CLOSING) { - return didWork; - } - - if (!_consumers.count) { - return didWork; - } - - size_t curSize = _readBuffer.length - _readBufferOffset; - if (!curSize) { - return didWork; - } - - SRIOConsumer *consumer = [_consumers objectAtIndex:0]; - - size_t bytesNeeded = consumer.bytesNeeded; - - size_t foundSize = 0; - if (consumer.consumer) { - NSData *tempView = [NSData dataWithBytesNoCopy:(char *)_readBuffer.bytes + _readBufferOffset length:_readBuffer.length - _readBufferOffset freeWhenDone:NO]; - foundSize = consumer.consumer(tempView); - } else { - assert(consumer.bytesNeeded); - if (curSize >= bytesNeeded) { - foundSize = bytesNeeded; - } else if (consumer.readToCurrentFrame) { - foundSize = curSize; - } - } - - NSData *slice = nil; - if (consumer.readToCurrentFrame || foundSize) { - NSRange sliceRange = NSMakeRange(_readBufferOffset, foundSize); - slice = [_readBuffer subdataWithRange:sliceRange]; - - _readBufferOffset += foundSize; - - if (_readBufferOffset > 4096 && _readBufferOffset > (_readBuffer.length >> 1)) { - _readBuffer = [[NSMutableData alloc] initWithBytes:(char *)_readBuffer.bytes + _readBufferOffset length:_readBuffer.length - _readBufferOffset]; _readBufferOffset = 0; - } - - if (consumer.unmaskBytes) { - NSMutableData *mutableSlice = [slice mutableCopy]; - - NSUInteger len = mutableSlice.length; - uint8_t *bytes = mutableSlice.mutableBytes; - - for (int i = 0; i < len; i++) { - bytes[i] = bytes[i] ^ _currentReadMaskKey[_currentReadMaskOffset % sizeof(_currentReadMaskKey)]; - _currentReadMaskOffset += 1; - } - - slice = mutableSlice; - } - - if (consumer.readToCurrentFrame) { - [_currentFrameData appendData:slice]; - - _readOpCount += 1; - - if (_currentFrameOpcode == SROpCodeTextFrame) { - // Validate UTF8 stuff. - size_t currentDataSize = _currentFrameData.length; - if (_currentFrameOpcode == SROpCodeTextFrame && currentDataSize > 0) { - // TODO: Optimize the crap out of this. Don't really have to copy all the data each time - - size_t scanSize = currentDataSize - _currentStringScanPosition; - - NSData *scan_data = [_currentFrameData subdataWithRange:NSMakeRange(_currentStringScanPosition, scanSize)]; - int32_t valid_utf8_size = validate_dispatch_data_partial_string(scan_data); - - if (valid_utf8_size == -1) { - [self closeWithCode:SRStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"]; - dispatch_async(_workQueue, ^{ - [self _disconnect]; - }); - return didWork; - } else { - _currentStringScanPosition += valid_utf8_size; - } - } - - } - - consumer.bytesNeeded -= foundSize; - - if (consumer.bytesNeeded == 0) { - [_consumers removeObjectAtIndex:0]; - consumer.handler(self, nil); - didWork = YES; - } - } else if (foundSize) { - [_consumers removeObjectAtIndex:0]; - consumer.handler(self, slice); - didWork = YES; - } - } - return didWork; -} - --(void)_pumpScanner; -{ - assert(dispatch_get_current_queue() == _workQueue); - - if (!_isPumping) { - _isPumping = YES; - } else { - return; - } - - while ([self _innerPumpScanner]) { - - } - - _isPumping = NO; -} - -//#define NOMASK - -static const size_t SRFrameHeaderOverhead = 32; - -- (void)_sendFrameWithOpcode:(SROpCode)opcode data:(id)data; -{ - assert(dispatch_get_current_queue() == _workQueue); - - NSAssert(data == nil || [data isKindOfClass:[NSData class]] || [data isKindOfClass:[NSString class]], @"Function expects nil, NSString or NSData"); - - size_t payloadLength = [data isKindOfClass:[NSString class]] ? [(NSString *)data lengthOfBytesUsingEncoding:NSUTF8StringEncoding] : [data length]; - - NSMutableData *frame = [[NSMutableData alloc] initWithLength:payloadLength + SRFrameHeaderOverhead]; - if (!frame) { - [self closeWithCode:SRStatusCodeMessageTooBig reason:@"Message too big"]; - return; - } - uint8_t *frame_buffer = (uint8_t *)[frame mutableBytes]; - - // set fin - frame_buffer[0] = SRFinMask | opcode; - - BOOL useMask = YES; -#ifdef NOMASK - useMask = NO; -#endif - - if (useMask) { - // set the mask and header - frame_buffer[1] |= SRMaskMask; - } - - size_t frame_buffer_size = 2; - - const uint8_t *unmasked_payload = NULL; - if ([data isKindOfClass:[NSData class]]) { - unmasked_payload = (uint8_t *)[data bytes]; - } else if ([data isKindOfClass:[NSString class]]) { - unmasked_payload = (const uint8_t *)[data UTF8String]; - } else { - assert(NO); - } - - if (payloadLength < 126) { - frame_buffer[1] |= payloadLength; - } else if (payloadLength <= UINT16_MAX) { - frame_buffer[1] |= 126; - *((uint16_t *)(frame_buffer + frame_buffer_size)) = EndianU16_BtoN((uint16_t)payloadLength); - frame_buffer_size += sizeof(uint16_t); - } else { - frame_buffer[1] |= 127; - *((uint64_t *)(frame_buffer + frame_buffer_size)) = EndianU64_BtoN((uint64_t)payloadLength); - frame_buffer_size += sizeof(uint64_t); - } - - if (!useMask) { - for (int i = 0; i < payloadLength; i++) { - frame_buffer[frame_buffer_size] = unmasked_payload[i]; - frame_buffer_size += 1; - } - } else { - uint8_t *mask_key = frame_buffer + frame_buffer_size; - SecRandomCopyBytes(kSecRandomDefault, sizeof(uint32_t), (uint8_t *)mask_key); - frame_buffer_size += sizeof(uint32_t); - - // TODO: could probably optimize this with SIMD - for (int i = 0; i < payloadLength; i++) { - frame_buffer[frame_buffer_size] = unmasked_payload[i] ^ mask_key[i % sizeof(uint32_t)]; - frame_buffer_size += 1; - } - } - - assert(frame_buffer_size <= [frame length]); - frame.length = frame_buffer_size; - - [self _writeData:frame]; -} - -- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode; -{ - if (_secure && !_pinnedCertFound && (eventCode == NSStreamEventHasBytesAvailable || eventCode == NSStreamEventHasSpaceAvailable)) { - - NSArray *sslCerts = [_urlRequest SR_SSLPinnedCertificates]; - if (sslCerts) { - SecTrustRef secTrust = (__bridge SecTrustRef)[aStream propertyForKey:(__bridge id)kCFStreamPropertySSLPeerTrust]; - if (secTrust) { - NSInteger numCerts = SecTrustGetCertificateCount(secTrust); - for (NSInteger i = 0; i < numCerts && !_pinnedCertFound; i++) { - SecCertificateRef cert = SecTrustGetCertificateAtIndex(secTrust, i); - NSData *certData = CFBridgingRelease(SecCertificateCopyData(cert)); - - for (id ref in sslCerts) { - SecCertificateRef trustedCert = (__bridge SecCertificateRef)ref; - NSData *trustedCertData = CFBridgingRelease(SecCertificateCopyData(trustedCert)); - - if ([trustedCertData isEqualToData:certData]) { - _pinnedCertFound = YES; - break; - } - } - } - } - - if (!_pinnedCertFound) { - dispatch_async(_workQueue, ^{ - [self _failWithError:[NSError errorWithDomain:@"org.lolrus.SocketRocket" code:23556 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Invalid server cert"] forKey:NSLocalizedDescriptionKey]]]; - }); - return; - } - } - } - - // SRFastLog(@"%@ Got stream event %d", aStream, eventCode); - dispatch_async(_workQueue, ^{ - switch (eventCode) { - case NSStreamEventOpenCompleted: { - SRFastLog(@"NSStreamEventOpenCompleted %@", aStream); - if (self.readyState >= SR_CLOSING) { - return; - } - - - assert(_readBuffer); - - if (self.readyState == SR_CONNECTING && aStream == _inputStream) { - [self didConnect]; - } - [self _pumpWriting]; - [self _pumpScanner]; - break; - } - - case NSStreamEventErrorOccurred: { - SRFastLog(@"NSStreamEventErrorOccurred %@ %@", aStream, [[aStream streamError] copy]); - /// TODO specify error better! - [self _failWithError:aStream.streamError]; - _readBufferOffset = 0; - [_readBuffer setLength:0]; - break; - - } - - case NSStreamEventEndEncountered: { - [self _pumpScanner]; - SRFastLog(@"NSStreamEventEndEncountered %@", aStream); - if (aStream.streamError) { - [self _failWithError:aStream.streamError]; - } else { - if (self.readyState != SR_CLOSED) { - self.readyState = SR_CLOSED; - _selfRetain = nil; - } - - if (!_sentClose && !_failed) { - _sentClose = YES; - // If we get closed in this state it's probably not clean because we should be sending this when we send messages - dispatch_async(_callbackQueue, ^{ - if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) { - [self.delegate webSocket:self didCloseWithCode:0 reason:@"Stream end encountered" wasClean:NO]; - } - }); - } - } - - break; - } - - case NSStreamEventHasBytesAvailable: { - SRFastLog(@"NSStreamEventHasBytesAvailable %@", aStream); - const int bufferSize = 2048; - uint8_t buffer[bufferSize]; - - while (_inputStream.hasBytesAvailable) { - int bytes_read = [_inputStream read:buffer maxLength:bufferSize]; - - if (bytes_read > 0) { - [_readBuffer appendBytes:buffer length:bytes_read]; - } else if (bytes_read < 0) { - [self _failWithError:_inputStream.streamError]; - } - - if (bytes_read != bufferSize) { - break; - } - }; - [self _pumpScanner]; - break; - } - - case NSStreamEventHasSpaceAvailable: { - SRFastLog(@"NSStreamEventHasSpaceAvailable %@", aStream); - [self _pumpWriting]; - break; - } - - default: - SRFastLog(@"(default) %@", aStream); - break; - } - }); - -} - -@end - - -@implementation SRIOConsumer - -@synthesize bytesNeeded = _bytesNeeded; -@synthesize consumer = _scanner; -@synthesize handler = _handler; -@synthesize readToCurrentFrame = _readToCurrentFrame; -@synthesize unmaskBytes = _unmaskBytes; - -- (id)initWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; -{ - self = [super init]; - if (self) { - _scanner = [scanner copy]; - _handler = [handler copy]; - _bytesNeeded = bytesNeeded; - _readToCurrentFrame = readToCurrentFrame; - _unmaskBytes = unmaskBytes; - assert(_scanner || _bytesNeeded); - } - return self; -} - -@end - - -@implementation NSURLRequest (CertificateAdditions) - -- (NSArray *)SR_SSLPinnedCertificates; -{ - return [NSURLProtocol propertyForKey:@"SR_SSLPinnedCertificates" inRequest:self]; -} - -@end - -@implementation NSMutableURLRequest (CertificateAdditions) - -- (NSArray *)SR_SSLPinnedCertificates; -{ - return [NSURLProtocol propertyForKey:@"SR_SSLPinnedCertificates" inRequest:self]; -} - -- (void)setSR_SSLPinnedCertificates:(NSArray *)SR_SSLPinnedCertificates; -{ - [NSURLProtocol setProperty:SR_SSLPinnedCertificates forKey:@"SR_SSLPinnedCertificates" inRequest:self]; -} - -@end - -@implementation NSURL (SRWebSocket) - -- (NSString *)SR_origin; -{ - NSString *scheme = [self.scheme lowercaseString]; - - if ([scheme isEqualToString:@"wss"]) { - scheme = @"https"; - } else if ([scheme isEqualToString:@"ws"]) { - scheme = @"http"; - } - - if (self.port) { - return [NSString stringWithFormat:@"%@://%@:%@/", scheme, self.host, self.port]; - } else { - return [NSString stringWithFormat:@"%@://%@/", scheme, self.host]; - } -} - -@end - -static inline dispatch_queue_t log_queue() { - - static dispatch_queue_t queue = 0; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - queue = dispatch_queue_create("fast log queue", DISPATCH_QUEUE_SERIAL); - }); - - return queue; -} - -//#define SR_ENABLE_LOG - -static inline void SRFastLog(NSString *format, ...) { -#ifdef SR_ENABLE_LOG - __block va_list arg_list; - va_start (arg_list, format); - - NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list]; - - va_end(arg_list); - - NSLog(@"[SR] %@", formattedString); -#endif -} - - -#ifdef HAS_ICU - -static inline int32_t validate_dispatch_data_partial_string(NSData *data) { - - const void * contents = [data bytes]; - long size = [data length]; - - const uint8_t *str = (const uint8_t *)contents; - - - UChar32 codepoint = 1; - int32_t offset = 0; - int32_t lastOffset = 0; - while(offset < size && codepoint > 0) { - lastOffset = offset; - U8_NEXT(str, offset, size, codepoint); - } - - if (codepoint == -1) { - // Check to see if the last byte is valid or whether it was just continuing - if (!U8_IS_LEAD(str[lastOffset]) || U8_COUNT_TRAIL_BYTES(str[lastOffset]) + lastOffset < (int32_t)size) { - - size = -1; - } else { - uint8_t leadByte = str[lastOffset]; - U8_MASK_LEAD_BYTE(leadByte, U8_COUNT_TRAIL_BYTES(leadByte)); - - for (int i = lastOffset + 1; i < offset; i++) { - - if (U8_IS_SINGLE(str[i]) || U8_IS_LEAD(str[i]) || !U8_IS_TRAIL(str[i])) { - size = -1; - } - } - - if (size != -1) { - size = lastOffset; - } - } - } - - if (size != -1 && ![[NSString alloc] initWithBytesNoCopy:(char *)[data bytes] length:size encoding:NSUTF8StringEncoding freeWhenDone:NO]) { - size = -1; - } - - return size; -} - -#else - -// This is a hack, and probably not optimal -static inline int32_t validate_dispatch_data_partial_string(NSData *data) { - static const int maxCodepointSize = 3; - - for (int i = 0; i < maxCodepointSize; i++) { - NSString *str = [[NSString alloc] initWithBytesNoCopy:(char *)data.bytes length:data.length - i encoding:NSUTF8StringEncoding freeWhenDone:NO]; - if (str) { - return data.length - i; - } - } - - return -1; -} - -#endif - - diff --git a/PomeloChat/SocketRocket/SocketRocket-Prefix.pch b/PomeloChat/SocketRocket/SocketRocket-Prefix.pch deleted file mode 100644 index 590f7f3..0000000 --- a/PomeloChat/SocketRocket/SocketRocket-Prefix.pch +++ /dev/null @@ -1,27 +0,0 @@ -// -// Copyright 2012 Square Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __OBJC__ - #import -#endif - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/PomeloChat/SocketRocket/base64.c b/PomeloChat/SocketRocket/base64.c deleted file mode 100644 index 7647385..0000000 --- a/PomeloChat/SocketRocket/base64.c +++ /dev/null @@ -1,314 +0,0 @@ -/* $OpenBSD: base64.c,v 1.5 2006/10/21 09:55:03 otto Exp $ */ - -/* - * Copyright (c) 1996 by Internet Software Consortium. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS - * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE - * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL - * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR - * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS - * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS - * SOFTWARE. - */ - -/* - * Portions Copyright (c) 1995 by International Business Machines, Inc. - * - * International Business Machines, Inc. (hereinafter called IBM) grants - * permission under its copyrights to use, copy, modify, and distribute this - * Software with or without fee, provided that the above copyright notice and - * all paragraphs of this notice appear in all copies, and that the name of IBM - * not be used in connection with the marketing of any product incorporating - * the Software or modifications thereof, without specific, written prior - * permission. - * - * To the extent it has a right to do so, IBM grants an immunity from suit - * under its patents, if any, for the use, sale or manufacture of products to - * the extent that such products are used for performing Domain Name System - * dynamic updates in TCP/IP networks by means of the Software. No immunity is - * granted for any product per se or for any other function of any product. - * - * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, - * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN - * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. - */ - -/* OPENBSD ORIGINAL: lib/libc/net/base64.c */ - - -#if (!defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP)) || (!defined(HAVE_B64_PTON) && !defined(HAVE___B64_PTON)) - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include "base64.h" - -static const char Base64[] = -"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -static const char Pad64 = '='; - -/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) - The following encoding technique is taken from RFC 1521 by Borenstein - and Freed. It is reproduced here in a slightly edited form for - convenience. - - A 65-character subset of US-ASCII is used, enabling 6 bits to be - represented per printable character. (The extra 65th character, "=", - is used to signify a special processing function.) - - The encoding process represents 24-bit groups of input bits as output - strings of 4 encoded characters. Proceeding from left to right, a - 24-bit input group is formed by concatenating 3 8-bit input groups. - These 24 bits are then treated as 4 concatenated 6-bit groups, each - of which is translated into a single digit in the base64 alphabet. - - Each 6-bit group is used as an index into an array of 64 printable - characters. The character referenced by the index is placed in the - output string. - - Table 1: The Base64 Alphabet - - Value Encoding Value Encoding Value Encoding Value Encoding - 0 A 17 R 34 i 51 z - 1 B 18 S 35 j 52 0 - 2 C 19 T 36 k 53 1 - 3 D 20 U 37 l 54 2 - 4 E 21 V 38 m 55 3 - 5 F 22 W 39 n 56 4 - 6 G 23 X 40 o 57 5 - 7 H 24 Y 41 p 58 6 - 8 I 25 Z 42 q 59 7 - 9 J 26 a 43 r 60 8 - 10 K 27 b 44 s 61 9 - 11 L 28 c 45 t 62 + - 12 M 29 d 46 u 63 / - 13 N 30 e 47 v - 14 O 31 f 48 w (pad) = - 15 P 32 g 49 x - 16 Q 33 h 50 y - - Special processing is performed if fewer than 24 bits are available - at the end of the data being encoded. A full encoding quantum is - always completed at the end of a quantity. When fewer than 24 input - bits are available in an input group, zero bits are added (on the - right) to form an integral number of 6-bit groups. Padding at the - end of the data is performed using the '=' character. - - Since all base64 input is an integral number of octets, only the - ------------------------------------------------- - following cases can arise: - - (1) the final quantum of encoding input is an integral - multiple of 24 bits; here, the final unit of encoded - output will be an integral multiple of 4 characters - with no "=" padding, - (2) the final quantum of encoding input is exactly 8 bits; - here, the final unit of encoded output will be two - characters followed by two "=" padding characters, or - (3) the final quantum of encoding input is exactly 16 bits; - here, the final unit of encoded output will be three - characters followed by one "=" padding character. - */ - -#if !defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP) -int -b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize) -{ - size_t datalength = 0; - u_char input[3]; - u_char output[4]; - u_int i; - - while (2 < srclength) { - input[0] = *src++; - input[1] = *src++; - input[2] = *src++; - srclength -= 3; - - output[0] = input[0] >> 2; - output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); - output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); - output[3] = input[2] & 0x3f; - - if (datalength + 4 > targsize) - return (-1); - target[datalength++] = Base64[output[0]]; - target[datalength++] = Base64[output[1]]; - target[datalength++] = Base64[output[2]]; - target[datalength++] = Base64[output[3]]; - } - - /* Now we worry about padding. */ - if (0 != srclength) { - /* Get what's left. */ - input[0] = input[1] = input[2] = '\0'; - for (i = 0; i < srclength; i++) - input[i] = *src++; - - output[0] = input[0] >> 2; - output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); - output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); - - if (datalength + 4 > targsize) - return (-1); - target[datalength++] = Base64[output[0]]; - target[datalength++] = Base64[output[1]]; - if (srclength == 1) - target[datalength++] = Pad64; - else - target[datalength++] = Base64[output[2]]; - target[datalength++] = Pad64; - } - if (datalength >= targsize) - return (-1); - target[datalength] = '\0'; /* Returned value doesn't count \0. */ - return (datalength); -} -#endif /* !defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP) */ - -#if !defined(HAVE_B64_PTON) && !defined(HAVE___B64_PTON) - -/* skips all whitespace anywhere. - converts characters, four at a time, starting at (or after) - src from base - 64 numbers into three 8 bit bytes in the target area. - it returns the number of data bytes stored at the target, or -1 on error. - */ - -int -b64_pton(char const *src, u_char *target, size_t targsize) -{ - u_int tarindex, state; - int ch; - char *pos; - - state = 0; - tarindex = 0; - - while ((ch = *src++) != '\0') { - if (isspace(ch)) /* Skip whitespace anywhere. */ - continue; - - if (ch == Pad64) - break; - - pos = strchr(Base64, ch); - if (pos == 0) /* A non-base64 character. */ - return (-1); - - switch (state) { - case 0: - if (target) { - if (tarindex >= targsize) - return (-1); - target[tarindex] = (pos - Base64) << 2; - } - state = 1; - break; - case 1: - if (target) { - if (tarindex + 1 >= targsize) - return (-1); - target[tarindex] |= (pos - Base64) >> 4; - target[tarindex+1] = ((pos - Base64) & 0x0f) - << 4 ; - } - tarindex++; - state = 2; - break; - case 2: - if (target) { - if (tarindex + 1 >= targsize) - return (-1); - target[tarindex] |= (pos - Base64) >> 2; - target[tarindex+1] = ((pos - Base64) & 0x03) - << 6; - } - tarindex++; - state = 3; - break; - case 3: - if (target) { - if (tarindex >= targsize) - return (-1); - target[tarindex] |= (pos - Base64); - } - tarindex++; - state = 0; - break; - } - } - - /* - * We are done decoding Base-64 chars. Let's see if we ended - * on a byte boundary, and/or with erroneous trailing characters. - */ - - if (ch == Pad64) { /* We got a pad char. */ - ch = *src++; /* Skip it, get next. */ - switch (state) { - case 0: /* Invalid = in first position */ - case 1: /* Invalid = in second position */ - return (-1); - - case 2: /* Valid, means one byte of info */ - /* Skip any number of spaces. */ - for (; ch != '\0'; ch = *src++) - if (!isspace(ch)) - break; - /* Make sure there is another trailing = sign. */ - if (ch != Pad64) - return (-1); - ch = *src++; /* Skip the = */ - /* Fall through to "single trailing =" case. */ - /* FALLTHROUGH */ - - case 3: /* Valid, means two bytes of info */ - /* - * We know this char is an =. Is there anything but - * whitespace after it? - */ - for (; ch != '\0'; ch = *src++) - if (!isspace(ch)) - return (-1); - - /* - * Now make sure for cases 2 and 3 that the "extra" - * bits that slopped past the last full byte were - * zeros. If we don't check them, they become a - * subliminal channel. - */ - if (target && target[tarindex] != 0) - return (-1); - } - } else { - /* - * We ended by seeing the end of the string. Make sure we - * have no partial bytes lying around. - */ - if (state != 0) - return (-1); - } - - return (tarindex); -} - -#endif /* !defined(HAVE_B64_PTON) && !defined(HAVE___B64_PTON) */ -#endif \ No newline at end of file diff --git a/PomeloChat/SocketRocket/base64.h b/PomeloChat/SocketRocket/base64.h deleted file mode 100644 index c345e2e..0000000 --- a/PomeloChat/SocketRocket/base64.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2012 Square Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - - -#ifndef SocketRocket_base64_h -#define SocketRocket_base64_h - -#include - -extern int -b64_ntop(u_char const *src, - size_t srclength, - char *target, - size_t targsize); - -extern int -b64_pton(char const *src, - u_char *target, - size_t targsize); - - -#endif diff --git a/README.md b/README.md index f53ef19..e65d0e4 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,29 @@ pomelo-ioschat ============== -A chat demo for pomelo [iOS client](https://github.com/NetEase/pomelo-iosclient) +A chat demo branch created by little2s based on pomelo [iOS client](https://github.com/NetEase/pomelo-iosclient) + +![image](https://github.com/little2s/pomelo-ioschat/blob/master/screenshorts/Screenshot1.png)   +![image](https://github.com/little2s/pomelo-ioschat/blob/master/screenshorts/Screenshot2.png) ## Install get the source code ```bash -git clone git://github.com/NetEase/pomelo-ioschat.git +git clone https://github.com/little2s/pomelo-ioschat.git ``` -open the project in your xCode (v4.3 +, iOS5 +) +then open the pomelo-ioschat dir and install the dependencies + +```bash +pod install +``` + +open the xcworkspace in your Xcode (v6.1+, iOS7+) ## Server the chat server [https://github.com/NetEase/chatofpomelo](https://github.com/NetEase/chatofpomelo) + +## Dependencies + +* [JSQMessagesViewController](http://www.jessesquires.com/JSQMessagesViewController/) diff --git a/screenshorts/Screenshot1.png b/screenshorts/Screenshot1.png new file mode 100644 index 0000000..025d421 Binary files /dev/null and b/screenshorts/Screenshot1.png differ diff --git a/screenshorts/Screenshot2.png b/screenshorts/Screenshot2.png new file mode 100644 index 0000000..6b215ec Binary files /dev/null and b/screenshorts/Screenshot2.png differ