From 5300dcd7a48f9ab13f32b6187519d20b99e54c98 Mon Sep 17 00:00:00 2001 From: Alexandre Colucci Date: Tue, 8 Jan 2019 13:42:53 +0100 Subject: [PATCH] AppleMail 1.2 source code --- LICENSE | 9 + README.md | 24 + Release/com.elgato.applemail.streamDeckPlugin | Bin 0 -> 143141 bytes Sources/AppleMail.xcodeproj/project.pbxproj | 516 ++++++ Sources/Common/ESDConnectionManager.h | 41 + Sources/Common/ESDConnectionManager.m | 348 ++++ Sources/Common/ESDEventsProtocol.h | 44 + Sources/Common/ESDSDKDefines.h | 152 ++ Sources/Common/ESDUtilities.h | 28 + Sources/Common/ESDUtilities.m | 87 + Sources/Common/main.m | 91 + Sources/MyStreamDeckPlugin.h | 39 + Sources/MyStreamDeckPlugin.m | 307 ++++ Sources/Vendor/SocketRocket/CONTRIBUTING.md | 33 + .../SocketRocket-iOS-Dynamic.xcconfig | 17 + .../Configurations/SocketRocket-iOS.xcconfig | 20 + .../SocketRocket-macOS.xcconfig | 17 + .../Configurations/SocketRocket-tvOS.xcconfig | 16 + .../SocketRocketTests-iOS.xcconfig | 19 + .../Configurations/TestChat-iOS.xcconfig | 19 + Sources/Vendor/SocketRocket/Gemfile | 4 + Sources/Vendor/SocketRocket/LICENSE | 30 + Sources/Vendor/SocketRocket/LICENSE-examples | 11 + Sources/Vendor/SocketRocket/Makefile | 30 + Sources/Vendor/SocketRocket/PATENTS | 33 + Sources/Vendor/SocketRocket/README.md | 213 +++ .../Vendor/SocketRocket/SocketRocket.podspec | 22 + .../SocketRocket.xcodeproj/project.pbxproj | 1347 ++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../SocketRocket-iOS-Dynamic.xcscheme | 80 + .../xcschemes/SocketRocket-iOS.xcscheme | 149 ++ .../xcschemes/SocketRocket-macOS.xcscheme | 80 + .../xcschemes/SocketRocket-tvOS.xcscheme | 80 + .../xcschemes/SocketRocketTests.xcscheme | 101 + .../xcshareddata/xcschemes/TestChat.xcscheme | 111 ++ .../Internal/Delegate/SRDelegateController.h | 67 + .../Internal/Delegate/SRDelegateController.m | 138 ++ .../Internal/IOConsumer/SRIOConsumer.h | 40 + .../Internal/IOConsumer/SRIOConsumer.m | 36 + .../Internal/IOConsumer/SRIOConsumerPool.h | 28 + .../Internal/IOConsumer/SRIOConsumerPool.m | 64 + .../Internal/NSRunLoop+SRWebSocketPrivate.h | 13 + .../NSURLRequest+SRWebSocketPrivate.h | 13 + .../Internal/Proxy/SRProxyConnect.h | 26 + .../Internal/Proxy/SRProxyConnect.m | 481 +++++ .../Internal/RunLoop/SRRunLoopThread.h | 24 + .../Internal/RunLoop/SRRunLoopThread.m | 83 + .../SocketRocket/Internal/SRConstants.h | 26 + .../SocketRocket/Internal/SRConstants.m | 19 + .../Security/SRPinningSecurityPolicy.h | 27 + .../Security/SRPinningSecurityPolicy.m | 73 + .../SocketRocket/Internal/Utilities/SRError.h | 20 + .../SocketRocket/Internal/Utilities/SRError.m | 42 + .../Internal/Utilities/SRHTTPConnectMessage.h | 20 + .../Internal/Utilities/SRHTTPConnectMessage.m | 79 + .../SocketRocket/Internal/Utilities/SRHash.h | 19 + .../SocketRocket/Internal/Utilities/SRHash.m | 43 + .../SocketRocket/Internal/Utilities/SRLog.h | 20 + .../SocketRocket/Internal/Utilities/SRLog.m | 33 + .../SocketRocket/Internal/Utilities/SRMutex.h | 22 + .../SocketRocket/Internal/Utilities/SRMutex.m | 47 + .../Internal/Utilities/SRRandom.h | 16 + .../Internal/Utilities/SRRandom.m | 26 + .../Internal/Utilities/SRSIMDHelpers.h | 19 + .../Internal/Utilities/SRSIMDHelpers.m | 73 + .../Internal/Utilities/SRURLUtilities.h | 26 + .../Internal/Utilities/SRURLUtilities.m | 72 + .../SocketRocket/NSRunLoop+SRWebSocket.h | 27 + .../SocketRocket/NSRunLoop+SRWebSocket.m | 27 + .../SocketRocket/NSURLRequest+SRWebSocket.h | 38 + .../SocketRocket/NSURLRequest+SRWebSocket.m | 42 + .../SocketRocket/Resources/Info.plist | 26 + .../SocketRocket/SRSecurityPolicy.h | 72 + .../SocketRocket/SRSecurityPolicy.m | 75 + .../SocketRocket/SocketRocket/SRWebSocket.h | 417 +++++ .../SocketRocket/SocketRocket/SRWebSocket.m | 1628 +++++++++++++++++ .../SocketRocket/SocketRocket/SocketRocket.h | 15 + .../SocketRocket/TestChat/TCAppDelegate.h | 16 + .../SocketRocket/TestChat/TCAppDelegate.m | 19 + .../Vendor/SocketRocket/TestChat/TCChatCell.h | 17 + .../Vendor/SocketRocket/TestChat/TCChatCell.m | 40 + .../SocketRocket/TestChat/TCViewController.h | 19 + .../SocketRocket/TestChat/TCViewController.m | 195 ++ .../SocketRocket/TestChat/TestChat-Info.plist | 42 + .../TestChat/en.lproj/Default-568h@2x.png | Bin 0 -> 18594 bytes .../TestChat/en.lproj/InfoPlist.strings | 2 + .../en.lproj/MainStoryboard.storyboard | 128 ++ Sources/Vendor/SocketRocket/TestChat/main.m | 19 + .../SocketRocket/TestChatServer/go/README | 3 + .../TestChatServer/go/chatroom.go | 70 + .../TestChatServer/py/chatroom.py | 86 + .../TestChatServer/static/.gitignore | 1 + .../TestChatServer/static/index.html | 38 + .../TestChatServer/static/proxy.js | 81 + .../TestSupport/autobahn_fuzzingserver.json | 7 + .../TestSupport/run_test_server.sh | 18 + .../SocketRocket/TestSupport/setup_env.sh | 36 + .../Tests/Operations/SRAutobahnOperation.h | 41 + .../Tests/Operations/SRAutobahnOperation.m | 131 ++ .../Tests/Operations/SRTWebSocketOperation.h | 29 + .../Tests/Operations/SRTWebSocketOperation.m | 91 + .../SocketRocket/Tests/Resources/Info.plist | 22 + .../Resources/autobahn_configuration.json | 15 + .../SocketRocket/Tests/SRAutobahnTests.m | 146 ++ .../Tests/Utilities/SRAutobahnUtilities.h | 41 + .../Tests/Utilities/SRAutobahnUtilities.m | 120 ++ .../com.elgato.applemail.sdPlugin/AppleMail | Bin 0 -> 150944 bytes .../MailBadgeIcon.png | Bin 0 -> 10274 bytes .../MailBadgeIcon@2x.png | Bin 0 -> 29264 bytes .../MailIcon.png | Bin 0 -> 11315 bytes .../MailIcon@2x.png | Bin 0 -> 36544 bytes .../NumberOfUnreadMails.scpt | Bin 0 -> 794 bytes .../OpenMail.scpt | Bin 0 -> 2826 bytes Sources/com.elgato.applemail.sdPlugin/de.json | 8 + Sources/com.elgato.applemail.sdPlugin/en.json | 8 + Sources/com.elgato.applemail.sdPlugin/es.json | 8 + Sources/com.elgato.applemail.sdPlugin/fr.json | 8 + Sources/com.elgato.applemail.sdPlugin/ja.json | 8 + Sources/com.elgato.applemail.sdPlugin/ko.json | 8 + .../manifest.json | 37 + .../com.elgato.applemail.sdPlugin/zh_CN.json | 8 + 122 files changed, 9801 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 Release/com.elgato.applemail.streamDeckPlugin create mode 100644 Sources/AppleMail.xcodeproj/project.pbxproj create mode 100644 Sources/Common/ESDConnectionManager.h create mode 100644 Sources/Common/ESDConnectionManager.m create mode 100644 Sources/Common/ESDEventsProtocol.h create mode 100644 Sources/Common/ESDSDKDefines.h create mode 100644 Sources/Common/ESDUtilities.h create mode 100644 Sources/Common/ESDUtilities.m create mode 100644 Sources/Common/main.m create mode 100644 Sources/MyStreamDeckPlugin.h create mode 100644 Sources/MyStreamDeckPlugin.m create mode 100755 Sources/Vendor/SocketRocket/CONTRIBUTING.md create mode 100755 Sources/Vendor/SocketRocket/Configurations/SocketRocket-iOS-Dynamic.xcconfig create mode 100755 Sources/Vendor/SocketRocket/Configurations/SocketRocket-iOS.xcconfig create mode 100755 Sources/Vendor/SocketRocket/Configurations/SocketRocket-macOS.xcconfig create mode 100755 Sources/Vendor/SocketRocket/Configurations/SocketRocket-tvOS.xcconfig create mode 100755 Sources/Vendor/SocketRocket/Configurations/SocketRocketTests-iOS.xcconfig create mode 100755 Sources/Vendor/SocketRocket/Configurations/TestChat-iOS.xcconfig create mode 100755 Sources/Vendor/SocketRocket/Gemfile create mode 100755 Sources/Vendor/SocketRocket/LICENSE create mode 100755 Sources/Vendor/SocketRocket/LICENSE-examples create mode 100755 Sources/Vendor/SocketRocket/Makefile create mode 100755 Sources/Vendor/SocketRocket/PATENTS create mode 100755 Sources/Vendor/SocketRocket/README.md create mode 100755 Sources/Vendor/SocketRocket/SocketRocket.podspec create mode 100755 Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/project.pbxproj create mode 100755 Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100755 Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100755 Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/xcshareddata/xcschemes/SocketRocket-iOS-Dynamic.xcscheme create mode 100755 Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/xcshareddata/xcschemes/SocketRocket-iOS.xcscheme create mode 100755 Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/xcshareddata/xcschemes/SocketRocket-macOS.xcscheme create mode 100755 Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/xcshareddata/xcschemes/SocketRocket-tvOS.xcscheme create mode 100755 Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/xcshareddata/xcschemes/SocketRocketTests.xcscheme create mode 100755 Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/xcshareddata/xcschemes/TestChat.xcscheme create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/Delegate/SRDelegateController.h create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/Delegate/SRDelegateController.m create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/IOConsumer/SRIOConsumer.h create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/IOConsumer/SRIOConsumer.m create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/IOConsumer/SRIOConsumerPool.h create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/IOConsumer/SRIOConsumerPool.m create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/NSRunLoop+SRWebSocketPrivate.h create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/NSURLRequest+SRWebSocketPrivate.h create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/Proxy/SRProxyConnect.h create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/Proxy/SRProxyConnect.m create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/RunLoop/SRRunLoopThread.h create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/RunLoop/SRRunLoopThread.m create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/SRConstants.h create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/SRConstants.m create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/Security/SRPinningSecurityPolicy.h create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/Security/SRPinningSecurityPolicy.m create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRError.h create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRError.m create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRHTTPConnectMessage.h create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRHTTPConnectMessage.m create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRHash.h create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRHash.m create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRLog.h create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRLog.m create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRMutex.h create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRMutex.m create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRRandom.h create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRRandom.m create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRSIMDHelpers.h create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRSIMDHelpers.m create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRURLUtilities.h create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRURLUtilities.m create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/NSRunLoop+SRWebSocket.h create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/NSRunLoop+SRWebSocket.m create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/NSURLRequest+SRWebSocket.h create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/NSURLRequest+SRWebSocket.m create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/Resources/Info.plist create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/SRSecurityPolicy.h create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/SRSecurityPolicy.m create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/SRWebSocket.h create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/SRWebSocket.m create mode 100755 Sources/Vendor/SocketRocket/SocketRocket/SocketRocket.h create mode 100755 Sources/Vendor/SocketRocket/TestChat/TCAppDelegate.h create mode 100755 Sources/Vendor/SocketRocket/TestChat/TCAppDelegate.m create mode 100755 Sources/Vendor/SocketRocket/TestChat/TCChatCell.h create mode 100755 Sources/Vendor/SocketRocket/TestChat/TCChatCell.m create mode 100755 Sources/Vendor/SocketRocket/TestChat/TCViewController.h create mode 100755 Sources/Vendor/SocketRocket/TestChat/TCViewController.m create mode 100755 Sources/Vendor/SocketRocket/TestChat/TestChat-Info.plist create mode 100755 Sources/Vendor/SocketRocket/TestChat/en.lproj/Default-568h@2x.png create mode 100755 Sources/Vendor/SocketRocket/TestChat/en.lproj/InfoPlist.strings create mode 100755 Sources/Vendor/SocketRocket/TestChat/en.lproj/MainStoryboard.storyboard create mode 100755 Sources/Vendor/SocketRocket/TestChat/main.m create mode 100755 Sources/Vendor/SocketRocket/TestChatServer/go/README create mode 100755 Sources/Vendor/SocketRocket/TestChatServer/go/chatroom.go create mode 100755 Sources/Vendor/SocketRocket/TestChatServer/py/chatroom.py create mode 100755 Sources/Vendor/SocketRocket/TestChatServer/static/.gitignore create mode 100755 Sources/Vendor/SocketRocket/TestChatServer/static/index.html create mode 100755 Sources/Vendor/SocketRocket/TestChatServer/static/proxy.js create mode 100755 Sources/Vendor/SocketRocket/TestSupport/autobahn_fuzzingserver.json create mode 100755 Sources/Vendor/SocketRocket/TestSupport/run_test_server.sh create mode 100755 Sources/Vendor/SocketRocket/TestSupport/setup_env.sh create mode 100755 Sources/Vendor/SocketRocket/Tests/Operations/SRAutobahnOperation.h create mode 100755 Sources/Vendor/SocketRocket/Tests/Operations/SRAutobahnOperation.m create mode 100755 Sources/Vendor/SocketRocket/Tests/Operations/SRTWebSocketOperation.h create mode 100755 Sources/Vendor/SocketRocket/Tests/Operations/SRTWebSocketOperation.m create mode 100755 Sources/Vendor/SocketRocket/Tests/Resources/Info.plist create mode 100755 Sources/Vendor/SocketRocket/Tests/Resources/autobahn_configuration.json create mode 100755 Sources/Vendor/SocketRocket/Tests/SRAutobahnTests.m create mode 100755 Sources/Vendor/SocketRocket/Tests/Utilities/SRAutobahnUtilities.h create mode 100755 Sources/Vendor/SocketRocket/Tests/Utilities/SRAutobahnUtilities.m create mode 100755 Sources/com.elgato.applemail.sdPlugin/AppleMail create mode 100644 Sources/com.elgato.applemail.sdPlugin/MailBadgeIcon.png create mode 100644 Sources/com.elgato.applemail.sdPlugin/MailBadgeIcon@2x.png create mode 100644 Sources/com.elgato.applemail.sdPlugin/MailIcon.png create mode 100644 Sources/com.elgato.applemail.sdPlugin/MailIcon@2x.png create mode 100644 Sources/com.elgato.applemail.sdPlugin/NumberOfUnreadMails.scpt create mode 100644 Sources/com.elgato.applemail.sdPlugin/OpenMail.scpt create mode 100644 Sources/com.elgato.applemail.sdPlugin/de.json create mode 100644 Sources/com.elgato.applemail.sdPlugin/en.json create mode 100644 Sources/com.elgato.applemail.sdPlugin/es.json create mode 100644 Sources/com.elgato.applemail.sdPlugin/fr.json create mode 100644 Sources/com.elgato.applemail.sdPlugin/ja.json create mode 100644 Sources/com.elgato.applemail.sdPlugin/ko.json create mode 100644 Sources/com.elgato.applemail.sdPlugin/manifest.json create mode 100644 Sources/com.elgato.applemail.sdPlugin/zh_CN.json diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6617491 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +The MIT License + +Copyright 2018 Corsair Memory, Inc + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e56d4e3 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ + +`AppleMail` is a sample plugin demonstrating the [Stream Deck SDK](https://developer.elgato.com/documentation/stream-deck/). + + +# Description + +`AppleMail` is a plugin that displays the number of unread emails in Apple Mail. Pressing on the key will launch Apple Mail. + +Features: + +- code written in Objective-C +- macOS only +- localized +- detects if Apple Mail is running + + +# Installation + +In the Release folder, you can find the file `com.elgato.applemail.streamDeckPlugin`. If you double-click this file on your machine, Stream Deck will install the plugin. + + +# Source code + +The Sources folder contains the source code of the plugin. diff --git a/Release/com.elgato.applemail.streamDeckPlugin b/Release/com.elgato.applemail.streamDeckPlugin new file mode 100644 index 0000000000000000000000000000000000000000..15ae1d5b0cd34f170bb77161077d727a76f199af GIT binary patch literal 143141 zcma&MLy#y8ux44dZQHhO+qP}nwr$(CZJW33s{UU`FM2T(Gnt#*_S7Ol=J3p+sG4F-WA?d$>p27${a9Q-CZ-+Ax<%yrkVU)}Xq|Mu(Zu6n!I z>s1%4q@Fr3Ofvp=aa>SO%jbRbcRnue%bVi0dGXFYa#QLEH6%a*AK>*K$f-L%Fj@UI z6o3KZ&n6;(CT9r%fKq;J8Jx~}YEnUFGH`cfXL2QIcR4W3KXc?)lVT%EO$pbec7Or}gK_xjlGy^<03U2UN3Nj2BG&ja}6qWR}AN1dD zav@{b@L>=_1bBFOH+VZYa&&5l@_+zRR(C_Zs{bzqE+IMw4 zt|hkq^D%utISLhwM||EtlG2=;TpbnzGBY&)rH}GU{;|a${^N_^dXAw4|Cz6Tbkr05 zYAKW__t8J^&?uv`nwUjEfR=NpC8Vr8h^>A@pD#zAkvf@cJF#C4>DK_UZ zZi8eq<@Y1xYaMd(&$zJg*z!LQmqze*{_%nfNchlOkf908*!{*H78S3d4^A}yWYpE zhsWjX@@c$tP%ADv6v7sQ?NzoOzv@QinGoMdYZ;N}b?sBAecS@Tu&}VVHI@GyRH(TM z+z-p;_s4oM(3dvmN8lU~nZy1a6^PvF%zbsgyu7S}TIb%>w{>@o?1oIz8yOorll8*v z>yI+1PA-wPO7v!b<{cjrIr;aG=>!q3lr_d{yZOX#OWylmQht-t)7OLhG4t~7rjhjb z&KtjlssJ?kz0xj}I^Dig6424rHJTrX4D$OqNJIV8Jyi*3%_!Zbrng9a^y+5TRcUHg zjS8bUvCBBi#b#m8&CN#eK6)HuFL>(~z`-&CP|Ts(+P$THV%R6o(}u6|hhq)Tf%_Xum+t{kgr zZ)d+bJQOtBN8gX8SjxW!42hK?t^BH4NG+X*DRsrwr7?jbT+n2l49XyRipkHD2n=Vi z5|PCx_^YaKAml_Vq$_qP$-6_BZ@db+hN`LnDF5cWKj9;V8#nYrqOBiRdf>zRB8WrL zrG3=>MFBL}23d3eoIJM@Ke-b>UWZk+o`6Wt;pJp?1t@wJa8g-u@rq*fs=?qdqU)q4 zD9>RKG~3g8r)A;;gX8kOg#b7W`e^Q$lSl9hh38DFO$mFi?jsT%K|LXXLhd|a834K*7*jdlI8jxjW5c!&6;JJFaP;N3)5#%D- z+VDjygTvzNwA2y>kh>%uB(sBk0O&@#qyDSgM*ID}71%qzr4|y8f<49Ws=AD5W=U*zCDTG;Fsij)iZo{a&|G$eydK$V3~_kh(#t5I-^l?Nu^=v%`Tq5 z?^iFxoW9y1Ql>3|%bIwAg>GPM3~g=Rb~6Pcj@8h@Ta#^iOf>E`FaY1im$n!zmWoBa zgm~m6og^tp6o#X|ct}C&VR%wvPAm8WNie{@>flI3!jotV3P2|g3{0_7a8Of8ri5yl zSLq@^eI8_oSnLk`N{V>^Ia=4~W8Y{fzpa=C9}z2mD@e zBj#8#vasf3*UNDAE`UH$9LnJt$g!H%3^TG!bi#?3F#~G=jHvMr{!tcNh=hX>@y$qz zhF>o6oOTg%bMqq+0ldQm=o`DJK?cAJlq4z2uRfoQr*Pke^Wa%`U9vkb_41G zj?Ktw?3FiwV08dc*r+eVr%i}~TZ@ppmYL~xKzY1P&$zDGbZ`>%3c`VO;eSSUrwBKF z5^K(qYwrS49*&F^A$%H7SkII*?2<(oiiIkSVmXE}uYTh_dmEf&|85fyJ?C7t3JH}g zMhV;l;Abo6+jRdXP4HvL!nhs45~i-DKox+YdIm;2d%izB)_(2or2MH_Kc+{1%?FSO zJ(pJF>tk1EXVYyY7KVG*td79tXC+JksQ3>KG`mcQ33j^VRBdtf(Y3uALL5l2#90ez zpLGxwd0qZSzY)adZFbD~n@0R>RJz+Pgp+8->#>{GPXzp*F0y4Z@eSUC#7{>*<~dAh z$9@=8$^33#7;))NIYsQ{MWv^8Tm_*wViYUmwgL)PPkYxh`!Vg=#5__drQc+MH_N$7b*dRBz{@5|TO*RPm}-P|#ZonJ z#|_?2_w9J>P~F-4#2t69@giz2xAqjQPC{Lv4UkA+Kuj6R2^`?B3J)Li#k3t^I%00v z0i|vjj*e;X@w`wALz4J|YXPB+jX5!=%-&T-ntxhROovc$3*T%cT9zT zTB?c-{~b)je}=RoEh#FpMQ^C{mef;TId@jwHy#RAm4_2Yi>Sb`uwxzP8y4p($iatNP93fktyx#h9R4*SQys28iCb zBFvn?;vOT``!Iw_FB)K3DUGg4lb|$`QLjWaUwc?dzsq}GGMovp$3%gQnS}O63eC+? zyB1`cwnZB#Hklq_^Y(s(g@OISyaH@Ga-T^x6+_0aK=d4HAA$4$1f8 zXW_^ufw$SSfI6@+Y=zlSN=avP_U&iB_WFiLVbX<2_r#Zwh&mg%B^3T*Jj5h8f<_1b z3rqdTQaZcM@ZA{7wlu&#W;H6a+Ca*|??glt?$!;uA0LfZOoRDbMBq^@Q#}`)!f8b6 zupt2Ef&QEM8iZ~CAv2RAVk3J{@LKAq^#AOfh!09osWeqd?~T=XBh^sI;$?hmw!!TI zinFjaA}kAys`Nr^B}JTW>N?6i|GodTff+klx+2XuMrO%!zG!&QY?!qvHD8(PGTo&4 zz%dIcLkcbmG%O{n3h`c|i(uiXKi(@CgMZ8w-m?!Zo#JKK7`}pB#REq&lfOv;k ziqbDPE4PtdzdrQIJuo^2Hi}tF@1=rshKD+XU|U;tpn!vXff*hB)li&{(!j5r5?MyB zpu|U+$;PyvYgJ!tCn`kB=i;u*@Nf}*nR}h&kA92(4&XfgZ8KG_@Hf7s4w~Vlb@e@4 zG7g;jd93mnPP|%br9sDDQAG-Ia&c4^#nX|bT4&kwq-?FW6HHB1f!$W}op6jYRJJ??FwT^kPsifL=?>j$2_L@<&56{g zHMIO0uYL~4N)df+shq$)XKzhVUZ<6utbsB+$GlB-MIMl z+~Bg$8FgheL$2aIPa59B0+Vrii=wP&hC3~?mlJ;#DF6j4WDW_}zOQfV*}z2#6%`{z zzCx}suGTF1U=c=a0`ey;d(0`_d?IgvV_t>uj8-tJk-%)^f$UCHth%E(Sh!7SsMvxt zi6wm=wql)X>8L>A3hLjnQoqh2%ROJ6GTh&`=vj38v_PI8X8H<@d26+ZlS|9;GfMeV zL4K@qdmEJB&G@mGjOuSVP9hI2QI93Iq;Ni5-n?fe3)0ft=!iZCX&M3-9^{-Ijy;2o zCWD!{zjTmb*&+Lxt1cOlg0EY2ETO{M?y@_5D6;Q1;jU4Xnxxz-E30eix|1U?diu0s zab*$OWv8D&AfcVGmzF}K+TO*@r%^q57Al)2w!+BVa_}8yHXi3x{0=)4kBxXXq++L*cheoQ8RhU6^8qw8{dt)eS6Q$;*{(_l9oe>!~#4tpX&AA`+we7yS&fY}i18=L4ffjJqw#4idZ;xz5O(iN+5vHv zD$bi|z8a9pyp)}2t zgK7StR|?KErlK}0t+t}|xQTXb+{nl^@>B6DjT*$hgjE0~4D(%uu|2e7+SK9-6FSVp zFgpNDW1gJ*3qgT8Uz{=@>&l2ID2j`htS-Y z^PtX3g(!Rpa&up~4Fp|+dP+$0Yzv_bhlO+kYF}YUK0e^~N&pMrk~nSg3@+}Egopc3 zbH{szYNM9Y0BA2LDQR)#;)CTsJ&E62z*9*|U6g!Kg7z6}9;UG1QJYCx=8%!Xm3u-s zMiz1u7I%>-pH3CwC!afCt27Xp3IyZ8P#}s!P2Za70Wug9ITw|dcB5OfS7Z%mIEg|E zYOUp6`_|H`?Pp7wyO`_m>*_$Fi(D8gvs=L*Vl_e= z1p!&xu0HHgWBD?`R8T@I+D`&AvwOB8%Rxd#Sg#Cp9a^-*)?0VmsU>*+D2#MxxjEzi zTe-!>a!8`7OE-F~P4`wvaYYik6=3(W75{{SR;Hvn18&}^+Q^8p0e)1DVZ(EsbS^y; zJu7Il%L+^aT-dxpL9P>tl+i&XR|zK5Jkt6ew(GD|K zA2fmGk+|~{`Xq_vRXR(w-;pU>9z zl&^t{cR^%h`#0ZH@3}3x6@NuwVm%Z_rXo~J-1kS^vguj_y zM$hd@sHf47p1iB6b0`v78=r)ZESu(laBO}X`v0luf~|FESW zfr%H$)G^}V@Dv}Tdoc|0q2r%TPS*boS9E}(FJg?%tniXp+`%dDiO5f>j8_FBgt&J# zyAiOHh}gHNE+^n2BKQ-v)0%)`WrT!0URLZ`cHGwd_SPRWpiQ&Nv_wAo_z>-^7H+7O zi*nwNP$he2jJf}Kl_*TY8CC~=y^z2iks)34mSpg29BwL{Ki@KwHsLNkb-;jOGb#v*UD#O(u{WTD$Yr&KK+ zwSt};p<;(n*vfhzC%0pnJo&h9Vcc`$xzla0nzBBdvV_Y*fMic8+^7J)8V9t(!r$JO zblcL2CHY!tU|d^io+u+F-V`*rgQMmL@g_^4 zjdX}mA)T%nJqFVoxThERLVGHe1q{5{$??KUpM)(d+cda{7I3}VboX=bb42VI;#TLd z0Qr6{BfXE1C5l2@A}NX6(;4SeQQ?^@64i8!VYO>Mm5^+FGDVW=pm7bbtMd&JqAb_Y(6m+D7kj6p#iT5-QWjK-r&8_&rhVczPMHAKnrMUY&b7eswDh zSfcexTubEGHs4eXQhRF2l9+v`=J;S@F3rn9I(~lvNerr`5EOWRLn>6fbq&1cm{$u` zqxzCr1?im+mxDX|s3JmcWcoy245MmKhBOj2-srOgc#8%s8^k(T9<)eX%-0^$51VW$ zrs=M#gGF^P!6J0QXt}GH&o<_0q%ajI-t)48PQG{>qy}@71}|s2NU88y@s%v&=+|%Qlay5YbDJDvgK@*5&2o z{2-Ak{wG)#<&?F==q6Un%ROC}_Uma4fKd3Pw?{mjxl+o0KU=sQK)#9H01p#DWm6=T>2^|S+=P3^94UcV{1qP zS@1fz$QSw5m_GII!b2_lr>|k@^7WE73-@~sM>O_fPVtUtD~&q4TTqz9KC(EbqpXq{ z44oGvZ!pL~Z4YcaMuQzUF5o8eb`4ENqlVEh`$$)0Bi@UzpXlF*Xvu&KPTZ%X;p<)p z->8S`M|WIJHm^b9_mQSq$%$v-$BD<{nM|3P7#FRv)3d9a=3estu-Tv1xYxh%C2*Gf zFa`Pa#@j2MP&@076%*v$3sU4QFS1kB^S(0WQU?vN%aGS8E*S(O%D5Ffl4jsDQU*dr zN2(Js2HSx-#p$Bp`QDVNdWm@S%dlXz%H z>3do!@}dnX4^{g3fuwHO+4i!l-FJSB(C< zY?IhwvrBgNipTz}krK8WN*Gr~pCL_sbU^Y+BolIsH$FTdW*!Q6`&H)xo;3aU6zA1P zl>|^>afZw=cZ5A>-;`S^{+kaGoc0dkLprGsRvNHK=4g!xDU}63#(H4~$MBS0T2!$8 zJcSubNshVM-ZZ`Q4_Fc=)$!^=ZBdYJGTx|V>vFmBs;M$~opVhyOF;%2%Jm0Hl##r{-9 zw(j|-G7=k$Qp)unlN5e+nZeT^V}&0w^np$vb}`*g+q&>+QwDkW^r6KLqslgza#=6x zh#-(1?xwy-pS`eX#nMXjydz{WF_b8n-2+$}FR*yJINFgwmL%@wE{=i2VfpZR zW}WC znmuY5P+KVb`(;);tJe4&TMNNAP<-o3iOKl^&=kRa;huoWrZ`l~5B?l1W^=SWtzEgM z4<*Bf#i)rMVQwt=VT$`@Vu*y6|2{I_H^+P~{chSKN-*yc4V>qP9eU{8%b=?FmdD9B z(;W9wPZa!*8obWf)PCq-YDJ5+A(Fl`C3FHb^U8ZGaqPd>n(nrq8AQ_+f&dc5`sOh? zm#DKP*Q1whYiOCHdx2j}*@)H~;U{aQhG>sLtS^9pI7wHZitLdvSD|>3MjrT)ArHi? zibNK@EaVrC$xudVG!Y6Xf7FS{qxCrHG;eoUCYMUod{Brm+~^p04L+z} zlQc5nSy@?cjI7~LB3$Nbkf!TGnm~q3n(%iFB_UpYXEB-?Tjb7s@9e0*Mg>!Cx4|0D zpNY37*JLS+1pYl|&BX91tyFOnY6(GE#!n#GIkdtJP*pA~0hE7ACX7!+kJ0ts z*JokfozIi@87u*yokHh^cPVn;&P}1hM`0n3JOX4zOY>e8yQcUF3O&_U znFkA1PYR+#)`ZA;ypk1FOQoTI`b-qOEC|nho_wdZm`sl z95jbGNt7$_n8!8v^tkB&QR=Zv$ZL!TqO#4X3W-JIjjKdI2RwDQbH^i9kxD^Gp4#$3 z)#O~^fJ84Kca=w?go|rG^A@@<)`2g^+g!EJCQ_&FZ5#~Cs(VD?Rv}nTz-@kqK_4kC zg|(QQ%T=9z1YRQY3)U#?iHFc=IbmwnH6UrD?NQG~`R1sTj~|&&uu`b$57NPI5)qEQ zM!A5=6)RVCp|~5#wv9Uee2ETEu8xkuB8RyN$KJZo)Kg9E4{|b&V|d2-^Myc)ShsuCJ zXqaK3<%q7a?+}~O393+7T6c!zYIixwVQsDuG?4Cs_4=Sxt#&i=6|b{_>wY-m5$0j% zxj6>_#;QrRN^Z;v#2>OpdQ#mm+|+2IL3!!G?7Q*R0A~vED8>enYaiO(TZZz|FdT?tZamp)k{eW>$zcJrDlJQ~ zeTWD5Q$?kY9tC}%zC;qx^E9x4iaF{ryX35eZn?pizl3K^p-4M6;C+jfKt4Cv{jk&g zP;GB3KRLaQn+}xjEAI*(YMGO72M_mSWo=T@E-$n^D-^#ny?bXP4sb^@#FGFYvEiFP z;MwRnk3&ADS?%tXRn8^2c2Aio}S7E@_tOhZxml zc+&=fq47}^>z-i1!@u57+DnOj5-aa^L{>}(E+nDC=7fJsUPPI zn&y<_^MBCxj1k*=emsD>dgftlZP`H-al?oeAXOUAlHi*iS(vMm^EHi041y8}k~{7; zk{K!woBgVt_i?*O5tKIGnJ1PAxzek-|D#blO=vF=m*mYcP?$`2Zux+$kuNr7b(%;1 zR;iPciezT(ZDw`4t=w#xkwP~yRGZh=NbAwOQz?JUi=Sb-eTi#1F>f}u+81_HAU-)h zi@?Ui-v{>wZeRXwt@}ybGA)-w1U=OlX&RU(3C84+a3e10VK6{?-M6fC8q@|V|d#>~dg+puj@ z)Uv87Bm8c8Jj%%WJ9!P7-$UL9SRXzf4nCoN4q@FFZY-m%7qvTZogS1h`-ZbFx zKdZ3DZSp9hHAugUYt$+gri>fbCB0x6?T7!q#T7(12l|)vLDpO z7Eu!559@%-hbu^cQrN;$MbZr-CEuZq-P-?qi@O8s-?{Q8t|8<3 zEll@@2QsWSYHdW!UA}zY8E1y+_?KZ(@jT>gZ3S5Xl6y6Ki@&k$>QnXChrvI!*Owdy zy0{+fiHEOeai6C=>JY8zlqh-}ST_$qOfkn3GCv~>1Ea(VmT9&S&$cj)%=F;;i4TcR zH;Q8qCciG}h!4IzFjamBS?DbJ;u@s#a82d3R89+!Wib>o+xV6q4DrtJC}=e^ap=t% z;tHm6H|v}>4g^4l2F>5G^VO!iK_d}c+x%eoV1YyAlj!zgdf@8e@$NG)90cF0!Z!V& z`_}H_9&fg%TCdwEzF;DGO|LipWm=x~Iv!}=#a<_u+U5G%(NEL9hjH1KF7GoB-Ff$A z%3g9@N_YvP&DJ*9n~LwOhXuh>hWw7+SmjKlr1FEswxT*Vb{=x; z)I&A!L@cI1SfPs^++~Be?MfQjO?e%T3oTTh){k^t^sgf6_yK}nWl~Z1tM8RFr{I|1 zqlcR*#&DkHaSMJJ(|CK99ZTnODHx~pDbkl$hxp-IaiGWtinJ4u-~v*%hY+zr z8E0)x>=OO#7mvvyL*dBD7d;ea`rjt^ria65KPKcLaRgPli^rt>;-IxDRLg$NKq zH)V>B0P>{Mpp@7o@6>5mvHf+8rNUOl=p1?sXcWIfl(C`3>=n$`%O4QvbBAOusN~Q4 zZ>pd%UbmpN-)=D8#BPp%@|PNmo{Hu*i$!LwDF55D*W-qPr|sq}-!MHkex3f-k>S2( z$V#opk8=4_;?K=dE8zO@b;!6NmX6Y~hoj=UAxy(rDDgQn_W4i>I!M!t$1@xQoRy$W zLz${-b(QL@`GIQIn{@P!-QH_yualYGEOXPlirQ?b1f3w+_D3(8)kk0^Xm)(#NY-qD+*UGv&9j+i+OqC zXd{uLo*`b}r`U7rs`sJK1bMS`mb@)KZ0^QOl=j#j3W#PO{k#T!JO9$~j@saSpw__w zjiPhL+7U6xln~%MRsfEYl*(tgV_;lUf$W*gH9k*HfZsDzS>8Ck6hX|cqj`8bkWK4n zI;FP;G+(>&Ou4fq@VtD0leK^NvXCr6cMJN|_K@ar?m!h>h~XZ{Y; z4?vy&{QLfT(eD1t8UJ)C-~Ljl0wK?S`M1deE7!(tLIMav3uwq|ufsidA``8P(#Lik zq4d5SQa?r8Wy+;OZ>8XL|3hm?Xu|tM*xKp_a85Lizu!Bho%fH2Bcxt+1!qVB_?tq4 zFbPBv1N7-cC(Fy!e~-{P|J>>7kM6B?4@$U<3Cq{S4l)hC4)NzFRmYpl`lZEJXWOOY z%%9Wo|3mlJH0v)7+(tsM9`mT4$mOp&gY-1Z`i1p-JrLP1=kCfp=cv5$Zfv-l^!c8O zvV=;5Z^mYTGcq!*o0^)x*6dGa=C$g{1&JN$?rHJ6Cs*~eo59KS&nk4b;HRA9iykbk z)w}up%TF4xE@g?!sJZU%d9`f%7)~RPx=erXhr{97j?|UIB(_mcT83~k1E^@rprG0= za=2P=i4kJd!2G_SAVfVKzyfXUnY#WeqC3`SQ8o%m$@%&G-&m5I*-e&R?nY9Q3I)bv zyn2u&w0ww}CXyBP(O#p6w~HRBtt~~Vb^JD`WHL%%Vd3HQ^Exfto13oF=40c>cZ=i! zw4sa&aA%j$MQ~MBa|j1hM2ZE=bprNaLW_bOth!5dpgg?I1p-K=hii00Ll8T%ACH)E z{}k9{9*ig?w$fcf_m_5mG<8A#Jvtj23N`@4qTQ(#CZ0Q@-W3Cq0@aiKvyoEUI^@xEe> zD^s{P=N4t+q2t>}Rq}HG1{1jpgth-Y7?^}~>$#aFZvIg3{%aGdzF!%6e>lk-@1@gM z8wxlTQ&L)@&U^4XzPh^VuZ%>FRwaQpdXDIC3;nuBo<7IE+wH=;XQRvTC8^(4o{w@M zu7|2iM3D2(*M1C>sZ+lb*%KkUWv%(F^M!JS-SV1V6$ZM3wmS0d5C=uPU(}%+qMAtG z;>i?D?qFWOz(Djo8Y&P+_{|r*J5Zi)@>ld1udb>R$TV?^oUka~fjgodWYYbl>pRbp#LLa~8tX@BbNOqN8Bt*mvumlS8kc6>-;1>ho z^@aI&@%hPR{B#6I^BSQOdnBtu<+b%=|2Kj9qoWfdk6x%*sH+RvlH4yc`1A9V&&pm( zDR_^n`dW{v)f%eI{&TsGbD`m76L!2)<4B|VKsZH{zrK3c^Zv+hZ(qUQ9?%x1*K~Aqe7j&*q=G$G@As>>4tEjlB zqZkjV^rjVWwaxo);2dJQZjUH{YyTA$vi;(&tywamz9Aq0t#SDG@>2TfTanyPPi*-s zD-9nWzK$@o^~%jh*b~d!XxDnT$dhWze2KY416xjrvzeQ3i1Yx~apn!WS5BkIWjgJ# zB%cg=PI}IjAD$_d9HDQZynGM~4!{mVQ~z#}^XWUfaj=c-ddrW}|0w&bx%|>1(pt)= za?^n)k~@pJpHP8ue99egQaFpfY9qOqwPQmNz3htwX z6Xe3g$f!wpkJ6!)WI@7^o$U8*o}0#f4{$lc*T1K+$b&@e=b~?XJK0TWnt{vf#oT0C zm__y<&Hr zWF)Qih8-FfNgyfnoJPrnM<`%AeIEo80V{#MQu0K{>S-P$ zsYpHLxR#A=q|?Auo2NY;2PWZV8{KH#04X%93sgouE2i*$9$w$iG3Tlz)Z#W_*b|MX zJ$%1t%H@q`-?r3+8PN!B0u$F7uOpM_9DXgAiCMuODp+ht%&oGBi2jRjvI@{fUx5IP zWwgiRX#!u|?GFf2nDv9Hru2wEHXM-jqk_BySWq(mc0Ffrjc=OFbzglbAFso>{W49U z)X7Qo$9Pyw!9|*$2UMatLHzAufW0NWHPcvjyCp`W=mbg8h1j?+zVuaCx){;Bq3{u# zu5aekK*e_H@NkzaB!Jh6Rmb^s43TUzr^yX?F=P7R`e`n`Y#3|o;5@vmOZoiG=jw$l zu1Eg;rz05|8Q-xl)L$V-hif6Vj=4vmXrGsMN+wr#BLZ2#D>E5DZ=2-B_5J&_F$UH_ z1?pcP&@rYNz2o7j_%^{cnlS7q%<_jNy_BB96`b|6WhsUc#Duz(2Lrt)->>P>ul^8hN6}M6qsRstq|}{F)xqf6nvcFQ2eUg2nuO zEO<+I#Y5bt^yuH7qNWWi-cz{7C-2hMeJ$W)Be~ZA4cDW>3m78R=9jvvRD@)c;m;(e zP|Tzj&P(E9_4r@>{(P0_`dWQ)-vbw z{?#FKUvpSy;#ENFFhZwl#Wo!iFz1l-ANiw!&^otS3!ni zF^GEfjgDf&p8$+9?lf^OkoVAA=E}w@jye{J@CFZay;wEHOCuoRW8QA zxII2{#rXDOFZgWs+x}TEooFr&FIF?>*>sG^S#*erk%E$`3cni;!saR|>n|=Z_P<09 zDhYdjoZR{lY0|8|V7hiRATl(DV6a$|L@f=_zu`QP z8wJkY$X?VRRWKDmV$$7Z8@iIFT@{dof|l&Y*6hI@Zqex4(4hAR?^VL;E^8Y%tyI5_ zy*vKSSxm=|zydGCJsB1{z1y5o9eU~LH7p<8z1FfzAxVAjsLRJBcNi$82B zzEj4CbJ3PF@P~ z3R=GFlDMFRz#}y#7j2kvn%M+Kls~D6vfYP#9>Vx#b>cya%+6yeA%(?NLB@6>8d>^o zVq4DOybdIJ2}ov;7mI#`;#4J~uA-@}wRRH92f&LK`^r*!7#$#@`u^hSG^hvBW5nai z8p_j_$6~+e>JmxrLTat}{UXPy11_kZ?CNkFlc_y5Z#H+2El8?5F`GVA-Yh10vZetxyYe=LD)txpvY~yJ?tY|VB(FSe zN?}q*&=9^WlVczLXCi-b<&KAX%m4BpKW><)Reh`#LKZ~XY=C%mmT?SfR@fi|=k(HZ zIq(TRc>=ulTio$^o0g5Bh+|b6TvVmOZ3u~X!NJII|6*CG{vMBY?r?bFt@D*Mo#Z5y z1E5U;^&b45KLXVgQUiCv)`aoI6v;(=$^SY&sVF;z);x5X=8$YIia263npr$|)<=s9 zn029CqS@0*FnvQN*@yKW#U=`?-Z%Bp0QVZH1IBVYiu@dZ)eb>r;^FVl{TNN9A=&UH z|K`qOfdf0&AaK&Ljg$v3skr(!N*<_*-<10OSRRM}iGS#JSC1aT#pW168^HI~D1!)O z8#Eh<*wQ20{gowuW_gp!HdJ?}M#-=oBl|29>oyF&kj>a!Ra^=|*64sFF+=o`hEj5A zv{?LiN%Q8Q^ATcF`+&LQ4~}mXONl2!a}HQg9yiI{X%q-V)Gh*_DI$_qWg?2pEx9|Z zy?n~9@u0{^l(lJGAygMcTXfk6Q%&w=YW}GSC{~)f(`c*zDZ^H=n``0q^D_B#?4{LI zD406}mH&X8HP!Bv=08dT* zx+$(uDKY8|5%6YfyYOxP*zMi_$Z`f+TLVYygk+=!ijYXJoER+ehS=fQtD%Y2rg>DEhBH)Q<@;0&CL0E^5kNh@~*eR9R#;0MjxM zVhsOCT|GJKKi0N4?_aGXW?ly_ML5HPG&wdP*mU$273*!?#>VSQerbTNhg#??9yEv> ztWyF&(U#ojt^v_hH*xgKRt;Kc24|Z$t@dK-0~leL5}NoWY>KTa`xcPN3Gl zjcy2}Qzq#MPBg01tkaH5c2qEM_}!#8h$R!eC>$&-jH)SdBd=bVd>otFw{uD8ehy%K z=q|uj%og92P1F4iMwKxCAT6DBtfiwwu^q9m4ky4&Qv<@2o+1&;KPBC{VT^KvC5(FC zZvir5yCZyCiLLP-+g&w~N^^RpF1#1L!6%)_oLhy$hV&?za=_hp)p%z$I6>`KKLO2P z8LvN+K!N;tJL^O8smzw=*>~jR5KnJf_U{K|;Q(cn!=MrU$N4_pQ zO0|n>oYJ4Dl(Jao*$wuwJY_5`_$I5Oedap8S>9Z|*C`l70vrctL?Vr#&qB9q(ScXk zB@fE%XaZNnf0S8}b|+##K5QwN$iZl#^<4;ZleNQ!!TVy% zNB{(4-DCZ8R;+XR_CjmlSP20y5r&y*qdo;a;dyON?cv0ZuC== zyy89i3?GWz*ejMv&w)GTRZ8e`tKxKc#sj*r|{8{jspYFkLI{S6k{b$K3R;N=3QbVMLka<`vn@`4mid5)@oR9a5_C*JYR& zceTQUY~p$}$=ZKR3{Jb19OL2=^CO3b+U}9dyw{Rd<4;>GLY`GEIdX#t`?>OWJbgIn znr`!Q8Z@>}jxLan>x)GsgUNdRUJDb=M3vKJMVxkDY-kwHH8lzZM2~JvC@vHtf$(|KwKt(6kF?j_-gr5GO3Dak=LDHBN8g>eb=xHsG+}6< z0$vDJcAi~`la&Y!x6~n`0SH+R(DVX%$=k5|2wSYexO+(wn3u`!-3`mG?jBrpeLWjw zZ8gDqOpeZqk42t{ml?d?WBz`l91>6#TPenE!p;lj+7TYEe@)h^q8hZ-71efugf^~d z$*k(Ht0v`fw0h&=wxl8oM(p)PU?GWEvL#`ig(_!@&&^RC#sCHjI8~Lq9Xn2Ot3k8W z(!oqKC7^ez>W2p>1y-IasR~rnk1|k< zs`)NgjouS)uM-a)`D_)f!jz*VdSgZH4C&xPAz2(B|b>sPk~mmgKk4P};t z>x{>$+`pqLGbtPw`7!H)BT0WQ{Oz=!-K?Iy(3-w|pHj;^&`UOZy%^DjzU)~0ny?ko zM3eJV#l{m$>lcaiLa1NJKe6Pakj8=gi=3{1^CZ=)T;G0BuIIJ{;GjlYJB!_vAJSdc z*nLff%J^L7QmMnC)0h!=qGs$L7NvwSRql&j0O?729ByGMViB4jcEyzGIRDhaK)^)o z7AoW|mg*qkJ&mVXWVrBqy$4xJ2QP$@NQ=&D`_@;(J2So3EK|QZ8V*IR^!nA{0+&p7 z(1mPW7J#o<$Jk`{d z!5lL&-&yMAQsYsf5}Ex`uIr-UuYJ>T+z0ygcgJ@!AzGBr2%4YTeo@MMQX||=GH=rU zMuJhvd-q{>Tj^&p9F$C(Q#rN0)VW#f<3dWrmtjFBaXLbYETj)5>TP5P4}1N6u}>a3 z^UF5G=p$?2R~;Fv?Aqbk{i7#iUd&XfE%^_8WZ*;CKH|lTB`;sw-K7(y-G{5ES(82! zm*N>>G#6JGhQ=_UWnxg8yK!KVaI|VQ>&_}bP60D=AEs2wwy&ZHCKFKi=G68T>C z+k{m%Gw({gQs8M2-{w*{Q2d@`s#7N1#ZR6s6+0C@b85QABM631Y3R}yp&~|VMmp#Z2P9p zrsfhzB+FRt@!Xz-z;MW++=FbU>6)b0C+W@S&*Pj>*1Ko(|7^J2ng_FWWLtQY`?IOvS09JLxc_2%U*e{xv&1hLT8!W?lxf}yP z#&KmZ-kLCfFW7D7_vmf$fgcU}-HCPAksNg3M2O$JM3lFi!qef`SMxgLCh+Wh`aP3^ ziYn6%L&L)~HS}d3I+oO+!cLdbM9iUyI==Q!Xy9s^t?DTgZtZ?yq+P(um-)yS$gW1K z2O_q=cWmdG3aYqs=$Kw%uunc>HVzxMT9~|eEdE2Z?9kC7psU2WGK!)JXck|LjVXXi zX*K(rFuEjQ=F>sD3%4xy>0y99&ztExF zsp2>2nOM4$+q(i2VDvDaDheL!8St(3I>4&($CS(*tv7lMXveF0-*o>B%%?Z3%I@BZ z2Z^Lf>wxy8)7CTYh-=nyg#RQCjv~C5>|70b*d>iEQLsjaa&doGe$iFnS@^lZ!kgQV z=PaHuCAOoX8mqf)d3i6C(P1%AFCZ4ZBjpkIhqp!cxNO$rJ{{8mIfFX;rUa}{(NgTC z)*+E_SFGJu%FEU;f@x$)k(3?8uR}$yvgghfL1$-yl5c`7j}R_0E`Y4zI-|k}NQoIA zXW}@$`PpG-+H;v6QJlupsz4A=HrQm;av>r|M?PhdM=@1G$-Mzw`M_K2t5smlfMf)g z%Y?zy|3ofBb6g%^kL5WpS^QUZ=oj_4+|eN@K4_twkU=c7c4tN3lFd9q%cl zEqh1(uIY*}Zjf=^QyF0E6n_d7;w{)r`$^bcU6YwY;0J27kZ3u);FlVkF$fDUOa%j$*2pwm8NVR@u} z!P%HRs3U45b5Z7e1pn!TQd0@pAUpWJ9%HO;++&=Nw3Hx!hqa-Y@SD<+1-xkS@QOJ2 zMQJ>{9~(ixDF|UKuWnFF0;yc?SuxhSVNa~XnjlIKp*rX0fSLNFVY%tq<-47O|C=aG zQV{clTCS9W71B32+nzK=?tokB*U{hEF`n4ms$bv6Yj;Yfol>+LH#~!@*nMM%Xi|y9 zQ*=<{Fihe6?t1Y_&Y6T{`#R+m@z-ka=I z5u6yFPL&#=_E`J6qkCncwsWM^h{RD;PCYtuj%~Imo6SzE{5Wh|6*;U7836U(y8ihm#>}d0c)p^44;=pMt z;cgzA?jW0wAK#ZVG1*vpsZl>n$Tl7|RBR6y;VwSkycUg?SNMaRezzc1SVVr)8C(RS zoQN>kQ~r$Yh4G6eBSiczYRy75H$HSn6{ZJ_?MWL_W93))lMi(~cs?w37$7lRCgQy! z$$qO2dAIn6-FS&w&S8BlJMxHWj!mQ^`1O7JM1Y=k@CzxvHaT0=@61)6Ub5%$ADzDv zhc36AjEsy9<3XoX?H6^DuM11+%aWpedZ)sBqdMN|)6~a(-__?f@&6 zlhhRNvn0)+L;jz?8cit;szW956+Un!s7;Nl5C?2*Z&%ChmhjWF?qkh4>)kTq`XC-tEO4Rr`ZO40k5-Z~tM zzY)CZ?rBp3bNp1ASK*@{Gj(I(7~=+;LXp2m2FiUDTTes4`cfj%O-UGla9Se@gWtjU zk<$*GT!LLxqEG_@>}&^RI2t=UpZycGx{Ak8hhcF;i8E=MD><6_E5m+ezgr{-@_> zj7FR65vNN;|Rn@-dp?rH6Y9?)bEw)0zOH)f=f z5(Jry)g$9tY_MwoCj3~+I$vT6dq^ibsKhVO>|-4)!hO%2D;R$OARq%F%$#4@{r(d{x;9QrZ?NdOsppd*N7pOG`Esz{nfIfm=i({SiuLUGn4O121sS z649Ictm`ybz~II@I^R_+H|p;?G#pGDjK5qmTUT+iIk<70$Zg#ROyKh45)rhDhY!c( zDT~n8?MmnPNtCFS9(86Z;H2D-_m68@ZU}DpP4q|f8jTr};~8=ZY^S0cRPgXOX45M2 z;^ZF7!}skZt|+k7*mVn`hi}Z8?l$waCo;(FeS1`J#^$?j1rOy4Sh(5Sk=`=aZ^{+W z#T1ug24ftprwym&2310NWc+}?#GE-dS1*WwbZm&Qh59VK_jm;9FjY)AAF7T2MG9ZSh#8L8u zeY9&eUT~+J(x5Nd&Z1H_%=fp5h`g|Ui%=Z;7aM#KO7q*sO=!z7l^WI`IatC4V^5DB zZo)7gQ+P_PrDNHaO}Fe3Kv;<)982d^wam+`VkDlO`W@+WsmhrP?=RASob)-9{VVRp z3}1-m`-5MB4|yw(Ii)dNJ0zC!6K@`cJAbrRAW6pB-Fvh2yAhL)x0^{1AY+ZWEFZ>D z{{nSoyQN3@8+zAQ36p~-#tiJIF}DqeSkrYTH(B7oels@FizmY-(Oa5i(fTU<0;m~u z6SU2GGe=^wrgq!>?5kSse(2XvC&7fS&d1F?@J{SkQ^*ZTrxN2hZ0soP^vCbjd)~yi*ndqzb&p*jugID0 z*Ve!*B??^VaZmyE*5LOq?u4P@b8YRF4nrYsk;j*L->H%A0=0gelKLRS0*G9B6MiWs0tN#KN4GZVV%-~Evxcm@@LqKnLEju7;_7c>G5 zH)h}+eb=Z02^qb1jZSpzU7Vr76cQrM@3{Eyykv&k@l&(%%SSNf_IyYKKB*hGxpzqx*`bq z8OL5;6*4PE z8j3~eZ85S(a!9-YuLSP1y$&iGs&$Z)VU!|pH7edh<|cw+_rb!?BbJeg<5GDxq1fjk4V17HkASmjb^ZqE47#?z~9M*n@! z)ti}Nw=TPtHCUn*k$+$EH|W%8UIJ#TXpXO9{)uW<$x2~*XEIdEoCG_j`}x@J_2K;O zn%``hQa|XW!uhik>9ysQ)6^Ko!u##oKS$r4o4RgCh1VMHpYnwNJUr6`x%t(*k}K_A z+A`9;m~-hmOFIdQq9u)*YB5lt4-l&2D;T3H@ThNK^`en0NKIRC@S$}w$0r%8RS2l4 z$mr;qFshGbD0@4na5yY8@Xul#fSyvV``#^Rq7AlehZy!#X|1j_mil*c;K*z1#HX?C zGX{Y_)OPAs;&D&yORZ+-O*s0nEJ<(9JkZD6*c05*TCif*#iA_@`}J*N+rAHy*t+gX z*zNDsc~js(jf$PJuPKOdKCx|naHUGg$tSc{Rp$^J@CyK@9Xli@V zhIV_b{Cr>V+E&u>XRq~u=5^d~H7+@3{Tmj-RMgxye)QRTe0l0nT+z6s z%(6Lm)DA>PsN&x7zu)u1Z;(jqTlaYPaPJBpj2Su)1Jn)hD zahbzdGBy`O^yabn+3Ea^#-sNfCHm;>sZDt#UELBrQ!edF07a7A+d%0Jp7n5Pf@B$wsN^=pi@d$65Rv3?F*w}Y&J>WHchL&W%_`Wr0AgbxWKi1c@jzvvafa4B zmmmEH#kVr+5XFVi&opX@_`k@Z<3`sc-Mj7jc0WFIwoX0$HZwW98#Qndt$_EVaibzO z0Bi*8Q4AD!x!dkG9UE++F;r$G9poqojvnuAFrtbSd8u)L`fGP|>6koR=HD2W*&7-< zJn5kbc6=j}h_|+9KKuyW z&R=%T5Pt;ibmV1>FxGV%PcFB}fBdM@8yZ#ixD9E2W=zHkzk8rgAF)bM^6F6ilJ1=5 z<@Me&o{=xAtpT9$Q3z!F_X3=vD%GiTT8P;~pb1~sIVy9j-U}0{;Sa%#&{LXiA}>(km)YCA4R zDd7{(W8F`&BwwQ^wmp4JiE$&^c+yi`99&+|A$zE;%|EN4?X9FaN=TbJdpp2Ff7m7K zm(~j+(3cS2T!c932sM-?YyZa_cv25opMy&_NgG3P&t~xur_uXJ;qK15Y|r|8?%_dN zTN@-*zbC1y5tFNSUBWbI9gf&-KfWJ$Ld|*=dNSWvU~g=Vo{%kZ392$})Yzrftgj@Y zg2e-~js!K=RcyhBRzlJP#0m;}DLSHx($51CX=0mm6%rM$Ts*Af4oRhi6QNfa4PG7Z2C4 z&~U<}Z&0{Xe7lI~S~QwCls=YWKDL8afAFwkDBXc|@lOqo%XWcjZAa<6Cp{nwH6I2wAz59GvXK+di$XG*6S(GHe4xXUkA`gcu8xpzhb8 zaWipX*Dq{+bA~d!Kf8bNBshA%ur1a*@JF?XIu%n8p)skOnBt)`aZHKNLTP`P=sUaZ zZ#%N0Gv|AcCNE)9Q17Q&oqGX#vmR;sqUFosUb(r}pBPBZ!r0|^k#1_VSYevTu>q`Dd8zyI^71E6$p-=xQ4#izGf9`% zjeU~3z~_t(M2<~5LVPcVsG=uKwZ{o2-fnpYvgGSHePG9-MqwMX0hfX46Ey|s?wx4R zdc9#MqUhP?ypo4fbAEQe(%{m4g(ZM3 zCHo-F%<70iEWoHm7Ant^l^8OQpQoP*0$1)$zI()MNur_!%uk%cCT(lcYF$cR#(ML> zTpeyjp(}w(H@Gv>TNUYF7d!+fb15aiUcPaCwN^%22cY8GNibV6dlxT72!f#`0rs{H06x z@eJ&-K35mKGxiGW%-JW?AW$gp-tP^XAuU;d8Xi`nE&z?X{Q03*e`9hwTkvpgvoO+3 zL673$QjG6fCLfPoZbKt9%@L}jeoXlHZk~VbeV-eRTyzET@^|aft(o>xTjdmNyf|}R z(&XQc!kAsGtath8>$2g8!p@PAVnhY~EH+3QI95O@=2R~<5v}*~Wmwd4q94cJ4b|)8 zfIg4+zy3HuYA_eY2jx%MP5&UjVTFen#@zJpW60Z6Wl2NJ+;%xZ1>VdJnRa=BvXsTK zCkzDZllI&jqI6b^3b}}5;B3t?34Tf6;cns_rhjiT>ESkG4ac*p)0Ma1Pw#a18s2%T ztG`_5i${xATF`I=Olz2w>Od!t4Z0tGAOCy5s0T1F$ugGJaJ+TRk%)FBT>0>!Cp^J2 z-*7p;)v|&D-HMt!N{Pxn;m*L6v99e2)8E52dYjL%SNj7yQKnR0bTr-fC}qZ7M#Om> zx+N?V76}kjQ?J8wLNgdtiGSC=7I`!SsZoK6l8zVK^i1z&F%5|btC z?}H|)vk_7g2_NucAU+xqDgv>{uq3A@X5wX`5BVgy`RU_EIPOTbG9y@g1H;+DlfRq| zC9W#{BAm=ABFVnE&Fz(-3x_b(9|faO$BesrurL&vKIQ&;l5K^p`fbHs!ik0oJqq4# zZd_>%3}69?v7X%0mK39+CBvQXGDzWMIi@}QZ;i*)UETCiw#FxQN)chMyv__@&<3~o zNo~r*jloF6*3gEo5U%W|1T^$&x~xCc`C^}3=D9yw`CTkBinSS=kr_W6@PpbIo#x9s zUXr}GksAa@C-Vi|2Bu348D>p7)x76bJ!WwhKx-5$~m0dwv}h3jvT z`f;lJDGyiqHlK@o8LutKR{KX~zu5eD8S(P;Ko1ImM0UO9yPf>(>?OPlcCh_$)|8YQ zzL?fq9}apmKB!XCNFd-1e8xUKgNQdn4ek2~+M0KyTD-dX)dhp??nM>^jGgAU?6HU< zHcYqC=5JU~6x{!k68`RkhYz_N#)kxYgfeig#{EXu9y~8EmE=QhdQC=`>RelX`gv$z z!zJ<`?3OpN*+~sLq*%m8c-#shEe9^F6CREf9fO(UHsZ>}*eU=9Bx-wz4^J%?ine`g z7kR0ykHqYPL|;>l9RVIxR-Oj2IVcw@4IuW6up=a+)Cdb1Pa>Cw6=wVv6wKI=i2j6S z2?X+=WFoZ9LlPHq{y? znEj_rDo8hTWScm1lOSW1M4PEfZ`Q9WGfF!(KM*SNL=hd=l#cs&RFC8Tm zCT;}Uh0Qktf$m7p#SM^&{K*|YRY&%ZyYx+_EM7Z8?LRD8xQH?Q03_Ya?J0avvI1Fu zI=LieIo@^ZJ3SN~w5%Z{*}4kjkE6TW8|`N)&SYm=W5KLu-|X_! z(&kJSFg~L;J!vdRFe2QUIyTY&?q>O|b8bN8bb!!=x?MuI%NrcK*6)N!SU9}QhB}QWqw}4o;suX? z_FB#s8n&dJ?!&kr4>Quyo9ug!8#7FR`qH}}-oLMQJAybbc3tta&pY`q=ZkB8Vn^lD z_B+9ZtiK&j`yv?p^@n$2Gudj7%#SXcphpBn_{9*Wy7zR0E?;xTXtIiZ^o9{f;ahkF zorFUcgcyRxE;?wJym@P(P61)!d&-4Zplx7=iPHH>9fy9r(C3JLJgOAXUC+9&^r-vt zppTJY;gtbqDWiX7njA610?U@R`>Nqmi_!jlz9hIm4}8_=x~()R|0)#fS4e~Ccr3R= zUI_$syCockiKPM!eZNVI;^y@FtZQk*`Dbq`+~&X>WMsL_4AuTx#{}s-;3hR1(`QU3J8&JRzTv|9evF4*axpK1E!Zf|wIcM5JVO$k@CJ=WJ(MdI{$x<; z9$nr3baI)pe3~xYanX$bj@7|%4v8zo3nIq84H1lPwrUBv`}#dbPy*QvxCC;W!g9_L)7Q8Bz&Oz?=Z5&hl4(_c4KR34tUV5==fqe@$Sn zHyjNiMoTzf64B8Ma9SfRJYF6`%Yu9qAu7MTeOCyJ!m)N_&@w@LQNHBcr&1D)Qm%n??iENW_HSXzDik16Z(&T$j(A*{ zJ}kVr7PD&Ql3Mh$XL#=MVIRy($2SaTN-Z>#hYpc(aLb~qrY@3tv#%?riBPr78WUja z@==#=A*jgmK*eb32W~9E+xiDV#I?c>f#&AXsxCzT{R`yFoS_7ki-ir+4_FyVPG&M6 zYwu_lXmR50*Zm&!Xn#Sy6`H^C`wxCjBE+N`Mvp=oq9dCDLue7*RzeTeY)$ifSH3+f z?Pb{<@`o_2^3#l&BkjR$wf`Ql{P{ql3=u|ZrPUX!*q98s``{>){~GGsb=CPiZ{nL5 zzSbycL3%WP$YBd4usP?9V`%Bznr1r0OSf24HT-}XV0~dE4XL+vqhtFW!?FvHq=RH& z;g_Q;%3;s`U7>X=68%R#qV@p`p{gDRH_EMbw$A|^-H2e}oNWVoj_tz8`{GFFc=ier3u4KNuSF_e{l>?Nr3-d(YEx7R&ZgvzJVv7Cc!YjwoGIrb|5O})+P0~To;nl&hF$iP( zc+b=iGF7p)yIRfB^s9Mu>!_Iz)+)v?9ycD}+Esb@*D(7)Y-e4!m~BF6{D|rLR+}2s zpSM||ziN7Q_=Ocym#Qi?ikfC@j-3c`ZfF#AoJf{>Fy0t9okG^J3h8hTw)x&{vZVHx z6Q$>7?(p)&cQdfZWBQjRm>Vf=2pV^b#xYyq;}?7qD-`I6tbDsqr|`2W4kmqeuNhjf__pC0Ea-{eU@T@% z;U-ICTvJ7iHe{bkf6HJnJlOwfI%|;UD&ZtT^I5AgWj?08 z%VhqfiG4!5 zyZdqFY{?vP0v3l03p9=c*ov8(8njsJXK)s7II}8PCd5hQTHN5_ctzr}dPP7@(nlEN z0X&uS`>amU^d&DgIt^E@+d8Ugfcu#>=xdmBa@m>ID+HznUyndFJN&^HL|H_G$s;i? z5CCrn&&o+gf!bk2(gc$h zG^HFINB$3P;r+?K>@AoHtOymOE?u1Nqbu=U*O}AdJj$su?f**E3}cG56m^ zXb`xDNOK^VmXUFWr3lO6RI!VIBmKJloJ?}@Y>k0WnM95u{k};Mi20B28k^x>l1F1o zoub%pbnZ|IPrhYTPM{1kHXNWwKYci24R9D~|*|Q&06|ydD(Xc3i}{dY!8xzYmqaGt$L{KTLz% z_a^fu@m_W+a5rvQ;>~Cl{=(-C>Q=lc-~{Y0g>{8W(S@$~BY?ET`n!^0IRGg1lKOSj z|E?c*Qydh}ZbWsCksWPic+lR(&1hxv^EFm;w3ldV)cdSmdP$$NU&&d2khxxR%UGva znf+FsBMrM_k%{N>(qd(5{2Z{VA5{6>0RzGT8;Hq73jINmH?-^Lmgf9^4(Q5SlNi;% z<+0~0!5n8(kkWkioUU8f{l5SmK;pk#(!L51EbLF}OoR%tVmL@7tUK#ULZ?EfT#4<& zEd%4NXgicLDD`29PLE3p22g&g!5Ii)w~=y)RTsed2X2G{Xb#a&LE=n?~x#QXNcDV2c-jcv5PxOW6nh@_m@H$jRUcb_5woNql}v<|g_jhY?x? zcB6_4ERVbg8JGY7KmbWZK~$~@foLLFQ$K~y#_nI3xwpc8G(9*1xM^`i%!#`ct3CXZ zoa>X>ScPkAWF#}%?7b8hpDxq~bXlgPY2H7*_Q#Hr#?}1)))lv2Z>IK)@B8>p?8l z5`|?tfkl@TY6Z8J%8Jsw$L+EV4ff}M`HNru$M=44>u(?#=5R#OKa0kJ&IJLHK=N?3 z*UuopuxM*r*_ylj^4Gl!yKA^P97a%lvawJ0B=vi!Bm~|Q`3(97h#Zj<9oVoOZ@Art<-qtd5DCwP9nsvF=i=5d z4s64TnKW3j;h>JKVJxs@d%ako2$MiIfz7SZxgLD@PCV68>X?@3WJ)G&)%X;{3F&<7 z9tp%Jq_nI;oS_Ohr+FYap#Vc0=5!4M^&0LAz7Jg0%)YXE9afwm@qB&SmC4LMHyd`Dg31Q=Pj~|<_`;i_LFFp&A^8{r>W4UZ= zErWAi!Q8;f<;Xnuz|C6*2fKgv?)U!rO`V=}U(;N^!!7hy(~ zNG16%ed$XM$lSS$F1l=k!{H2al8=EeBU!o+d;V5oIDZV=_>zVM zj*XVew0Pyw`+p+4aBP45`(^pX@07|F>t&jXEjH}u2I2fHCuUl($2GbW+;8~kA&dlL z($L(72DxQw1XgnBWG--?>c(bqa%~&-#>U885y5(@rJ1*=9|mQ>j|w$~p;PA=*88&! zCD=PMgh2s8u??VgY;-jJqucI%689_DuU|2I@ZjMdDXVrlZDlQ~d|*;y$HtIW=oIF9 z3E^CDAgun2av+yf>p6THzq6M2W9!(15y=!6uv&g&?6?LvbI{9H>{Opcm`Vl z_hBM62vY%+1+aZPb|5AYRDU;?C6!BM^D6OGVNU>X7QQ+9^>h*tNF@bPHa7;7Ll|`d z#{>&iE=(K|U+jGn(Uw|ni40DPmxS%JgVjRcN%!>~`^Li$KXS6PwALR@c}gdf=Y)VjJZOU@H#j(W$;~(4d^<)07tv2;A@Q*B>RAUZ za4+`%ErCLbdBFl_plIT&2L!4m7Dxr3fT*%awPjA)QeDzA^N>`I+%5L;hm{C2c?Wh` z!l#~mSQ;I)W5hKE+s};j^}5V)@Q+RGp#xU}lnT}(qj@kIgvV)!oAwQkTfu z=4xrk?vnNft5kX-5}d_qlIdX?I@K%D;T~DFbsGrEE zMhZKzKx1<<9?x0|6j7vdzF>TdD&Utu%P&`zV!B>kDLA!(FGhh~WBv=Dbn>JV(wrHQ z<=G?Ri|mo)G(J!V16s_9?T}&1v4OR_v3v--m0}uWRED|-uwuahC;VBN86OaDB8u;o zVTaQROukxN_;v>f6JG~9I2f0Sv`6a8v7ZnYC}u-d_^?|{+^`3gK(vc3brE#Zqi;`TYDomyy(KtDO@8Kx$e51 zGq>J)yb=`L0xXYWD(RHNW?!BTwPE}JW4(Z3AF+G>q;O6M2o+ZPIIw@up2q31E_{Tp zxvIJj8tQ3-qSg{nAxvkKfp|D9r*)c2Hg}#N@pysQ&AF4(ka$8|v4awx9F(C+D>mK4 zM|wlhRruCo_T5odudW>V5*(%aI5S0cD)$7Wj++SJy;px|7eJJQ)xkxpChJbCie(-3L*ELl?X-H(0jW8=U6^{>MNW42}RFNmdW ziKC9ZyE*MzyNB)Ds^>rp6}f9{mE~A`6?ZNM(sbI(lNVZSyi&y`mgDunouqKe`>9#Vh4YmWv0Y zEH{+-DD<3MDki11OJUCe@c=p8-!U>ZuAGqHiJzYw*3GtpCqf z3%pEB&0+a#)glU=H_P0`t|EAKmN(T56{OUhc4W{{c1RI zc_6b%Civ$YBywmBN-U5@7X`|55)}`c=JS0AWUQ}S=HpZh;eiKn#@$)@`2vod9N69^ z9fwm`8a_ecPAPA^O4gkJIyv;@Z?HcihVRytRMejbH;zjsI@KvzC-x7Tfascm(rgKV z!<0%IVlbA1C#j$uXJ%impA~Q0Vu7m?JracDt z$lOdyT%i>bSh`V48aBw`-Ym8lu7RUhP9A+`Otx*Rm5^;5qBbf`AmUPpzCi265?a~< z&K8h7+Tr!OWa31JR5dQcC=5gl3|Y|riJlYSs1Tu0_7iinNN)y5#M~W38xOh!5BK*< zTSJ9hvZYnZORF79Yfok!1U0v0$fBy4d;|p7>;pWl_ zMFYfQ4Pm>cV0q3v(IaUf#13EI^dwd;6#9t$^oPPZBOpEy&fOe!<=*m^x46Fdy<4Y0 z^p_vnP*GXka`r;QZhQk`3~mYh>M|8bZsSjd+5-bh`TTJi?fMyzQU=k1X$isX)k9=rux5-EXPdMdZXTO~2 zjYw4;++^|DO&HwpaTeHxJU)D8p+w`-IXi~iaT$lz+TkmcIqWeM4yPrKd9z5a6!)-f zV-4EmG@ct+A?}K0=1COXw(T?kq+-+ zZpeZgb2PVUDYwGm7mjjp$2|sR8dDvyc)qW%&+_>0-M8I%jzaqu{a1 zSn)u7l*AIka+W}ac(4ma7LMQ4FH_TSwEE*jbWU_25)VQaF0PcL`M168ZG4U$t4QgW zqFjb78cYPN9EgF!dT^>B!oYL%D~C=XaR&C3OzCQf3~(ce9AiwT>$AAeVI2BMCJ!4< zpawcq3R}Xgk-k$WBtF~-CQ5L?h3JGvJ=hIMXLja>f(>vX*Y%g!U#l(={%?l=O|fhy`;y|hu1c=?ie z>zruU47LIs9-Wj+F4~$K9h;ahKg>suqcckg$1^rY9=gR6b8gbZM$ zcM5x+Vc+ELKBv8HRX$eLl6FLU9l2l|w)cWI-V1xh9}ee~1F_YQJo1PI8*s+1zyA7q zY=QSb9Co`OiZaEQiZFWQ&0u-TL2#h}uBmG0>#60!nc z?e)Rh{Mi0}a0*zN(S0@y-LqMU1S~$J0-wPaN7$oA2`8J!FdKUf@xHQpDU{rV*gRG6 zwXVawRk_sFKnIJZWq8gZ8`m~USN9Zrud&*rERETF%(qQMD5Hc06b7!=E(PX#JL!+sswt9^%J3sgfZH!bqC8PVp$ zIO^SPrP!byPI}yNZRwIK=t7~~_{5C$+5Ly_+lwuj{r+-C-*{%b&1!}Ffpq|$j%}E_ zapf`-m`%pqae8XT>Mn19^$Ix1&*LkQ%W|o4$Z$mbfp9K4klcjtRb6t)jh5SQzrE!R zS6=xI4C$*4=Q0SNLLe6kU^bo~6Ja$u&7;)?euB_>dMp)CM7K)QvS)xarWEk=sczCW`C&{u zlp>GYTOxg9IcZy7DHCB#mta&^8NjBW@Hq`(IZ_u?Qfnzp1&)|{0PKD0QF+($i55|#(w4DmsyA|Clo+3nlbd%q27?+=xpZ?(=k9hsR2BH^KkGtfGZ(dR0B z*0%%pY)o%JLx?2096`^@bUtz%Jl_R>-g-$K?L``2c$9c%IA0 z#TP#YOZ2)_lJmueXCmvtmk0-So+A?&S)qE=eR$7I_`Q(rHmLlc;3 z=IF_<}3FXiiAOwWw0UVGUVbQLB+vDZ;L z=aBQ(H{h#|*p6o87>Fe;;m+U4(T9I2Q^O-NhB>pb15b(r-wyGGurn>DEBGv~*Uy3z z&Q46oIF#st!68iPB&4>~tx9x3MaWx9N&=Q;jrCR2GvQtL+;w~T*7o{6-F?SrfPJgQ zQQeLg0uBwmCvXppZwAJ@QaQUH+N7^K>lhz$yzgXaE-);X z@gDmcxb`l|q+{4m83r&^7ygZf7d$;1cQ~qRa+uH0eBqu20hBI4W9F%BCBMq=D~qOgA|xf6*AErXG7 z+}i^2=9NISLlXF~+!WfA#5(v;X{F3##ynK!$EM_E80M!W2#yjguf=|X^GYyIc`JPkfTL%j^HkP1fs0^b^wN*X){UFx=&j$D!=tAlqTtRk4M#3G*24)4ogV~&+VLH; z32;NdesWkR5#ijxN8y{wkKvV+6L$0Ha7eq zWDd{EK)glabT&UdiTk`PhoSVc4NWpIFe1-lGg1;&X-Uw6JtcF&V4!OCs#WKox4rd^ zk3D{TuD_?#S-Yg<>F~tSK_CRv|Euc6T~jBiaVpK(oZFGM!v`~Giw#*V&bpi<1a^$i zC7l({83B>|AXulSrfj$UeP6lp5P07_2ehUjoZ518ydAk$ch>M2^ZCT3|1Nw;`;A<#kKYDMK!ohZdT;`#9NP7;oE#a)^uz|K ztg3_Xq4W4h8k+HBeH&%fAj7WWLiOl1P zoR2OfR(!Lgp{`u@k>Bj8M7nB%g(+4Ng7$sK9ik0m$(YsGN9=Tn5 z_U%#EkmgkzFag^FgJLZfKURS2l;MRb!|1GBN-A()Rtl>;He&A|lrn2V)~#8AFFBXW z?k6$z0UfTUx||-0S&z$ZZEUD^`F*yZ-+S-vcfIiqm(3qO8LhB;s#e10wgDCwXFfS` zN;@DGN!Xp0KCHI$!i#s#k#kn&(%2s5tZ>c<$as!Y!w25+4)@hpUFG}y7ru1j-Pc}Q z($KKWp<&7<}>VivU&5p~Z!iIJGW2a!_;a3p{z;z%hz=^06MIdKus>{@3JqhK#qMU|1 zJM^y@MvQQh1A*ZM!3Lo9;)3hzzzDu`AH(cErZ1Y7VzRZiRO+knDF#@NXTUk!ust2> z0AWC$ze&mJbz5Z9hBoOL#w2g$dVG0kGejlUra`%`Zd)TZ=w^Jk=vzeND_=U!lks!u zejj$D<*Ytu`Z1Y&yz>}#^7qU7HLW0`=lH$Ze8ZAz>(t~({P@u$U+?HT{IiE2e$)<& z!}*B}9Lixo2cEHXIEjzX!oS4`*_117cX^sT+2m-(>a5PWvEe{6Tudj}IUR+ee9}Pb zhj-l-`HR2!3ze^Pkw^!8cEO6VAeq!-XhI=;Nrdw{q?k0~IhvqNh+K#WV?nHJ5_*Nb zeuGq8{SQ*I={=I4curDdPv9dfVExTkN$v7gu(uq=MpJ{*xOxY~4qhJ^{QM=gP(G0t z+q(p+L4X*+*z>dEOiiK+m`I_AA3C#f%SF=EP%97K`c=90_S@w3SM0)|o0)IsKkvNr&ilS&oshoox>aW5vs+omlfl5ym*2PU zPwuB08Gh@E+xy^eVw0yp`W#1S@<=pzT(*1)30}ywk2<1rCL?ZV26Ox|uSqYXpC*?% z)OgcoM7lb2A%4dlcX-g39{<7}U%0fWq+~S)ey5e}g&X;_-wPjJFfoUi;Ak#1(K3yZS_eF5{hfEgtQw5%+hocD5UNi{BL0EJ0q>Yxhy`5u zCf1mShFHE}2&;fHNcfn>T0#%A!aU?oKKL5(Gsj!|sMsib-P5edYE~}AZmkeSd?;8X zJPdU^g)A$>9PbL7G8D@e%qcdn>_**V>2lOsLT2wXx0=@GQIl3w!(uD$iy_JCY+*P@ zbtv&Wl2#95vfbU@&d|r~48~e@ScDj(JU12ZBPHf|OS@Ed<15cw6?G} zryu!9?|9G~K5^neuZi?`xqPLoXoxIq5dyK4f+DwPMvaRFx>&HU8#qK`9zTi^o*XdT zkEUcnEYye9fd!aF%b6a+Y$jN$I#vQoa)HS$ zpJf&4#Jh=L$I)GCZE7$(UV51!bHtP|glj2Aa;qFuB; zKKS4R9eIU?O|7lX{o~{4l9;R%hYu6scx;r#^+Abm!O9bOi%y38zHy22D7!In0_ ziIz5aj#;dMRhzC8`xp{k$dKTZn8MdDT4-w7uN9+8(|oeWoH%wInGO0rnK|%j?81ob z=R}XkoP*T5qoV`L=rbjSKC|Pw4JLcRC8prqYfaJo^Npvh(me1_w;C4wO({A-DQv!{ zFCTy}JFsn=oj}YYuL9&_sy|u$rf{rgcBdetgW{qBvuE!S2gk_B*yPS%KKZ!wi(mZW zarA?tTedtOPEQGrM&el|>;zee%4}OCHncr9e!Pj-#WfJdBQ9TIkvs14M%~c~6G9as z(yO7)cKtIAAO{O~PKGY?4_~M!E_%xyw~i1;9iqc zH_P%wo1Xk<)}|IActj54j^Lk~^)4tAo4R5JQfdKv{A9nGSyf|~QC@#zzo{f0tWenwj87aE9wQAhG11-lBrh4H5mTT%E3aOT2 zs}o56G#3x{`aZD(IQe4aam3p?deW{~eTnmd2ksw!>7|zrGD{xB2-Nxb<8Kx@eBL5N z0{dbCwgrfcN;6U5nF5dm6f=Adw=*vm8SkZ%*hi~he2@)i96%RcbdiTW%p)KD(2etg zfw7f*3)_659zRW>s1McbLas`VvXMxds0_uk;Ne7!qvFuM+~XNX{S=b-lK16NSW;S9 zm8QTerJ{bMb0z4cDlF~dWkx~>G=G#g;L%S&lq6hyhF;N6G8ergL8NAfUe?n*>Dk7g zkMIs*9fD(X&L?h%i61h3*rWL}=c7*BW)ANBmD%*v!=?%mK{`4}`}b@Cr1&pFh`~jrEgX5lZ-nPQX*#qYHS6`lf-Tb6iP=6jZ&Re zp;!>$Cg)tp-z)8ly?Fw>8WNSJK&(4@Um%i~r$M~rvz}qMFwbK8pFVRgLcdy5J^p^A z#TjS`1Wdf^pebrPWPbMKtEO|uW^>sMA26=WDwZ=&!1RaAC~6`BcY&#x?Kk`0YBX1` z`LwzAj{D7$_wP4@r#i4q#c_I8o;kc{r#aDh6zYa45*8!*rKQG?=lxL@laJ%tW)jO( z)j&)@89W$d&Vgq_67ELFI1A?j>_VytXBt2TQW-BRD~tZ*$tNW)Z++;Y zAK&}zGmpnFT~+vZY>5erBr=fb^;xVW%TiSmBA>p+KG8D(Kpc@v0YW;{3VJLcsd1A8 z*YYq#6eF&nAxV8vfC>U6c{58^tfewM6PN@lfy)m75j@*-y`x`}5-OVz1sKP-kj#I^ zSlLHDV)d1SP>G4GO6z(qgB^+7P>0lomz&Zn|I|27H5(r*_mC7by6I`Nw3KZbip$Ly zL+b3s=a`vSvgZd2pfdBBwc=9yT_Kd#cE0p0)6sm~?gb(ZLl&Z8o{#cP4U`vf0}L_C zD*MS}Cz%08P2H6<%vexEnre%0R}Um0Of*OF^H|Ih{L!W(tSO?`%Ls>(S#ez-2 zyY+d=pI%XzY5|PrB85kS+K53l?$dkR7D|?xJ*BO$Xe#{is)$@%J?P_EK=xoQZ`;ij9Y~3SJ zIlbE2c*LA+YG(M!;EJ-6uL|iw6d~zrkPp&5d1cd(oq-`_^||L^pnz^S-gFc_86*No zOY_CEGyPbqwhbLRu>09>e)H=u{Li&(y&g9Cjx=+kjLwrkdBK!P9@}k2BIdG0+GWGWolS8 zA(K+u&;p92u@)QKtzCd zMQwCGrgV5mK;f~iS~ZAfe`3}(gi7wBwJy6MFxO(>H6yglIpFtlE}2y(Mv8JrlL0U0 zKpHOlGY=Ya6D-F1vvL`lw5El9Jees!>1vD8^mMc%%Sy5Luu-%!APE#Tlu0X-xwq{& z8g~WCIrHZ-Od;rG$~ua2kkXnWKB`2@<%;L!W}0W7+x%mJr*__o%!f8^oDLjS(K|!( z9*q=@cw_8tO!1~7*=J{i0x3T=Lky*|_@_Sgss3+$>&q*BzSJ_QTqa4ZBB)_jr3_(3p&}D z31sTMs%-fM66Il`Jwd^gC^>3?6-5(&Wg7$v;bG~*%8p3P$2lX3L{gTI({`ImLuE|{ zmBCU#_$T`w%&x!?V|J#a(j{xol?fn*uNfQ~p{#-lY4b-zmd-U~X%$HCxb-nyJF)c0 zW!=szNjtaHDO4VPv)-8sWbHJYU)y&)HN7M}Ff;*|WCO8mNJb?EOObTbW~mcL*e%n$ z6wE)u^kBuY;cNlOObHXi_$!{ba{2QAgPFW~ntFgOj7qAH&xQhZvo2<}?zmPZ2>_xT zq7b4eJ{lAYA$l73^o;xVOKJ2djfJim%M|>m8WylgBT+{>Fal8dXhE{ige;eJPmQ6W zFPWzjQ(j6{jis-_yM5;85vPS^~}tE<_H zAqyKfs0OnoQ4OigO58xwwhXq2WFQPFKRwWjd!ns)z)juOuxqKd3eCANWOq~K94y+| zq;|ztok{S@?Wy31r($)PmzV9?Ki>VH=2m#Tp5m~SrG`fjnN)v) z#}g~TMh$<-{jI3&2ZGw_8ps^0Y$}`;0HN0EM!75kFZ2Nv?SIHRlz%#SY+3kF zHIRlc_4vf_r3y(15LIz%REg^qPd`y9fycTD)D50#IvL6)ilRxm8q48> zVj%~@L7EN1i=Hc<)Jf3mu=n-Xf{AnmJSDGnDe)gNCDyPgyZ2NK8!|k_mMW%-Pf1}f z`>HZa!5p$IrFEt2Buc{OS9aDXfNQ#)04Y?`HVe-vusrOK4r%z-u&5;wl;?>k>Z7_) zP}u}QIi4Cg)n$sSD!D?=$Nf>py>v*S>PO|~e~e+wwdR`3mu~*mW4oIg-WYA-Q{hdW zyLb1FgdG_qDmqEmOBovt2Qxd2OI@r}#Br)J(4uDM%*culBJL7YA7?xueYOP{9U90% z>I!(6=1$GNPG?RGRf`=0)L5MJ(SV6h(vb@d4YJ%2A0+Td;F!v_K!;G?`1wi4+A`6n z6V?e8IQ35nYgkgl<-w#VKgpofTV;=@cp6IWIq{NZP?`d#%7}W6rF^0!>h&QI7R9-N zJZZ@EIEL1M1I@vlB20VG4A4w%0F;N~$Y=1d5k!SUjv+z-QRyeXM=tC1J(LY~5P9sd zj58#LRzvg()#6+fFma@8lu6gASYos_HDYVuV%0h(DbE)@AH1n!{rctDEFHpRq^Z-G=7k)x!s%*(JYj~9-@Ns?E-Haas|uD3MwCWWLgTwAkSa*A^jt$t@+a`9*J}DN`Ar5tohKJw zWh!lfLye1qB!J(U_j|3rCmkv~ZaEh?vXZGOr}fS_?l>CKuNk07AKdf8GBdCtd$ z92d-pD4*WZP$zI}*p=0&D1?Gtmf=+1Va0UIag-c;Y9TbjN9i{KC$C{=VrmAJH3PkF zrslG5w-nV5UHWo$B>+yQmJ>k@#ZoFdVvtD*{yA)&X(nkW2~%Yg?;8M=Qn+vH=nl`w z&q)b|C$7KiuDi0gm@P5XbCvA7{kVt0v z3N&fDM+K`<)dMxl66H`(^(PzLCeA&*3(I1MD4pxMn9y-vOi~BilewQt`dKi|8BUfH zGSIa~lT8#&AW;_J%9dMsi2^BKbpiq1!%2EhK8j>)BgIr+8wHDx%HdmbkisXJc!3Ye za#n_^s;jf$5VLK4GEPzJgzQS4Be96AssAKvWJNP)qP19J8&zP}@R|q?gNZX(muoy{ z9IIhZe_#9H(S`&6x_9qhfx*R^rHcPYkQls|%HE?POwaU3L5-IV6;Me0Rqy6Z4GU1DGwp*kDW14H1yL$Bq5!9$ z22&;LsGnZfps3ohR0T9>=n_~oB#LqP_k?T+F9N7PW1Qq)i7RY8@1 z;j9`{G6%y(ruA+~ow=!@GR(DB`q5G)9()*7O73Fj)YrqjGt-8m$-$L|)I^s-UB0^y zXQVmtfuUhXV0<{VbH|&XJ@5R>zKRRNuy$HPps2|27Pw!gxhnv2M?LaSGm5XAsi5MX ztssyH?ZNr;?KTJj+?gcYX-zTI`T~JKy*2SQIwpW&hp(wm6y_{oh%X=sSOSTLJq3$5 z&J&a=K^<(nP@#IJ#MzdLAu(~Cpg{0YAVH02hX>MB{R_Ohr>sSlMkNYopVk1=!6<_K zv9U4Rm`XM=V@#Au!;;bwrA!AX0$C~(oWeXjkrfu0@xeZJm(EAI4D}Au4T55UPgFw` zQ+dm4NbNaGEoG6@E2*s%S5}(3W#^ii^X8j0tUWZ%^83@w{PWK@DY3Oo}k^!BTYqWwgl4x7_xZTW`AQrmXSt^vEGi<^S`5aT{x2R@%m2r|hDd zr{-XEy#M^YGxG$bn~#3yHnk!TA?lWJ+smt=>>x9t!kU5Xo}M#3=mKC}JCyJJ8IO z$5xG*jPSOnj!d#_g8!w)qbIRJ%Y~B3|6{6Iwro)gF~_w=+1qi6P9sM-+ic!-*pMp@d{( zfhMXL;l73@4PlbaL}l!U>GcppQU;#O0wf~2ivT#rYI!?T>xt3CRtY&Y{M`D*fX`#{ zp+E%%xfngRnhbP=nlOE;JqLgROa(rQ>R^%2FrjkX>pCp?&G|W0`>i zP7jf!bf;yYT+kz}L=DG98BFgxd(AN0fi7M+$86oU+gymx0Iw@vO%_31#-v|;_4-*` zw;q`YxUsSv?FSrq;d(ouzL?N6 z!UBbJy_4jd)Z=w6VCh`qv?QQ(l(104lc*B31xf@@gcpwy*0oYGMegnCGpPl|_8r9^ zWn0dM9dDXEj1}iCLl>LI-19n0U~TOv{?8&ayk4SkzxDZ-%}n%*3edgoYB*>r7uB<* zl8`NFC<_8a*c;0ximIehFByO&ABH=Rqb3t}=E+f1TSIxGb4cFDeryB7sNl9)GoNF} z+hjTG%)w?3dE!+E4<5V`P0?4VnUw`ai^f7@UV6+7GMj;v$8j<=e(*5Q$h{yn6i52( z8Vz}(nHk}UuymzQhqDHtsT|!;Jhve*zkc3fwq&eNOV4B*gkO8GR~+ihR)Q*Im?^Cq zmj5;gFyeDW6%-OoGB@-R2UVUP*?>5Eo9Rck-1~aUoL}{Kn*;)g+C2`Nz^3U3P`0V1y}%$sH48y zf}O&uqu6qhrYN7(O~kkL^!DTC21WhaD$|4gj(DKemoCTUkSi_+oU^K{A2@lk?PmxT z1AlhY*Pfd(BY0{w=qZbYW2ZuHOl1=_!+6LCI`EMk!+Vo6!f=PTti&^QOG_PJxO2${h{{GQ>L`K%G#5O9J zt-a>@3m2Mm3fyL*^{Bacc8*zIm1Sn-cuW}%L-Qd$5|k!ELs4v*Y`G|tfWyH8M^p8a zt({hxe&PHYyY<12J%{6$T)5Pchr+-8EM7np!kwF&v#56V?4>z*&ZAHM;*rVvrSp!o zAK!V<1hE;nUk=Nbl3&Vxj+mJ^GorC57MAR%aJtqKk+?o+QRGc9!e!+@G z&O-M2;42_lG3Nl|`ft_RGTEP3@2iK%RvPZAK?wWW-`OK>l6EOxT{)L+lBdf9g1T0D`J0CN>A z*4rq6mO!*0Mi4oWS$z!zFBAz(j5z@o z)$EM-^$*97G@ft{_H~_l$`}M_9m{sIIP70o-E~;V!1Vaj1w?b1Vr_=|tkKd}OMqq5%B2 zltNpCZ;wc0ky{Dt%>!^n+ zQ$pDdNjQvgCbO}{DYHy3hJp(&tTu-Z95D;BDll=@-We$Gl`v!|JrWpTrgjWNzh;=) z6RoGrB^N9<=PzHvb2jy8q@Z!$WvXYGD%g_Jt z+_?)cswgXp1tvygpZ(mOPi}hg=k1FYEsE~nzh5fHvVB*#VTE>9Aq*fjg^VJKjrQbl zJNFe-8>hqB29Poqh|F!b-4?~%X9Oplv(x>42a;74TpiOkr1Wegdr_}X@;7#|l6z`g zn+QiOlxhpYZ77v5Ph3Ne2_p($zL-c zG4v89T77A*MKuImB^|=O-nQN@>ru4`S9g0EepZ~fm?w!@;Siov_8)38Z(+798O*|Y zHEawVH07m5#7HQHF>GNg7{zug-`ud~GIJa==(qMZn0<$uLNmC+PW-1Wk9Pyf?LK5_E~qDy&9?{@xt z!-lLIZ&x)L0+cKYG?(0sVp``DLkM;hD zkfnv;;Za8g_V_Tiu@staUhOPXjjNWn;VP#rcSy3@l9!lWR;(@@J zW7n=XH$U>j?`|a?0q_y5iDhcbAkD7M(oN)C72*yY9N{{1|mQ&_@rx{PN39 zcYpoiC($kPFzi0PYZvAqzQTr_%xol)C45$=K8U0t8c$;tUgOiuhb$yUKM!q}h^s+XtJxc#_%k?Ox1PVKKo)o_GY z2^u*;Mf9SEHT=EXX|nVj<#1ABc-Ya`-TKJWzkKY6t*wpGpFG;@ z`|f?;Ubk$?;#>0aiySAKj~@KYpMUbM%P(Iw?ut4_iz+J2kAC!{(T{)ZPacG4yQ90i z=T*`fJl5J%)7l zSz+h+QBNqXDm*dV%CjV8Qf<5~oOJ-f|ETkaSr%N0jl7@fZ`AE(KB5+z3OW-wB>M;> zLlJP){!vC|-dh4be)UflG$Q+|2oLXlp7V;@szT<)-R6hva3nVIeAKGkcM zEu9xf3hkH}A2F|NeYN4{zy0dYYtAcqs`u2$U@2r|sDB`XCFscGfgJ zp0%K}f3oiI;b(h%8MRgDT!sRp$M%MNncKXs^z*&(Sm$tWXFm?N3W8BwqPg6nEOg03 zd=6IsY5vM^EZBn8fZvOeTqt;?mFKD^;=Fz6;@XC@4j{aAV92&U{o>27f1VO-`|M|K zS$xYafA*E?S@UXv3C&AGQDm)I8YL9Qm=-wm2Xid$My?^yr zpO>7b9U;JZnR)Z;8&1YLr~!tn&M}7dLfjYk&tF{ka?^3MU^M7Ojst5j+0qko*X%>= zfTybL*T;uWG;&|xGW9F;dD3!faZBDww{Utw-ki|*e+Bb>000c=Nklq ztlYdpKZ_C4BDHht&bw(LM)up971>+9>Y?)k=7#Gj~h z?I7ct)f>+Sy6H!06a81BHC`t|AjsCOA^?Ne&>+rsYzhS2B zE?-r6B+#I3?1-nJN}j4{d;PXQzqOj*7W6H8pO$m-_hmH*MOK)7{zr^BKjZ zwZE}CuRN2*=gr@#z%c88Pj~|Y)|M&xIy5Ba(7qc31AS4bkTWlT26P5%3V0gkZO+b) zlf#=gy>Qp3ZvM<0di}n8zfyno)z|(*Nm+HFD9DzVU;O)P*RFfyw%cyX2#pX0nByRLW}0it;ZjEzWq+?+i>tSG>5p_iOo6*J=@>EFHq|PJ8 zKP%r8^OU3PiVN8UcuZ`NjY&==y>HNs-}a8)ds}!{W!!PtjuJFXB zD7(Cjo2(1n8P0u6D*Pt`6oVDhA%Vf^4=7rp_W9S%rMWE&HEM!MMDAp+{%d5u!Q@)31r^CB8+Ucif?!>YYx7>0| z?1ArocaWLcb$GF#g)mX~HO)Vrvv?F;aR1jg6HnQZ03ii#$+UKb>_^P5p2#C9{s9LZ zCr=!ErMIVl7mm3z%PPvQojJ3n#OY3fhYY&+?%VVA>(*F*fm)X%kAHr3`ztR^thlK7 zql5jU(Tz_(yNUbgw2usQbPUAM<4*bFonPBrF|+JT<74CfFTcE{ndg&OGT6p{^UXIy zEEb3V{vYq$$$iZgn1+oDnnDt<`OP zqcakmY-Tl36pEkU&$i|`Vhn7LP8`=TsWdTU8=YVylt|Q*Ue1cuDO|tgbDz+u_u$MX zPPiTZc??_L@I(V1=q(tOzJAzP*X4Smj+tm=hQi+N1{2ONVGrV5+yWh8#wBUAf+){_ zDZEzzqOBd$!%b;wYFdvl@oTb^m)kqtSIId52}U3RLZ6;wusIXTBB2QuUZQKJ5no(a{_~$b@zlL@<}RKy(BIwncYpKw z#}VPP4wXHYl9J_k>Zzy9h9@`vAjRh`#x-tZpBxz?2OLHJ_t>#xnpJsWl!KS7zU~{` zR~em^m6bsNr1VwcQIzGFAguBPJHw7Qo10_Z-Q7$hkL@+lnS&m0#wAg=r#mz{vN`I` zobAady23;XkAwqV@EhzsC4jKu2}h;P&qHsDycLOux~A@lf>0B#3C&UjB8WtMZ*2e*A+q7hZVj=Zi`!Jc##OOUtX)K@o%^=$pp^Z%-}v~(DCk{}Il>gwuJ8X6izjTAIQ$Sq9(Yg*g0 zxppCE@i2?@YC-tfXP*sCJxex}-qR3~tkWqVBt<3ZnJ$0jMNBcV7@XLJ9sV#rV&*XE z$%w^6EfIs8+0jnPT-1PiS9PItrKvphQ_y`ZBCGQ~4qsk4ju+8DM?d$HL(X&n5Vr#0 zWE4ZLvzc=Y8pmJ{X1O`;xUY~+kh{!eQ=1YcB)=p1e&X(L`TL$wpsicC+F~7L2UuB( zurVwGSlOE5!QKjY!_LNrgS&tE#N*%ExqE;6;zjd|KCo`xtu=M?E==+H9esVh#{th> zB+|cr?OIm^Va)mu|LOPN*!I%Q8HEKOAL#Fe9Gl)*vt~`czI*qiWKRr1zFTkq>~rhZ zUbF3)rykK1dK4-*0eI9y4d4mG*oT)aS>i53EcMf$J~s(KlGMdkYG-F>?4En>5ug;N zS9s#iuNzs{NdW0xzHNLoI(F!}X!`8gE?3%8hF?6M2M(|h$8NvrGhObyOC6axBhFZ; zH8d7r>7X4Up3P&5@|*@%qJaa)+2_$c!T({^g#A#zSvCI%i(}+@Zmt13YWH{q~!j z4?q0Vz`S|$^c}8Eo3=jK-qE>z-@yZV9$rBX)*SSXdaG0*HF9 zf*AW4(s8V4#X8s?J-t%ZNXfW*{@X&Lp%YXjc}`OvZ6uLaM@C&8^75q)95p*)!K1Fp z*zuSvH5-456|M|#4SevJD2CS=p2T!|4ADH>0HI(sD3vKDitK4@{A?{0%fd;0G?gWx z)R5cZ!R!Wa#n>N5VgYx2vNz!HMv&I$!weKT94UeD#3U5wL~@p;Z|s_q`ca< zUwQGLvU9Rm9dBv<@^x$0J^sm0elq^>!w(A}&TFo@CKYF$s(69I!KSfsyp5`|1Of%u zl~-Qrt*tF^H8%D}UwGjKz4uOl=>25)gDNhW$nSVmeX9)9e`-kf+pL*^23IL_P4@P5 zprztOmKyEDG3XHfse;BAW%(ExdoanSDQC@0F+tUt$LVD^jI_8bg3C1Ap0NnWbhFb0 z`}<(LwMmMN$MKH>9ObF-UIHjtnI2|0DVdp>X`Watt+}mDA36a*0sutH-2#E)x>*8A zsiB@*TZh7rJ@#0Vx+E!x8fE(TD4papPXF5kl`;8C0!i1&21ZM)G|S^m&qGFzdY+xTeNKF0Ql#uW{_@Z%hbjC z!t@IN!&3VF-&Q5MnK5}~GlthtCt!*I9sxxBOY&n?R*`>&WMxdRpv05^cm5{pJ_&qX zC(o0Otb0@6&Hmxmg(q7p+JN-N;8fcTX^7TOb&;$q#n5}nd&%^4e|j8U|3N{S{C5bp zlCG(#aRtiC9KF50G09XYSh519moZu9KWarx&)_{c*EhZMr(z`U>HOWk_nr5W*VB(W zPhKmXKWa!e#ve7&-!rjfS#&(3AN_ll?`*v`y&jV1|CPMotti zehvJ#b`nq&=Zpj9ouB?68~{qHugR9g00000NkvXXu0mjfP)i30&3{vxkB$HUz>WX_ zP)h>@6aWeU2mlO;kxc*q0000000000001fg003ieZ7yYOXJK@2E@5zRY-Md>X>2ZY zWKe8%XK8LPO<`$lNn>wrE^uyVWqNaPCBU=piH(hMlAIVDoY=N)+t}C}Z*1FkvayYg zosF}xjSb%Z?!E85s`t8T(BJfY-93MF%~VZwJ5>xp%O8uw7!~W~l zFPbF(4bYat^1=W>LmbkZG0Z<5WGba14*+;m0RRCZ0Kn@%DBu_Xa03GXr$zt(PdWgA z>y+K0%=d2r!AVNn1pq+8{7-`bWM<<501(>NYFe&Z@^U<;j`obkW{xK2jGp#Rfd2@e zC(pmBy}7F~$kX1=!G*_@pX|RZc>c}*12d6<{>#MGmY+;ZUI`@X=xh#RXJlgplL;V# zKp;M6GYcLSF^T`#{!im4vvPHH;$dR)@bF;tU}bc4wq#=F=H_MsvoNu+F#NM%aPe|* zHTGn1a3TM1lK;&kX6|C@Z0+P~?dSmdk8W(@=;q2#M)p58|1JOR$kp27|9Wz8`Jc4@ zCCK!j6((jzFw_6x{tx|sP##5RYx94e|HCi9%*XWqVgEZ8rvE3H|IU=@e^M1d1X_8yPQp43QimL}K2`xy$455ZL+mz3k9{ zG$?*(|Dv^%^1)Tp&CM;RcbeO6y5dy!UJzuRNkt?LdwUBv8Qb+K_?n3@TJRT?ATA^T zzxd_idGL$&6$%d3Qzqo`@v&om{14RN;NX&;yP0kY&u7gv zC?Mi(R*KbF5<_!h)Pz})52L+Wta#<@V#1uI#|Z4nlJ@fc!IWQ{DqUtMOO=G{w3cw; z^wFzE_r=|;OooUt=Y_b|E&J)kw;N6ZYDI+5@^K^%A=)hocsSia_ylAd>#-b@Hv(m;F5DvL5TQ)DlNDZEA z%s_UqS+90Hwdd>EiF(?!+;RU{z}WIz{Ics)61$`0N{l8_eG9{oGfjgJ1V~LH zz^GWA8T-Q#bMr}+c=%UZb_HmEmIH5AP2&*W`yZZ%(IcYzK7P1C1KM$c%+02wv6(-J zz01i`B8H$qEV^SeF+C~(QKPs1FEkjxAe>li7_;UO+wCBP{=lHyD5wGu z*VRSmk~vQ0ZGQhP)VT^QPn|Je;IjFo1oQbDp$kx{e!n=4Pg%wxj)odw{;-l$Nu^a>T+zXOZfG*(5GR6&(B zPDj-w4al7OGwY?EWGdJIh>XR(wjU=I}A6E5@H z6z}UIsRe8puE_bU>|(&(8$rff1~*7wBd2tMuw=ZUpr*Ius+L+Q317JX+rSAqcli+a z`}_L?!_?F}g*5Xoiq?~dS9YqR8xDX7dQ3R|q=oi!#@$O7Vl27|{3SXN&HFq5=$b3abOYfg>W@$r$)T0Qn;{355CSv!?!EnnWO$ zuz=4@6J$HhtqycWrF6k`XEC?A{N0*0S`DYh@@MQ)^AmiPIZm(Wa4CK5N4Y-dZf-y8 z@Z_lov4U!bz5oIcL!%MnC)m_U#m8-7yD4mC6atOn$7xQm>a{rPWX6-J0*o5cVL6{c z81y(-V07k`t~RC->}O_p-{t`Fj&ls=4I?oK$l!i^k&Rc%IV`+ zF_Pe{g<>rkh#3?=a4paa|0mpN~zB~!! zaW@=)20L}mhoYjuqtT~V7NggO5!-2_SLkERp;cHQJl#CSkO@hfG0FWPL^x6iVq8&c zJQK@=TA6D30$l?B9sD-83cc3Gd_Lv-7Sd5|yC!twJ_vNq)^ zd2jC|=h8P8LJC^W-I`DOn>J{{hgI1$jm_igxT+z{X&oe36^JUz&NgKh2*zh4&P7=h zF2UsGOK{7S5p3mCw(!A#nuXVQ z;3MZn32P}R#+L+Tk|`qb3s`r#buqw@+mAPisX&6DRFUGzrS~NQdQpxiVwrLczSjC{ zUY0@=I<+68L{mMQrC!uYD|k2XbCt*GDV^zzi3B4uI&_ELB)qAU(NTsxt? z+;6UsNPX#{Y;e_k*@}fl*d_cvhRqi3mtWsA7hEg%A zQ`bsZKXLuN^!uOo(I6>3rzdVEz*h{0iP$a=Gr!u&hqh|1?V#EV zSz9HTdPdTRcHaWP<{DPiGiMu&_=%WX+2SZgDr05}VR(#fyGGCKC$Tft?nI)N-L zHjy0qGPZo=&6R@+yg^YXjVUmv#|8@23>C$y-a5y{sQm0)s%lXcaxl8!xs8QEpb8}5 zNJSf=TIB;=h8?)0Fu8%^4~s9rs-+Za!jm1U9SB{uS&sJX$7_2^P}lqSvh(EZQHhO+qU`ZY1_7Y+UB%v+wPvWt)27FX0tbY&c&v1QXpz!pi6Jt}<2bV5B{By$bs5mzOh19}ue)n87hPcq}-RvLWy$ zv^9*ZYX;pn43iK10!xSEQfGrgv6U83G=3pK#sUuzv});nxm@>M3)g(poUlDm2zT4V-)!HY8Snsvkfbco~ zlovi`Q|u<{WV+Hq5wZPohGr!oUCVonkWywOyj$slD z`U^C$TAwiKROiRSgoeQ1!yDJ(IIWN1Z>b=0*J_2{P@Fm028;;*3| zHc1vYYzFDgK)af%q`CN{SUWoIe!bV5yyCNG81~kmdtdGjr$v#8R_#%YTqqQje;l*h z@Zuu=nh!+rLzjDlaKIKmMjQTK@eT0HJ!B(A*sSP-9S z35r@(W@MDhaW%yyNxOtY&_Q(mWmzjZ&2?VL`y$KpO%dEfQOWmC^;RKO+&L7$G{PH< zL^qh(%4xH$6Xsj7-fWL+;(89vDr}{oant5*(NV$Nx8Wd$NgKFtzu6%J49X5RY{i}o zr*sro$SC4RY&#E_%W0oE8B%_BRE7kUppZ>W$4#VO%7$cjI+fQ9{#LXBtLcs<8Ws|V}3J2 z)Uls|&sTZzIt`pS6w+owN$O~PMa(hiZr&tH+187?e>vl?(IcwXD;s87M?%siBZr3)3V$#D8^z?p%M+@-?Kur@@`Blydm&b zSw3*yFNZn9ZaAMeZ8QDv{;M?h!pz$A_-#}(i9 zr;Q8_w1gr|Oh~XIAY@!a5k+8w);3X48F@5EE&<*3iuR^tSgcr^Ng49(yD3>T5ubwp z5;n*_QpLqBjCG8wbEfzjFT}>?{j>ohId$a%6N1A=g;z1zCI=yX zKJ~-cb}quy((a!>&I0DuVXl5W3IVX?fIA&+7NbtVD4e}oeeEy)-&8{eNetmJVsAUm8g!Q`s;X9 zK7_u8zP1e-W>J?<4(V^f)3fV*C69HaL2H=iCpubr}Zz18re+Xa@~#*IxYA zZ{15^(o^|Lui6NuSU;CMjrp%zs>BCm!(F~zEF4~~c1VL78VTyTb02nmD*O7P{r08n zkCpdm#rROB-kmfcF~Zq=LcwD9DgtU&Cw0gFBc{+R&2c-2m^$kn>QT)telD~NgJP}l zRgxsh1?#%!n~|Qk93oJkL!p;SKOJK#u7QGYacnX0RPe38Ju$O==<;!*%+O(~z4EZS zoe@BV>@FcPJ@mG5r__(m6#iZskH?T~Cn%nXRYt4STWY9GqVB0ZHw(2-QbsTn3sqs{ z7ty+wOgKf0isTv@dQSchD2Q&-k`o$?+hnF}@ibqP-!twg2Fg&M%(J78$ip_x^ASG~ zAXM(g2T+$-Iem#qz!&iM)6-i_8n+Z(`cfpQqxIWAexA^gX`~TOJz<>D2T7c74ChAX zB=O)Kvo(>m_t?X5hj0{#A>PYf`IRqBqbt~&e<{4qwSQA4VqgUH2DPieb$cCaOj|QM z3%=Pn(0S^nf@uAfC7((}pEwxC4faq^y2+SI&Z3kjhh8R+l?cIP9`zR2d#*#0WN#8A z$7qKQxnq689>oc4p36BVq0f}c-F)vReMNO?LO|%Hv0?zB15j^ooeiW{lKR>AaCX`*xC6 z1lA;QG=C{EZ4`}!W<-v|wUb?q>)V=jXl1B=+&R#MIN?(~#iC$hK7ha&YsVszxnAsMU*M-aAr}5vvG>9smUaou%YQk%_K;eOWO_JF zUU<`dIbK(pS{{x#(K^>`!<>VDkzrYdcLFuFJt;u431~Lnp?~xB=!<8l#-( z0V5E=tkq#1^rRtNVX%uKzFO2NvYk9YHB*wcfQBlj$=$i+ZA@Wy%)#S!#}2v_Dj=2) zT!G2nVQEVvGy&xF;pEI<&z%hKn#w6!HgxB1K%lwj5u^81RW?}N7H>XJr_`?08_iR` zkDZ$gg~?Egm>)8vq^tfDC>Okg$EX8FPXXu5?0_zP-`b=4+Fow{%buoI_P`QFCux$H zwY%iscN^(U#K<1L~v%$s>>nMPPG|ox()pF>|3UaZXD6V>q)5C;a7J5$cVnC-ewmq2{q$(Q5$V) zbEg*ckJ(bDCz{yk7iaOG&9=75eGGOVT5CV{^ORCSwyoe01g~Mmlu7qu>A6?|g7h&& z%xpkV0`Kl+V{nOBcR$IsPPE4u^-43}QJVlqk0J^xW*yyN>2>9#9a-NH34MZ_a2MFg zZm((3T8Viw3gBaxq~Xy+%R2zPF*6`@UaD+0j|+EEBh5TM@!t=GsJtB;()t!>+h?sa zw(LmdmDYZL>o+M3YZR+NlR~)7vZ58CLb-5#erV9i1KHIVg`8DUa#BRoIiBAw!7I|p zgDCt2`E#!~Hs^3)GkH>b>HhcWC+1i&u)jq1a%jtwA1S(u2yK;I&`RqTEV^G7JGeVj zlHK^BIHKKhm*vZ{q@i|=p4eqREZo<B5tDwfjNv!ZO9^t!z@Kugq57k41z+C5{R)m!?W(C>{U6b=yb+xd%fEJZk^hgHzLasrluL2hQyG11iY?%VX5MjQC~gagfJ$XmZC zJsvepLw#+LTuLIwCpEFJF|G=fM4Qp*TaHByg75jLj?QxsJz{Z9{T@Yb%*@mu&Y2n; zVS^3@>nSwGR)9o)nWLP57{@0~>5OzrrehzwAnUAa$M9pE1jHb1cJ_Yjh$izlBclLb z9vk#vQD-wHxBPB2gF8K;|}3%=Phk8P2(S@-WZ_I2dyVcm&1y|30JLTq4@4X zJ@tCwC?>_Nj9)oEg0ytqlFL0j=QzR0Gv~B1B>*qa11zYB>1=IxE>kU`OM5^(=hz;m zt(2?Aa+YrIrB}luF9ZyggN^z076l$sgL@B%0=X9?s2fT^CJ<_2s+vN#sHatX9FsV_ zMR|*K9tkSzz(rWXUPg#D@K34whMZb#nrDqV0>nw%U0K)ef{iD5g@sA!wkOz|7)#2; z6JGiw6zmxVXyhrtQIga1`4h-Kw{jCA&wS=#e0~GDn#L#1@XxbeqV7Vn~x#5Hj zqTd`>&!k1nF+1(ilyJ`3y&xR^>G<)rTg==8Um=ncBUo7M-jx;iD(LZ3-y+K(`2sms zLWbzZY#x%9_|e2q4Mp0G*idgrskJtOKn3dI7(h!jr=}17R|(EqrHz% z6q9Bd`-MGBu(*RZlD9BA@$jX*)u?1(HOVv-FA>``)k_I$XDegfhVezckYFqit;Q!x zzTx%Wf7H3DTsn4B_NQ(WI$YNYr*;p^THDJsx8%IOycL5n8)36=^0c8Jb*DG~&h`i5 zVgsP%kA4&y0!oR<$RKX>yM-W&Ko-gh%ZbxqybmMU<8*3FM)tg+0A123RiKIC1Oj>N zU&9j|DYoOrItT*@s2It@0+zo4^t+0A(0yPg<{-qvvbRrbMwp*b0|+Lloyw7rwP*`V zjOB|IuG>gTBf>WR@|sm5O1ZiPRTf)7VZWScE?ASc5){zEZ<-0i%ixaL7nzyYwhn{3 zx@L=mKsZ5XXZ`aIPKuZ)Xr<5u581E9UPzzt)TS2%@jnxEQv^Tt1RQea6z_I?6f}A? zHf#vkT@J;~-v=Jf_3SM|VIDh*=;gg7;fjE;5t~g0H!j;LOl{2&fUE>a!S&a`^ERAq zImb?6I??|;J$!Xa8#Q8SsH#@?Xa=D2p1Z#bD87@}CZ(N;&Z}FQYZZMFuR%ot<6w#L ziB$QwX^^wt5H-05W$XuOH$OBQ>&Au%vY8`Nsr_C773#8D@2#uLsIHb*M>re;PfCJgpCaDk)ZfezsV*+kL|6S(7$zi|gqFB3bB^mv6s3Rm){bYQCd zgBoD$t8UR^LD@tYiPl7BNDDJbO1mE&3sp-ypmX55`AMLA0mB$#loZfTLkksFye%5- zSV=}y(%mf>L|RzMOkwf2zEAHoLHV`NKwVqE&URPj^tSUDVfcwg;r2`H_HuHYAjmF~ z(psd7&Xb?j_!81$q+TU&*&-abeQDyF$hDvFJ%XAqoCw+1VU2f)$ub;hxyV>hA59Ji zxd7qgp%f>1u^gU+@Mh&zlW8cyk@$swAYNVauK+_8Sh=@OC(Yqpm$1rV>MgbU8MQY2 zai)^|aj@8O6oc)8he2$vCQKS|gA49QqrP&v6ZTTBJjff?+xmGbAr)qo4n$bL$`V)3 zM8mhrb?$}$AuB7$ZfjRuQUATrBXLMSvMsrVC42ZJRYW?H!>hT?%mH;r=DE@EAOI1c z$WY}ty(p0q=M^RUX#ZPFSBp0WfKaW+bj+xkqgJH1^|~?uR?0N|RO5?*Lt895k?Cz>L1Dr_MpgKHyIz zlDt|Z!(}Q%%OCM*<(?poM10E6I4^L1aZ(WmdJbj7+?UDOiCC{fto;0&9!I zIvDPvPx^=!<6rG(3+Lt)j->q72{;#^@qp|UQP=Unok3nn0uxz$1o4QdZirTHA&pe6 zHZo${ns}n0a@x983n_2I{T^d&dZIJv+!nByWU^NKkcIXly1D5Cei zqc?7F6FK^P^XVieWZb;_EFPlHCCv7TXjr5cM%f0Y0K(^!Xoo^#*>akGfS39BY`${5 zpY&B#T{~M9Y5!>Xqo#_Lj2&ZI<5EWjD<@bYgS{QW%t@mHL9=g#9L{5Y700TsVbW@~ zCBUP{CB_PpgiqzxRz61~3$PZ3&ObW~yS@Yg0UK+aS%)hWM>vvDdDa`WGuVS+w01R8 zYMQIlDJd5m{6-w`mzZd#4?lx1moV0uvg=$oZT_NavurbNe*1KiY21*WPK_?BX1GMC z+v_MmBnFAc+mS9;pf~ErrSW}xnam|yDUr%S1`$Z8pnru!-qJ*XV;o1Wa4w;zq@TsS^6l!C00Yv_L-M_7k}F}gtpHj!+oiG0%5Bx|zI2#b{)}sH zRFi(^y@EM*bT&xrf6a;9eA1W61HyNNl0{#l(N##~ev(n(u@S^DX5YCequ129v}=S- ze|@9|;N-Uub&dy}&1b`J|7EG%mKzaR&G|)d88J>!^TZRWGB4klSLsv3T=vRqul@8g ziJ~_c&$W{IbkW-wb452Nhgy|0UNZoKw0J!Gmy#GMLGf50tVtdtyZX(XfV7Lzmh=4kAMSF&XqGU^Q7aq?W>{ zU|QO+ogB8R1LreEc05z8Ek+Hw4qBspxvuN>{)`>>giJhSkdU@AM3RYhHWI=HtWw;| zE)2e!W>BxyLS?1dZHyRU;pI)#`)_AWAe`5B+R_7yz)l>W*J)R6Q^sKUJp0Mp=fhu6 zes~wa)nO4bD>L)cL?}w#14Rwri&% zS8$z$Csxg#t0#%oGd00bf>j{0z)f3s33acIPW9$cl9rv9S!A?EV`{6J$x$!sN<5}6 z7aG`Wv1PN6jl{e5XQGZkP%A+gcgr!oySp^E5Gib9+pYQWgJ;l@7)^8ctCH~CpJMgG zdD_X1d^p8*QJq~05m7?eJ0YlTVbvChD+B_VjYP109#E+r>LPw?-fv9t=MMsA+$$J`{Lc751U2 zK?_;Gaby6)V1R-O2AEWD&Ve2Od0*zqlzX1!;~$N8y-R?Ppk75q#dyQi9WP%y&#q5^ z?0oYL@scLd*VFkx-y@(?VQmoeYI9Wj)uF)pjkoG*-nl7QpJkc0 z&fe#pukh44q$I>+3rX@LZM{hd`M7ZMp;-I8^%y;R!I_?DSlaIRX&fr-AZ#qj8_q)? zoI9KEq}-Vf)tRm@At>n0+_HIW0=5@j&Rjhc_90FcUR@-=zo{?S6OF<$#M<~B4L^5F zfx#dRnZpwgOwfSS_+BbD(27-QTaOxo*aO$(;>r_;<{qXRBG6UF3cm!Z>+3bGJpSO>Y_px3$YnZ+U1^Ave zm%rGr*>@iL71<1rQcN2aZknNzW*YBhug(+j{v1;u+w4>d+C$9BiL0wSZHPSF^+H+> z|FSa#_k~$THe`>~mF?*p_<{x}DgpMDg~LtgXyYd|s%lOiRRrw|R%-7bR`_R8M|-5G zl$GTIn#9k!Bdz96*>Cqj%)kM6Jkio0Lv5B@UViMCB9?PwilqT5lv9ZbDH;A;_ z>c4Xw8`yOQA{pJ>=EuD8C- znw*}ddszK;%RiJyL=5}3mN(gTqQ)2T>$w<|R1ggSQc(-W(Z2d9JV`c4iS1so?-2WorK&beS?4Pn>9B4W0YCgzt?w>2?3+Zqs3 z{k_WNS$VqN)N;HWSkQKOo)@H+-Q#$O*y(-;0gF|S*$s;ZZeXOl1+T@%k{jQyY}(P~ zc)JRigGjPm@NW+2noxM{Wz*-jI05l)sI>&3m&wj_)i56N9K7f%o!_xM^*GCw?VZr` z<$panHhTs5I|+s;TF8SAMA0?Jte*h}CFLW?7PE<-2cQuEnJr-qx6jzaFDv?qV<$sZ zGyCbUx?>qfjj)*^Kp~d|w=|W`))~4EP#q-Ai}`$A>A#O&@xR560#f=Oi!^9uzpvLe zzqGp-?wx@qEnL0tt`lt#!lP)Brbg(X`=TMFRWVv6+XT>ZD3z7feITeh);OLRU&{6I zAIuBDJExGr($~H(9oeGJPTvS%GP1Hw3Die+tjKXYMX-x|uuNxoJNq!$a27~IL7k|O!iN+-7C$ZZ6jvu`_4Ijc z$=22Da$2k#LZ+KqMQD9_9BvEKbWG1&y5^j^o0v2Oq0@1DEP+8(?u0fuv7l6d zqQX|~{@nNy0Bz;TWN>M6xbr$;9%$`PfE5eeQ8=wiiG-$5aM{I=sq7iUn9hjYXbJ*V zdgA_VMWr~;V*Hb2G@dM0ebO+vlrUOq!4eN4n(#5q_jn0!C*uaa*z`~RmUyf(LdB?UT^I8Ap>pUKACwQtZrV5g4~-_T;(EyFN1aA*3o!R=>YS zDSvmdd&f5INzg81(}5oWQu$St(1?6uIC9EXEp-5@&dRs};mDQ2-z zS$>(!e`>RRF1n|FL^1`>pzCjF^yuxL3*?YZP>pQsJxf$CF)ronL{1yHs5Ac^8 z{|2l^_VenGc>TnG_i*_5-GiQ)8*oC@HJtJ96~0Pg^Ek{?YR0k@oThK;5>Cz;+Zg+?>g{SMDFIh zY?h1A;Gy(zHOYU+e?Qd8E17P@*V)U|ePI!qYmeu1}VcPVBrV;8&qflA(j!nd*~=Rg*435P^~3eJpA; zT{qZW2>&^c-9Vpn>4NJkBHRvy;J@RHINtUh1(pu*wx;#t9y)dj;^iS!s(IDM!WmQ! zdj)WHX^zAPK=n>cn%t7edw&v=pklkEjBa=9`sL`}zS_mDdXMnd92x{GW*RStF^E0* z2`d@TAw;L>uPoL2V_2($@A>xXSk=DWad&-&h;KmjhQrXSP%ypSk2T8MqyLzG(<5T1 zhhowv)u08|k>qbGwJfojf3}yAhIcQx-CmGFKHMnYdhp%p#+u9LS^M67*z<2u`(r*< z(?ZqoNqM19O2bDZ6T~>uq)xjRy6Bt~}YmpR{l=&MF=I5|gR#>jl9zw+V zc3Yo16N$6SM$(xSGoN>@~j3Xs3Jg%2GABF>qT5UuI96E((?a82v0AKqe25+9V zI7{am%3{NVLCwho6YA0-)>#Tg`rzmisLT#t{9S4W`_!;_UGN^LkHdW>v@}Xyqxe;7 zT$eGrtT&V8DNx|Wy)s0BKTRI}GHUp~ocaO6FbwA>#qXd8yUj0C z7ZgAj2)XIF`~6Mm^g#wMgUw0z_Ot*TUDvh!^!lj>^?A(0)&U$a%h&%Zj}<`ZG)v)Je)=Q~MK ztb*Kgr)B7TZNxO-yO&S*lli)YlYNH>K)3ZrD6{X%Qt2l(^enxC3t)ae9>TX-v(_-r z&trJ2%^Hg8KP|vSg=Kkd8wxs|jriU3N!@CA+0@`~n%^HLaGLl+G1j?#KkKsV*Y;T_ z8R>&a^Ejqc?SYlmgM>4jmj>kY+}vL(2WuR^&T!ML7OzLR(Q!p;^?Qqatdwh}*EDg7 zQ_XG>sx($&IeaVFv%Y8XDBZii=E>uE|bV}&4w_>p5E(9 z3!;J2ZFYHb8>8A8$%@G$qo6 z)$A`??ZGDejk*8nAEk6~r;vR*Lw<#O&qI780V^6K*^@}L8?}sJe7*gf$v#|GVak>E ziv2^Xk`urklW1|X8n%G~7OHRHtAM#vLSo>-!FbVf!|;8^{#gQ+AC&edwW5Enpxp@Q zxEVyuiU#d15IrzT6MI;v!DROh&Gaojism~=ST=#Nuihw9NQ0y}1;x=6+FXa==rl8c zUpf;qWvrSj;N#Z_n~6Z{O@!_a^kI&o^^D=#?}-O%Gy|}5=Jf~{2-ZU~1WyLD_Ad^{ zef)3hS7{D`sKbFxsQ^semY8ZetYgX_~Wp2 zzvrf?ucLXzjAHay=T;A-$b@f(!d}=0Rhp@-Q-c_PYL0(nX6-wY*U$2NYSLRM;qG1` zqk_RZT6U2wL+Mx-1|u1<#1m!rsJQx+EjYfNMWOv@xQ3}MXC649t3NPTm{xH$p{LuQ zv;D{=K?8dstlh5IbpQ)BF!~Q52Xr4-?{P`i*H? zgwhzcECdObcSb0~n`U13AhbYo{T*Y7P~y=GbDiL1H2n#870RAI6Pq?YSlL=TT6xBL zr#VXV9Q|g&D)HS2)Md;AHt-G5N_?Y(nWI4!dmcOpUu(bQ%|RYp-}hWf(~YvA?`$cc z)!z8}G<%f$$M6>K=w(b%_&fGS*%5bUm_W-5@v-)qUqRn}@~a_x=S6NiBy&)AH-os zw$6muQL$@%fLK7*7kS=~?nEytO~iAB(JfxqIGk@5KTH$p)QK8`)MJqa;^=7%>UgT2 zfn_L{8RppO4IcS*)jGWz#ifF4h+DEEAR~^;v6V=8QIz)EsC!Y4F71svRiD-KpoDpv z`e|YQKz46xGLx;rqbZU-Qoi~oJCi~~%*R!r0fG5CUs?=|5!-Yv1!osJ>lDAGpPo`m zN)R7jCu^x8W&J4aFfoC{6Rn9F+CnwCly9Sk$S0d%%Ix+8u(l4uf{KRgrEbjZ}z^PNEh1w+12dObrYsOCHV zDal$xw-z}=2*{E6L5-VM`?`F6;UETHli@>LIcJjK{{4UQD%LMJP}wXdy@FZdWkLsj zsKjjX$XD|~PV(c2H=8Vk%tifa20w=?usiJiqmnmke{*%{FJ4yVvy5FCR1h~z2nWSa=qdDA;O6y4qyY(cT4!~+ z(hN^PZjP?@FJu@g4sM_18~GD>n0O zG|}~&FeXLx0gC#Lg}Btfjk%=DLyE|JqU%7)TaN*rdPejoHsvXkM`eW#T?(2@4MwHa zEKO`;Qy#uV&en!#c-w%ebQd@-B|ZC{s8n(hlH|CQ0#R5d9+W(hB_-p&IaGw&gY6fv>5b);z5eodsg`d$5NUL>ySxgorc_}Bf zJ#3)TwYWA|q!$8GHU>&`98WK2M|BoR)CRl|;SuHM7m9h|MnZ*sq{t^a^7k_r_IK3M z%Y*dc(j}p!F{b1+C?3eJY+yfvYSH8IbJ0x}FMTQ#cW(nDMR*o-W}+cVa`M(aK#8%? zs&|A!j0acDS&V>OEU7)oc_6K4C8$PLCKSCnbkWk;zon+3U0{Btxt&BAF|uX4{?0v< zvGHr;K*~+nx(1|ZJ)3EkT=$m*Mv$4DZL}W#9M%>2zdK%7XRCUI!aHX2%lqx5ROS91 z$~4{Tl6p7Ej{<&w&a-5OC%s2~V&l~}3I588T*LdcY< zjTqHp_WD$nUSK(w_72%DqwMgU!YV1tpJ?`L5HJI2WU^*xVO5&f@ZcuB65&BW7?N+V@P3b2yuMCDBsG#M097!P#YDpG%HIhagd;1lg-(TCi{081;!F< z)~h7HjY*>_WJ4PPDDv~|iNC@+(7=Mv5Vb(1<^WSu)HlQLUhh9(!D%>Sf=S1gBiY`! ztC}f+3IQt%OV*P7D`)C<4)J;YJT>dKwx5{@C zNX0a-R|4uP%UkKj$U`k7fnD~G>&%XS0=q`aYQ>^z<%V^@GFU6Wf^%3D~JOwn`M-u}9Lkiou=F*;Jd(UA^i~BQ5BQXXA*=EZ#D+ z+75jy8%S#x&Fs=k zpCcydAqDmws!8B72s#tC@YO$h!B(>+*E=T<68NZ5roo%z>5M8JZhB%Ls z{)|6JoU-IXJXUXn?O))q`msyV)+!$>P?sqqCk$+dt+o8>y95fPGRK)&r4~tSj4P+N zL&~NKs2$A)i&7KPNAUeb6Ch-qkFn9jwLiT&55m*R1_LalXDvxMfAT@=GO}47yn?e^ zVWx}PwTzdcs%3-GZVeh(34bBBY(_QZk_9yAQ;1B)9$^|Z zG_&}~$bK&VFomDube_TFKyA@dDrP4ikJ~^^NhOC zut<(KF(4jB>WoBEY3Tdof8)i)JqYN#j_VRNvZEKTjP)gz+XRjtHPjeu5%Y*C6r9!( zTY`<%4P=wheuA6)+Tz%B=hvQKvXKNG`N0QH8UHHSmdUufqdBXvv~!6flSlaXfFfnn@W}iW4i7VJoCo#N zv%b7MSQ1f{9Vg21j~z1{XVS0->n5_;hhi%hY~?A=SGN@I%8w8%rb21d5^3x@!VBy= zNHJ1y7iNDGOc|A8f<(FMP1EMt#+MaE#+_#4KJfqOJ@?4<}v@Z zeG+#IT9=3FUu{Vv+`66!hW4ZtvOs9 zEEY%3>w&(X-u|C}f{?5eJ*bI}G!CJvAWtVn62hpF3Yee zS5*3rxZJ}?gP#h86WQUr6|YK!{M?%QcDq>N*JsovB1?Ui7%mTT)GPysfhg`^?Ry@a zPScoKYfCuGSfq5$?-*GKZSJ*rGGt5`@)j-g^VS-abcm#j^7OWZoqH0qi^^u(6?Fuw zvUSv|CeEihjL20a5ds|Ysm#B>?~XJ~hAb&mi*d+IKM1R4&Si=m`+?VBO7*?d@stRT zdPnc3eVk#SrM++a4e>$Q*Wb-MyI|)YO>E~HKtBoLf5HcUh+825%sBP0m>jB$>a!Lg z*&@GaIuf81E10u03pB$8bc#i%J#G+3lPi3XzqpbsjQG^>`C^z-B#yq8 zF@z%s-e|@O-^dE^$%_eYg?}XgNXTJt30&|yN)Dypp$)(UwH+Sg&YIUP*_(GtfNf51 zCd3;Tme^Jl)XAETX>6iwc#IOs?R(2Hrys-E4p9ilG`%bRP2T-YT2AO zqHBHPa_v|%+KF7e>~V1}(PrprNZe1fSqfd8r8Kq;ZMG>;aBelO;o}MmBkibUIK3Q0 zp#yAUHE}(-ribXEYek1)`uon zI>izQkvn&>g-x=BdSt~1D3XOOVyr3ZK7Q1zK1eY*iNy

?i~y`#BZ$OJU)>D9MQA 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 + +#import "ESDEventsProtocol.h" +#import "ESDSDKDefines.h" + + +NS_ASSUME_NONNULL_BEGIN + + +@interface ESDConnectionManager : NSObject + +- (instancetype)initWithPort:(int)inPort + andPluginUUID:(NSString *)inPluginUUID + andRegisterEvent:(NSString *)inRegisterEvent + andInfo:(NSString *)inInfo + andDelegate:(id)inDelegate; + + +-(void)setTitle:(nullable NSString *)inTitle withContext:(id)inContext withTarget:(ESDSDKTarget)inTarget; +-(void)setImage:(nullable NSString*)inBase64ImageString withContext:(id)inContext withTarget:(ESDSDKTarget)inTarget; +-(void)showAlertForContext:(id)inContext; +-(void)showOKForContext:(id)inContext; +-(void)setSettings:(NSDictionary *)inSettings forContext:(id)inContext; +-(void)setState:(NSNumber *)inState forContext:(id)inContext; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/Sources/Common/ESDConnectionManager.m b/Sources/Common/ESDConnectionManager.m new file mode 100644 index 0000000..589c3c1 --- /dev/null +++ b/Sources/Common/ESDConnectionManager.m @@ -0,0 +1,348 @@ +//============================================================================== +/** +@file ESDConnectionManager.m + +@brief Wrapper to implement the communication with the Stream Deck application + +@copyright (c) 2018, Corsair Memory, Inc. + This source code is licensed under the MIT-style license found in the LICENSE file. + +**/ +//============================================================================== + +#import "ESDConnectionManager.h" + +#import "SRWebSocket.h" + +@interface ESDConnectionManager () + +@property (strong) SRWebSocket *socket; + +@property (assign) int port; +@property (strong) NSString *pluginUUID; +@property (strong) NSString *registerEvent; +@property (strong) NSString *applicationVersion; +@property (strong) NSString *applicationPlatform; +@property (strong) NSString *applicationLanguage; +@property (strong) NSString *devicesInfo; + +@property (weak) id delegate; + +@end + + +@implementation ESDConnectionManager + + +- (instancetype)initWithPort:(int)inPort + andPluginUUID:(NSString *)inPluginUUID + andRegisterEvent:(NSString *)inRegisterEvent + andInfo:(NSString *)inInfo + andDelegate:(id)inDelegate +{ + self = [super init]; + if (self) + { + _port = inPort; + _pluginUUID = inPluginUUID; + _registerEvent = inRegisterEvent; + _delegate = inDelegate; + [_delegate setConnectionManager:self]; + + NSError *error = nil; + NSDictionary *json = [NSJSONSerialization JSONObjectWithData:[inInfo dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:&error]; + + NSDictionary *applicationInfo = json[@kESDSDKApplicationInfo]; + _applicationVersion = applicationInfo[@kESDSDKApplicationInfoVersion]; + _applicationPlatform = applicationInfo[@kESDSDKApplicationInfoPlatform]; + _applicationLanguage = applicationInfo[@kESDSDKApplicationInfoLanguage]; + + _devicesInfo = json[@kESDSDKDevicesInfo]; + + NSURLRequest *urlRequest = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"ws://localhost:%d", inPort]]]; + self.socket = [[SRWebSocket alloc] initWithURLRequest:urlRequest]; + self.socket.delegate = self; + [self.socket open]; + } + return self; +} + + +#pragma mark - APIs + + +-(void)setTitle:(nullable NSString *)inTitle withContext:(id)inContext withTarget:(ESDSDKTarget)inTarget +{ + NSDictionary *json = nil; + + if(inTitle != NULL) + { + json = @{ + @kESDSDKCommonEvent: @kESDSDKEventSetTitle, + @kESDSDKCommonContext: inContext, + @kESDSDKCommonPayload: @{ + @kESDSDKPayloadTitle: inTitle, + @kESDSDKPayloadTarget: [NSNumber numberWithInt:inTarget] + } + }; + } + else + { + json = @{ + @kESDSDKCommonEvent: @kESDSDKEventSetTitle, + @kESDSDKCommonContext: inContext, + @kESDSDKCommonPayload: @{ + @kESDSDKPayloadTarget: [NSNumber numberWithInt:inTarget] + } + }; + } + + NSError *err = nil; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:json options:NSJSONWritingPrettyPrinted error:&err]; + if (err == nil) + { + NSError *error = nil; + [self.socket sendData:jsonData error:&error]; + if(error != nil) + { + NSLog(@"Failed to change the title due to error %@", error); + } + } +} + +-(void)setImage:(NSString*)inBase64ImageString withContext:(id)inContext withTarget:(ESDSDKTarget)inTarget +{ + NSDictionary *json = @{ + @kESDSDKCommonEvent: @kESDSDKEventSetImage, + @kESDSDKCommonContext: inContext, + @kESDSDKCommonPayload: @{ + @kESDSDKPayloadImage: inBase64ImageString, + @kESDSDKPayloadTarget: [NSNumber numberWithInt:inTarget] + } + }; + + NSError *err = nil; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:json options:NSJSONWritingPrettyPrinted error:&err]; + if (err == nil) + { + NSError *error = nil; + [self.socket sendData:jsonData error:&error]; + if(error != nil) + { + NSLog(@"Failed to change the image due to error %@", error); + } + } +} + +-(void)showAlertForContext:(id)inContext +{ + NSDictionary *json = @{ + @kESDSDKCommonEvent: @kESDSDKEventShowAlert, + @kESDSDKCommonContext: inContext, + }; + + NSError *err = nil; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:json options:NSJSONWritingPrettyPrinted error:&err]; + if (err == nil) + { + NSError *error = nil; + [self.socket sendData:jsonData error:&error]; + if(error != nil) + { + NSLog(@"Failed to show the alert due to error %@", error); + } + } +} + +-(void)showOKForContext:(id)inContext +{ + NSDictionary *json = @{ + @kESDSDKCommonEvent: @kESDSDKEventShowOK, + @kESDSDKCommonContext: inContext, + }; + + NSError *err = nil; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:json options:NSJSONWritingPrettyPrinted error:&err]; + if (err == nil) + { + NSError *error = nil; + [self.socket sendData:jsonData error:&error]; + if(error != nil) + { + NSLog(@"Failed to show OK due to error %@", error); + } + } +} + +-(void)setSettings:(NSDictionary *)inSettings forContext:(id)inContext +{ + NSDictionary *json = @{ + @kESDSDKCommonEvent: @kESDSDKEventSetSettings, + @kESDSDKCommonContext: inContext, + @kESDSDKCommonPayload: inSettings + }; + + NSError *err = nil; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:json options:0 error:&err]; + if (err == nil) + { + NSError *error = nil; + [self.socket sendData:jsonData error:&error]; + if(error != nil) + { + NSLog(@"Failed to change the settings due to error %@", error); + } + } +} + +-(void)setState:(NSNumber *)inState forContext:(id)inContext +{ + if(inState != NULL) + { + NSDictionary *json = @{ + @kESDSDKCommonEvent: @kESDSDKEventSetState, + @kESDSDKCommonContext: inContext, + @kESDSDKCommonPayload: @{ + @kESDSDKPayloadState: inState + } + }; + + NSError *err = nil; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:json options:NSJSONWritingPrettyPrinted error:&err]; + if (err == nil) + { + NSError *error = nil; + [self.socket sendData:jsonData error:&error]; + if(error != nil) + { + NSLog(@"Failed to change the state due to error %@", error); + } + } + } +} + + +#pragma mark - WebSocket events + +- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message +{ + @try + { + NSError *error = nil; + NSDictionary *json = nil; + if([message isKindOfClass:[NSString class]]) + { + json = [NSJSONSerialization JSONObjectWithData:[message dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:&error]; + } + else if([message isKindOfClass:[NSData class]]) + { + json = [NSJSONSerialization JSONObjectWithData:message options:NSJSONReadingMutableContainers error:&error]; + } + + NSString *event = json[@kESDSDKCommonEvent]; + id context = json[@kESDSDKCommonContext]; + NSString *action = json[@kESDSDKCommonAction]; + NSDictionary *payload = json[@kESDSDKCommonPayload]; + NSString *deviceID = json[@kESDSDKCommonDevice]; + + if([event isEqualToString:@kESDSDKEventKeyDown]) + { + if([self.delegate respondsToSelector:@selector(keyDownForAction:withContext:withPayload:forDevice:)]) + { + [self.delegate keyDownForAction:action withContext:context withPayload:payload forDevice:deviceID]; + } + } + else if([event isEqualToString:@kESDSDKEventKeyUp]) + { + if([self.delegate respondsToSelector:@selector(keyUpForAction:withContext:withPayload:forDevice:)]) + { + [self.delegate keyUpForAction:action withContext:context withPayload:payload forDevice:deviceID]; + } + } + else if([event isEqualToString:@kESDSDKEventWillAppear]) + { + if([self.delegate respondsToSelector:@selector(willAppearForAction:withContext:withPayload:forDevice:)]) + { + [self.delegate willAppearForAction:action withContext:context withPayload:payload forDevice:deviceID]; + } + } + else if([event isEqualToString:@kESDSDKEventWillDisappear]) + { + if([self.delegate respondsToSelector:@selector(willDisappearForAction:withContext:withPayload:forDevice:)]) + { + [self.delegate willDisappearForAction:action withContext:context withPayload:payload forDevice:deviceID]; + } + } + else if([event isEqualToString:@kESDSDKEventDeviceDidConnect]) + { + if([self.delegate respondsToSelector:@selector(deviceDidConnect:withDeviceInfo:)]) + { + NSDictionary *deviceInfo = json[@kESDSDKCommonDeviceInfo]; + if(deviceID != nil) + { + [self.delegate deviceDidConnect:deviceID withDeviceInfo:deviceInfo]; + } + } + } + else if([event isEqualToString:@kESDSDKEventDeviceDidDisconnect]) + { + if([self.delegate respondsToSelector:@selector(deviceDidDisconnect:)]) + { + [self.delegate deviceDidDisconnect:deviceID]; + } + } + else if([event isEqualToString:@kESDSDKEventApplicationDidLaunch]) + { + if([self.delegate respondsToSelector:@selector(applicationDidLaunch:)]) + { + [self.delegate applicationDidLaunch:payload]; + } + } + else if([event isEqualToString:@kESDSDKEventApplicationDidTerminate]) + { + if([self.delegate respondsToSelector:@selector(applicationDidTerminate:)]) + { + [self.delegate applicationDidTerminate:payload]; + } + } + } + @catch(...) + { + NSLog(@"Failed to parse the JSON data: %@", message); + } +} + +- (void)webSocketDidOpen:(SRWebSocket *)webSocket +{ + NSDictionary *registerJson = @{ + @kESDSDKCommonEvent: self.registerEvent, + @kESDSDKRegisterUUID: self.pluginUUID }; + + NSError *err = nil; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:registerJson options:NSJSONWritingPrettyPrinted error:&err]; + if (err == nil) + { + NSError *error = nil; + [self.socket sendData:jsonData error:&error]; + if(error != nil) + { + NSLog(@"Failed to register the plugin due to error %@", error); + } + } +} + +- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error +{ + NSLog(@"The socket could not be opened due to the error %@", error); +} + +- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean +{ + NSLog(@"Websocket closed with reason: %@", reason); + + // The socket has been closed so we just quit + exit(0); +} + + +@end diff --git a/Sources/Common/ESDEventsProtocol.h b/Sources/Common/ESDEventsProtocol.h new file mode 100644 index 0000000..dce5238 --- /dev/null +++ b/Sources/Common/ESDEventsProtocol.h @@ -0,0 +1,44 @@ +//============================================================================== +/** +@file ESDEventsProtocol.h + +@brief Protocol used for the communication with the Stream Deck application + +@copyright (c) 2018, Corsair Memory, Inc. + This source code is licensed under the MIT-style license found in the LICENSE file. + +**/ +//============================================================================== + +#import + +@class ESDConnectionManager; + +NS_ASSUME_NONNULL_BEGIN + +@protocol ESDEventsProtocol + +@required + +- (void)setConnectionManager:(ESDConnectionManager *)connectionManager; + + +@optional + +- (void)keyDownForAction:(NSString *)action withContext:(id)context withPayload:(NSDictionary *)payload forDevice:(NSString *)deviceID; +- (void)keyUpForAction:(NSString *)action withContext:(id)context withPayload:(NSDictionary *)payload forDevice:(NSString *)deviceID; + +- (void)willAppearForAction:(NSString *)action withContext:(id)context withPayload:(NSDictionary *)payload forDevice:(NSString *)deviceID; +- (void)willDisappearForAction:(NSString *)action withContext:(id)context withPayload:(NSDictionary *)payload forDevice:(NSString *)deviceID; + +- (void)deviceDidConnect:(NSString *)deviceID withDeviceInfo:(NSDictionary *)deviceInfo; +- (void)deviceDidDisconnect:(NSString *)deviceID; + +- (void)applicationDidLaunch:(NSDictionary *)applicationInfo; +- (void)applicationDidTerminate:(NSDictionary *)applicationInfo; + +@end + + +NS_ASSUME_NONNULL_END + diff --git a/Sources/Common/ESDSDKDefines.h b/Sources/Common/ESDSDKDefines.h new file mode 100644 index 0000000..d36c130 --- /dev/null +++ b/Sources/Common/ESDSDKDefines.h @@ -0,0 +1,152 @@ +//============================================================================== +/** +@file ESDSDKDefines.h + +@brief Defines used for the Stream Deck communication + +@copyright (c) 2018, Corsair Memory, Inc. + This source code is licensed under the MIT-style license found in the LICENSE file. + +**/ +//============================================================================== + +#pragma once + + +// +// Common base-interface +// + +#define kESDSDKCommonAction "action" +#define kESDSDKCommonEvent "event" +#define kESDSDKCommonContext "context" +#define kESDSDKCommonPayload "payload" +#define kESDSDKCommonDevice "device" +#define kESDSDKCommonDeviceInfo "deviceInfo" + + +// +// Events +// + +#define kESDSDKEventKeyDown "keyDown" +#define kESDSDKEventKeyUp "keyUp" +#define kESDSDKEventWillAppear "willAppear" +#define kESDSDKEventWillDisappear "willDisappear" +#define kESDSDKEventDeviceDidConnect "deviceDidConnect" +#define kESDSDKEventDeviceDidDisconnect "deviceDidDisconnect" +#define kESDSDKEventApplicationDidLaunch "applicationDidLaunch" +#define kESDSDKEventApplicationDidTerminate "applicationDidTerminate" +#define kESDSDKEventTitleParametersDidChange "titleParametersDidChange" + + +// +// Functions +// + +#define kESDSDKEventSetTitle "setTitle" +#define kESDSDKEventSetImage "setImage" +#define kESDSDKEventShowAlert "showAlert" +#define kESDSDKEventShowOK "showOk" +#define kESDSDKEventSetSettings "setSettings" +#define kESDSDKEventSetState "setState" +#define kESDSDKEventSwitchToProfile "switchToProfile" +#define kESDSDKEventSendToPropertyInspector "sendToPropertyInspector" +#define kESDSDKEventSendToPlugin "sendToPlugin" +#define kESDSDKEventOpenURL "openUrl" + + +// +// Payloads +// + +#define kESDSDKPayloadSettings "settings" +#define kESDSDKPayloadCoordinates "coordinates" +#define kESDSDKPayloadState "state" +#define kESDSDKPayloadUserDesiredState "userDesiredState" +#define kESDSDKPayloadTitle "title" +#define kESDSDKPayloadTitleParameters "titleParameters" +#define kESDSDKPayloadImage "image" +#define kESDSDKPayloadURL "url" +#define kESDSDKPayloadTarget "target" +#define kESDSDKPayloadProfile "profile" +#define kESDSDKPayloadApplication "application" +#define kESDSDKPayloadIsInMultiAction "isInMultiAction" + +#define kESDSDKPayloadCoordinatesColumn "column" +#define kESDSDKPayloadCoordinatesRow "row" + +// +// Device Info +// + +#define kESDSDKDeviceInfoID "id" +#define kESDSDKDeviceInfoType "type" +#define kESDSDKDeviceInfoSize "size" + +#define kESDSDKDeviceInfoSizeColumns "columns" +#define kESDSDKDeviceInfoSizeRows "rows" + + +// +// Title Parameters +// + +#define kESDSDKTitleParametersShowTitle "showTitle" +#define kESDSDKTitleParametersTitleColor "titleColor" +#define kESDSDKTitleParametersTitleAlignment "titleAlignment" +#define kESDSDKTitleParametersFontFamily "fontFamily" +#define kESDSDKTitleParametersFontSize "fontSize" +#define kESDSDKTitleParametersCustomFontSize "customFontSize" +#define kESDSDKTitleParametersFontStyle "fontStyle" +#define kESDSDKTitleParametersFontUnderline "fontUnderline" + + +// +// Connection +// + +#define kESDSDKConnectSocketFunction "connectSocket" +#define kESDSDKRegisterPlugin "registerPlugin" +#define kESDSDKRegisterPropertyInspector "registerPropertyInspector" +#define kESDSDKPortParameter "-port" +#define kESDSDKPluginUUIDParameter "-pluginUUID" +#define kESDSDKRegisterEventParameter "-registerEvent" +#define kESDSDKInfoParameter "-info" +#define kESDSDKRegisterUUID "uuid" + +#define kESDSDKApplicationInfo "application" +#define kESDSDKDevicesInfo "devices" +#define kESDSDKColorsInfo "colors" + +#define kESDSDKApplicationInfoVersion "version" +#define kESDSDKApplicationInfoLanguage "language" +#define kESDSDKApplicationInfoPlatform "platform" + +#define kESDSDKApplicationInfoPlatformMac "mac" +#define kESDSDKApplicationInfoPlatformWindows "windows" + +#define kESDSDKColorsInfoHighlightColor "highlightColor" +#define kESDSDKColorsInfoMouseDownColor "mouseDownColor" +#define kESDSDKColorsInfoDisabledColor "disabledColor" +#define kESDSDKColorsInfoButtonPressedTextColor "buttonPressedTextColor" +#define kESDSDKColorsInfoButtonPressedBackgroundColor "buttonPressedBackgroundColor" +#define kESDSDKColorsInfoButtonMouseOverBackgroundColor "buttonMouseOverBackgroundColor" +#define kESDSDKColorsInfoButtonPressedBorderColor "buttonPressedBorderColor" + + +typedef int ESDSDKTarget; +enum +{ + kESDSDKTarget_HardwareAndSoftware = 0, + kESDSDKTarget_HardwareOnly = 1, + kESDSDKTarget_SoftwareOnly = 2 +}; + +typedef int ESDSDKDeviceType; +enum +{ + kESDSDKDeviceType_StreamDeck = 0, + kESDSDKDeviceType_StreamDeckMini = 1 +}; + diff --git a/Sources/Common/ESDUtilities.h b/Sources/Common/ESDUtilities.h new file mode 100644 index 0000000..bc2a217 --- /dev/null +++ b/Sources/Common/ESDUtilities.h @@ -0,0 +1,28 @@ +//============================================================================== +/** +@file ESDUtilities.h + +@brief Various filesystem and other utility functions + +@copyright (c) 2018, Corsair Memory, Inc. + This source code is licensed under the MIT-style license found in the LICENSE file. + +**/ +//============================================================================== + +#pragma once + +#import + + +NS_ASSUME_NONNULL_BEGIN + + +@interface ESDUtilities : NSObject + ++(nullable NSString *)pluginPath; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/Common/ESDUtilities.m b/Sources/Common/ESDUtilities.m new file mode 100644 index 0000000..5bd5b56 --- /dev/null +++ b/Sources/Common/ESDUtilities.m @@ -0,0 +1,87 @@ +//============================================================================== +/** +@file ESDUtilities.m + +@brief Various filesystem and other utility functions + +@copyright (c) 2018, Corsair Memory, Inc. + This source code is licensed under the MIT-style license found in the LICENSE file. + +**/ +//============================================================================== + +#include "ESDUtilities.h" + + +@implementation ESDUtilities + + ++(nullable NSString *)pluginPath +{ + static NSString *sPluginPath = nil; + + if(sPluginPath == nil) + { + CFBundleRef bundleRef = CFBundleGetMainBundle(); + if(bundleRef != NULL) + { + CFURLRef executableURL = CFBundleCopyExecutableURL(bundleRef); + if(executableURL != NULL) + { + CFURLRef checkURL = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, executableURL); + while(checkURL != NULL) + { + CFStringRef lastPathComponent = CFURLCopyLastPathComponent(checkURL); + if(lastPathComponent == NULL || (CFStringCompare(lastPathComponent, CFSTR("/"), 0) == kCFCompareEqualTo) || (CFStringCompare(lastPathComponent, CFSTR(".."), 0) == kCFCompareEqualTo)) + { + if(lastPathComponent != NULL) + { + CFRelease(lastPathComponent); + } + + CFRelease(checkURL); + checkURL = NULL; + break; + } + + CFRelease(lastPathComponent); + + CFStringRef pathExtension = CFURLCopyPathExtension(checkURL); + if(pathExtension != NULL) + { + if(CFStringCompare(pathExtension, CFSTR("sdPlugin"), 0) == kCFCompareEqualTo) + { + CFStringRef path = CFURLCopyFileSystemPath(checkURL, kCFURLPOSIXPathStyle); + if(path != NULL) + { + sPluginPath = [[NSString alloc] initWithString:(__bridge NSString *)(path)]; + CFRelease(path); + } + } + + CFRelease(pathExtension); + } + + CFURLRef previousURL = checkURL; + checkURL = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, previousURL); + CFRelease(previousURL); + + if(sPluginPath != nil) + { + CFRelease(checkURL); + checkURL = NULL; + break; + } + } + + CFRelease(executableURL); + } + } + } + + return sPluginPath; +} + + +@end + diff --git a/Sources/Common/main.m b/Sources/Common/main.m new file mode 100644 index 0000000..93f75bf --- /dev/null +++ b/Sources/Common/main.m @@ -0,0 +1,91 @@ +//============================================================================== +/** +@file main.m + +@brief Parse the arg parameters and start the plugin + +@copyright (c) 2018, Corsair Memory, Inc. + This source code is licensed under the MIT-style license found in the LICENSE file. + +**/ +//============================================================================== + +#import +#import "ESDConnectionManager.h" +#import "ESDSDKDefines.h" + +#import "MyStreamDeckPlugin.h" + +int main(int argc, const char * argv[]) +{ + @autoreleasepool + { + if (argc != 9) + { + fprintf(stderr, "Invalid number of parameters %d instead of 9", argc); + return 1; + } + + int port = 0; + NSString *pluginUUID = nil; + NSString *registerEvent = nil; + NSString *info = nil; + + for(int argumentIndex = 0 ; argumentIndex < 4 ; argumentIndex++) + { + NSString *parameter = [NSString stringWithUTF8String:argv[1 + 2 * argumentIndex]]; + NSString *value = [NSString stringWithUTF8String:argv[1 + 2 * argumentIndex + 1]]; + + if ([parameter isEqualToString:@kESDSDKPortParameter]) + { + port = [value intValue]; + } + else if ([parameter isEqualToString:@kESDSDKPluginUUIDParameter]) + { + pluginUUID = value; + } + else if ([parameter isEqualToString:@kESDSDKRegisterEventParameter]) + { + registerEvent = value; + } + else if ([parameter isEqualToString:@kESDSDKInfoParameter]) + { + info = value; + } + } + + if(port == 0) + { + fprintf(stderr, "Invalid port number"); + return 1; + } + + if([pluginUUID length] <= 0) + { + fprintf(stderr, "Invalid plugin UUID"); + return 1; + } + + if([registerEvent length] <= 0) + { + fprintf(stderr, "Invalid registerEvent"); + return 1; + } + + // Create MyStreamDeckPlugin + MyStreamDeckPlugin *eventsManager = [[MyStreamDeckPlugin alloc] init]; + + // Create the connection manager + ESDConnectionManager * __unused connectionManager = [[ESDConnectionManager alloc] initWithPort:port andPluginUUID:pluginUUID andRegisterEvent:registerEvent andInfo:info andDelegate:eventsManager]; + + + // Start the runloop + BOOL keepRunning = YES; + while(keepRunning && [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + { + + } + } + + return 0; +} diff --git a/Sources/MyStreamDeckPlugin.h b/Sources/MyStreamDeckPlugin.h new file mode 100644 index 0000000..a96c7cf --- /dev/null +++ b/Sources/MyStreamDeckPlugin.h @@ -0,0 +1,39 @@ +//============================================================================== +/** +@file MyStreamDeckPlugin.h + +@brief A Stream Deck plugin displaying the number of unread emails in Apple's Mail + +@copyright (c) 2018, Corsair Memory, Inc. + This source code is licensed under the MIT-style license found in the LICENSE file. + +**/ +//============================================================================== + +#import +#import "ESDEventsProtocol.h" + +@class ESDConnectionManager; + + +NS_ASSUME_NONNULL_BEGIN + +@interface MyStreamDeckPlugin : NSObject + +@property (weak) ESDConnectionManager *connectionManager; + +- (void)keyDownForAction:(NSString *)action withContext:(id)context withPayload:(NSDictionary *)payload forDevice:(NSString *)deviceID; +- (void)keyUpForAction:(NSString *)action withContext:(id)context withPayload:(NSDictionary *)payload forDevice:(NSString *)deviceID; +- (void)willAppearForAction:(NSString *)action withContext:(id)context withPayload:(NSDictionary *)payload forDevice:(NSString *)deviceID; +- (void)willDisappearForAction:(NSString *)action withContext:(id)context withPayload:(NSDictionary *)payload forDevice:(NSString *)deviceID; + +- (void)deviceDidConnect:(NSString *)deviceID withDeviceInfo:(NSDictionary *)deviceInfo; +- (void)deviceDidDisconnect:(NSString *)deviceID; + +- (void)applicationDidLaunch:(NSDictionary *)applicationInfo; +- (void)applicationDidTerminate:(NSDictionary *)applicationInfo; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/Sources/MyStreamDeckPlugin.m b/Sources/MyStreamDeckPlugin.m new file mode 100644 index 0000000..97f90f7 --- /dev/null +++ b/Sources/MyStreamDeckPlugin.m @@ -0,0 +1,307 @@ +//============================================================================== +/** +@file MyStreamDeckPlugin.m + +@brief A Stream Deck plugin displaying the number of unread emails in Apple's Mail + +@copyright (c) 2018, Corsair Memory, Inc. + This source code is licensed under the MIT-style license found in the LICENSE file. + +**/ +//============================================================================== + +#import "MyStreamDeckPlugin.h" + +#import "ESDSDKDefines.h" +#import "ESDConnectionManager.h" +#import "ESDUtilities.h" +#import + + +// Refresh the unread count every 60s +#define REFRESH_UNREAD_COUNT_TIME_INTERVAL 60.0 + + +// Size of the images +#define IMAGE_SIZE 144 + + + +// MARK: - Utility methods + + +// +// Utility function to get the fullpath of an resource in the bundle +// +static NSString * GetResourcePath(NSString *inFilename) +{ + NSString *outPath = nil; + + if([inFilename length] > 0) + { + NSString * bundlePath = [ESDUtilities pluginPath]; + if(bundlePath != nil) + { + outPath = [bundlePath stringByAppendingPathComponent:inFilename]; + } + } + + return outPath; +} + + +// +// Utility function to create a CGContextRef +// +static CGContextRef CreateBitmapContext(CGSize inSize) +{ + CGFloat bitmapBytesPerRow = inSize.width * 4; + CGFloat bitmapByteCount = (bitmapBytesPerRow * inSize.height); + + void *bitmapData = calloc(bitmapByteCount, 1); + if(bitmapData == NULL) + { + return NULL; + } + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGContextRef context = CGBitmapContextCreate(bitmapData, inSize.width, inSize.height, 8, bitmapBytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast); + if(context == NULL) + { + CGColorSpaceRelease(colorSpace); + free(bitmapData); + return NULL; + } + else + { + CGColorSpaceRelease(colorSpace); + return context; + } +} + +// +// Utility method that takes the path of an image and create a base64 encoded string +// +static NSString * CreateBase64EncodedString(NSString *inImagePath) +{ + NSString *outBase64PNG = nil; + + NSImage* image = [[NSImage alloc] initWithContentsOfFile:inImagePath]; + if(image != nil) + { + // Find the best CGImageRef + CGSize iconSize = CGSizeMake(IMAGE_SIZE, IMAGE_SIZE); + NSRect theRect = NSMakeRect(0, 0, iconSize.width, iconSize.height); + CGImageRef imageRef = [image CGImageForProposedRect:&theRect context:NULL hints:nil]; + if(imageRef != NULL) + { + // Create a CGContext + CGContextRef context = CreateBitmapContext(iconSize); + if(context != NULL) + { + // Draw the Mail.app icon + CGContextDrawImage(context, theRect, imageRef); + + // Generate the final image + CGImageRef completeImage = CGBitmapContextCreateImage(context); + if(completeImage != NULL) + { + // Export the image to PNG + CFMutableDataRef pngData = CFDataCreateMutable(kCFAllocatorDefault, 0); + if(pngData != NULL) + { + CGImageDestinationRef destinationRef = CGImageDestinationCreateWithData(pngData, kUTTypePNG, 1, NULL); + if (destinationRef != NULL) + { + CGImageDestinationAddImage(destinationRef, completeImage, nil); + if (CGImageDestinationFinalize(destinationRef)) + { + NSString *base64PNG = [(__bridge NSData *)pngData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn]; + if([base64PNG length] > 0) + { + outBase64PNG = [NSString stringWithFormat:@"data:image/png;base64,%@\">", base64PNG]; + } + } + + CFRelease(destinationRef); + } + + CFRelease(pngData); + } + + CFRelease(completeImage); + } + + CFRelease(context); + } + } + } + + return outBase64PNG; +} + + +// MARK: - MyStreamDeckPlugin + +@interface MyStreamDeckPlugin () + +// Tells us if Apple Mail is running +@property (assign) BOOL isAppleMailRunning; + +// A timer fired each minute to update the number of unread email from Apple's Mail +@property (strong) NSTimer *refreshTimer; + +// The list of visible contexts +@property (strong) NSMutableArray *knownContexts; + +// The Mail icon encoded in base64 +@property (strong) NSString *base64MailIconString; + +// The Mail icon with a badge encoded in base64 +@property (strong) NSString *base64MailBadgeIconString; + +@end + + +@implementation MyStreamDeckPlugin + + + +// MARK: - Setup the instance variables if needed + +- (void)setupIfNeeded +{ + // Create the array of known contexts + if(_knownContexts == nil) + { + _knownContexts = [[NSMutableArray alloc] init]; + } + + // Create a timer to repetivily update the actions + if(_refreshTimer == nil) + { + _refreshTimer = [NSTimer scheduledTimerWithTimeInterval:REFRESH_UNREAD_COUNT_TIME_INTERVAL target:self selector:@selector(refreshUnreadCount) userInfo:nil repeats:YES]; + } + + if(_base64MailIconString == nil) + { + _base64MailIconString = CreateBase64EncodedString(GetResourcePath(@"MailIcon.png")); + } + + if(_base64MailBadgeIconString == nil) + { + _base64MailBadgeIconString = CreateBase64EncodedString(GetResourcePath(@"MailBadgeIcon.png")); + } +} + + +// MARK: - Refresh all actions + +- (void)refreshUnreadCount +{ + if(!self.isAppleMailRunning) + { + return; + } + + // Execute the NumberOfUnreadMails.scpt Applescript tp retrieve the number of unread emails + int numberOfUnreadEmails = 0; + NSURL* url = [NSURL fileURLWithPath:GetResourcePath(@"NumberOfUnreadMails.scpt")]; + + NSDictionary *errors = nil; + NSAppleScript* appleScript = [[NSAppleScript alloc] initWithContentsOfURL:url error:&errors]; + if(appleScript != nil) + { + NSAppleEventDescriptor *eventDescriptor = [appleScript executeAndReturnError:&errors]; + if(eventDescriptor != nil && [eventDescriptor descriptorType] != kAENullEvent) + { + numberOfUnreadEmails = (int)[eventDescriptor int32Value]; + } + } + + // Update each known context with the new value + for(NSString *context in self.knownContexts) + { + if(numberOfUnreadEmails >= 0) + { + [self.connectionManager setImage:self.base64MailBadgeIconString withContext:context withTarget:kESDSDKTarget_HardwareAndSoftware]; + [self.connectionManager setTitle:[NSString stringWithFormat:@"%d", numberOfUnreadEmails] withContext:context withTarget:kESDSDKTarget_HardwareAndSoftware]; + } + else + { + [self.connectionManager setImage:self.base64MailIconString withContext:context withTarget:kESDSDKTarget_HardwareAndSoftware]; + [self.connectionManager showAlertForContext:context]; + } + } +} + + +// MARK: - Events handler + + +- (void)keyDownForAction:(NSString *)action withContext:(id)context withPayload:(NSDictionary *)payload forDevice:(NSString *)deviceID +{ + // On key press, open the Mail.app application + NSURL* url = [NSURL fileURLWithPath:GetResourcePath(@"OpenMail.scpt")]; + + NSDictionary *errors = nil; + NSAppleScript* appleScript = [[NSAppleScript alloc] initWithContentsOfURL:url error:&errors]; + if(appleScript != nil) + { + [appleScript executeAndReturnError:&errors]; + } +} + +- (void)keyUpForAction:(NSString *)action withContext:(id)context withPayload:(NSDictionary *)payload forDevice:(NSString *)deviceID +{ + // Nothing to do +} + +- (void)willAppearForAction:(NSString *)action withContext:(id)context withPayload:(NSDictionary *)payload forDevice:(NSString *)deviceID +{ + // Set up the instance variables if needed + [self setupIfNeeded]; + + // Add the context to the list of known contexts + [self.knownContexts addObject:context]; + + // Explicitely refresh the number of unread emails + [self refreshUnreadCount]; +} + +- (void)willDisappearForAction:(NSString *)action withContext:(id)context withPayload:(NSDictionary *)payload forDevice:(NSString *)deviceID +{ + // Remove the context from the list of known contexts + [self.knownContexts removeObject:context]; +} + +- (void)deviceDidConnect:(NSString *)deviceID withDeviceInfo:(NSDictionary *)deviceInfo +{ + // Nothing to do +} + +- (void)deviceDidDisconnect:(NSString *)deviceID +{ + // Nothing to do +} + +- (void)applicationDidLaunch:(NSDictionary *)applicationInfo +{ + if([applicationInfo[@kESDSDKPayloadApplication] isEqualToString:@"com.apple.mail"]) + { + self.isAppleMailRunning = YES; + + // Explicitely refresh the number of unread emails + [self refreshUnreadCount]; + } +} + +- (void)applicationDidTerminate:(NSDictionary *)applicationInfo +{ + if([applicationInfo[@kESDSDKPayloadApplication] isEqualToString:@"com.apple.mail"]) + { + self.isAppleMailRunning = NO; + } +} + +@end diff --git a/Sources/Vendor/SocketRocket/CONTRIBUTING.md b/Sources/Vendor/SocketRocket/CONTRIBUTING.md new file mode 100755 index 0000000..1fa59e7 --- /dev/null +++ b/Sources/Vendor/SocketRocket/CONTRIBUTING.md @@ -0,0 +1,33 @@ +# Contributing to SocketRocket +We want to make contributing to this project as easy and transparent as possible. + +## Pull Requests +We actively welcome your pull requests. + +1. Fork the repo and create your branch from `master`. +2. If you've added code that should be tested, add tests. +3. If you've changed APIs, update the documentation. +4. Ensure the test suite passes. +5. Make sure your code lints. +6. If you haven't already, complete the Contributor License Agreement ("CLA"). + +## Contributor License Agreement ("CLA") +In order to accept your pull request, we need you to submit a CLA. +You only need to do this once to work on any of Facebook's open source projects. + +Complete your CLA here: + +## Issues +We use GitHub issues to track public bugs. Please ensure your description is +clear and has sufficient instructions to be able to reproduce the issue. + +Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe +disclosure of security bugs. In those cases, please go through the process +outlined on that page and do not file a public issue. + +## Coding Style +* Most importantly, match the existing code style as much as possible. +* Try to keep lines under 140 characters, if possible. + +## License +By contributing to SocketRocket, you agree that your contributions will be licensed under its BSD license. \ No newline at end of file diff --git a/Sources/Vendor/SocketRocket/Configurations/SocketRocket-iOS-Dynamic.xcconfig b/Sources/Vendor/SocketRocket/Configurations/SocketRocket-iOS-Dynamic.xcconfig new file mode 100755 index 0000000..526b259 --- /dev/null +++ b/Sources/Vendor/SocketRocket/Configurations/SocketRocket-iOS-Dynamic.xcconfig @@ -0,0 +1,17 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#include "Shared/Platform/iOS.xcconfig" +#include "Shared/Product/DynamicFramework.xcconfig" + +PRODUCT_NAME = SocketRocket +PRODUCT_BUNDLE_IDENTIFIER = com.facebook.socketrocket.ios +IPHONEOS_DEPLOYMENT_TARGET = 8.0 + +INFOPLIST_FILE = $(SRCROOT)/SocketRocket/Resources/Info.plist diff --git a/Sources/Vendor/SocketRocket/Configurations/SocketRocket-iOS.xcconfig b/Sources/Vendor/SocketRocket/Configurations/SocketRocket-iOS.xcconfig new file mode 100755 index 0000000..d4af81d --- /dev/null +++ b/Sources/Vendor/SocketRocket/Configurations/SocketRocket-iOS.xcconfig @@ -0,0 +1,20 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#include "Shared/Platform/iOS.xcconfig" +#include "Shared/Product/StaticFramework.xcconfig" + +PRODUCT_NAME = SocketRocket +PRODUCT_BUNDLE_IDENTIFIER = com.facebook.socketrocket.ios +IPHONEOS_DEPLOYMENT_TARGET = 6.0 + +INFOPLIST_FILE = $(SRCROOT)/SocketRocket/Resources/Info.plist + +OTHER_CFLAGS[sdk=iphoneos9.*] = $(inherited) -fembed-bitcode +OTHER_LDFLAGS = $(inherited) -Licucore diff --git a/Sources/Vendor/SocketRocket/Configurations/SocketRocket-macOS.xcconfig b/Sources/Vendor/SocketRocket/Configurations/SocketRocket-macOS.xcconfig new file mode 100755 index 0000000..7308c45 --- /dev/null +++ b/Sources/Vendor/SocketRocket/Configurations/SocketRocket-macOS.xcconfig @@ -0,0 +1,17 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#include "Shared/Platform/macOS.xcconfig" +#include "Shared/Product/DynamicFramework.xcconfig" + +PRODUCT_NAME = SocketRocket +PRODUCT_BUNDLE_IDENTIFIER = com.facebook.socketrocket.macos +MACOSX_DEPLOYMENT_TARGET = 10.8 + +INFOPLIST_FILE = $(SRCROOT)/SocketRocket/Resources/Info.plist diff --git a/Sources/Vendor/SocketRocket/Configurations/SocketRocket-tvOS.xcconfig b/Sources/Vendor/SocketRocket/Configurations/SocketRocket-tvOS.xcconfig new file mode 100755 index 0000000..12e64bf --- /dev/null +++ b/Sources/Vendor/SocketRocket/Configurations/SocketRocket-tvOS.xcconfig @@ -0,0 +1,16 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#include "Shared/Platform/tvOS.xcconfig" +#include "Shared/Product/DynamicFramework.xcconfig" + +PRODUCT_NAME = SocketRocket +PRODUCT_BUNDLE_IDENTIFIER = com.facebook.socketrocket.tvos + +INFOPLIST_FILE = $(SRCROOT)/SocketRocket/Resources/Info.plist diff --git a/Sources/Vendor/SocketRocket/Configurations/SocketRocketTests-iOS.xcconfig b/Sources/Vendor/SocketRocket/Configurations/SocketRocketTests-iOS.xcconfig new file mode 100755 index 0000000..7f02fc2 --- /dev/null +++ b/Sources/Vendor/SocketRocket/Configurations/SocketRocketTests-iOS.xcconfig @@ -0,0 +1,19 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#include "Shared/Platform/iOS.xcconfig" +#include "Shared/Product/LogicTests.xcconfig" + +PRODUCT_NAME = SocketRocketTests-iOS +PRODUCT_MODULE_NAME = SocketRocketTests +PRODUCT_BUNDLE_IDENTIFIER = com.facebook.socketrocket.tests.ios + +IPHONEOS_DEPLOYMENT_TARGET = 7.0 + +INFOPLIST_FILE = $(SRCROOT)/Tests/Resources/Info.plist diff --git a/Sources/Vendor/SocketRocket/Configurations/TestChat-iOS.xcconfig b/Sources/Vendor/SocketRocket/Configurations/TestChat-iOS.xcconfig new file mode 100755 index 0000000..a12622f --- /dev/null +++ b/Sources/Vendor/SocketRocket/Configurations/TestChat-iOS.xcconfig @@ -0,0 +1,19 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#include "Shared/Platform/iOS.xcconfig" +#include "Shared/Product/Application.xcconfig" + +PRODUCT_NAME = TestChat +PRODUCT_MODULE_NAME = TestChat +PRODUCT_BUNDLE_IDENTIFIER = com.facebook.socketrocket.testchat + +IPHONEOS_DEPLOYMENT_TARGET = 8.0 + +INFOPLIST_FILE = $(SRCROOT)/TestChat/TestChat-Info.plist diff --git a/Sources/Vendor/SocketRocket/Gemfile b/Sources/Vendor/SocketRocket/Gemfile new file mode 100755 index 0000000..43c41f9 --- /dev/null +++ b/Sources/Vendor/SocketRocket/Gemfile @@ -0,0 +1,4 @@ +source 'https://rubygems.org' + +gem 'cocoapods' +gem 'xcpretty' diff --git a/Sources/Vendor/SocketRocket/LICENSE b/Sources/Vendor/SocketRocket/LICENSE new file mode 100755 index 0000000..032b206 --- /dev/null +++ b/Sources/Vendor/SocketRocket/LICENSE @@ -0,0 +1,30 @@ +BSD License + +For SocketRocket software + +Copyright (c) 2016-present, Facebook, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name Facebook nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/Sources/Vendor/SocketRocket/LICENSE-examples b/Sources/Vendor/SocketRocket/LICENSE-examples new file mode 100755 index 0000000..1de7813 --- /dev/null +++ b/Sources/Vendor/SocketRocket/LICENSE-examples @@ -0,0 +1,11 @@ +Copyright (c) 2016-present, Facebook, Inc. All rights reserved. + +The examples provided by Facebook are for non-commercial testing and evaluation +purposes only. Facebook reserves all rights not expressly granted. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Sources/Vendor/SocketRocket/Makefile b/Sources/Vendor/SocketRocket/Makefile new file mode 100755 index 0000000..20e5e9b --- /dev/null +++ b/Sources/Vendor/SocketRocket/Makefile @@ -0,0 +1,30 @@ +TEST_SCENARIOS="[1-8]*" +TEST_URL='ws://localhost:9001/' + +all: + $(MAKE) -C SocketRocket + +clean: + $(MAKE) -C SocketRocket clean + +.env: + + ./TestSupport/setup_env.sh .env + +test: .env + + mkdir -p pages/results + bash ./TestSupport/run_test_server.sh $(TEST_SCENARIOS) $(TEST_URL) Debug || open pages/results/index.html && false + open pages/results/index.html + +test_all: .env + + mkdir -p pages/results + bash ./TestSupport/run_test_server.sh '*' $(TEST_URL) Debug || open pages/results/index.html && false + open pages/results/index.html + +test_perf: .env + + mkdir -p pages/results + bash ./TestSupport/run_test_server.sh '9.*' $(TEST_URL) Release || open pages/results/index.html && false + open pages/results/index.html diff --git a/Sources/Vendor/SocketRocket/PATENTS b/Sources/Vendor/SocketRocket/PATENTS new file mode 100755 index 0000000..6c116ef --- /dev/null +++ b/Sources/Vendor/SocketRocket/PATENTS @@ -0,0 +1,33 @@ +Additional Grant of Patent Rights Version 2 + +"Software" means the SocketRocket software distributed by Facebook, Inc. + +Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software +("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable +(subject to the termination provision below) license under any Necessary +Claims, to make, have made, use, sell, offer to sell, import, and otherwise +transfer the Software. For avoidance of doubt, no license is granted under +Facebook’s rights in any patent claims that are infringed by (i) modifications +to the Software made by you or any third party or (ii) the Software in +combination with any software or other technology. + +The license granted hereunder will terminate, automatically and without notice, +if you (or any of your subsidiaries, corporate affiliates or agents) initiate +directly or indirectly, or take a direct financial interest in, any Patent +Assertion: (i) against Facebook or any of its subsidiaries or corporate +affiliates, (ii) against any party if such Patent Assertion arises in whole or +in part from any software, technology, product or service of Facebook or any of +its subsidiaries or corporate affiliates, or (iii) against any party relating +to the Software. Notwithstanding the foregoing, if Facebook or any of its +subsidiaries or corporate affiliates files a lawsuit alleging patent +infringement against you in the first instance, and you respond by filing a +patent infringement counterclaim in that lawsuit against that party that is +unrelated to the Software, the license granted hereunder will not terminate +under section (i) of this paragraph due to such counterclaim. + +A "Necessary Claim" is a claim of a patent owned by Facebook that is +necessarily infringed by the Software standing alone. + +A "Patent Assertion" is any lawsuit or other action alleging direct, indirect, +or contributory infringement or inducement to infringe any patent, including a +cross-claim or counterclaim. diff --git a/Sources/Vendor/SocketRocket/README.md b/Sources/Vendor/SocketRocket/README.md new file mode 100755 index 0000000..0264af6 --- /dev/null +++ b/Sources/Vendor/SocketRocket/README.md @@ -0,0 +1,213 @@ +# SocketRocket + +![Platforms][platforms-svg] +[![License][license-svg]][license-link] + +[![Podspec][podspec-svg]][podspec-link] +[![Carthage Compatible][carthage-svg]](carthage-link) + +[![Build Status][build-status-svg]][build-status-link] + +A conforming WebSocket ([RFC 6455](https://tools.ietf.org/html/rfc6455>)) client library for iOS, macOS and tvOS. + +Test results for SocketRocket [here](http://facebook.github.io/SocketRocket/results/). +You can compare to what modern browsers look like [here](http://autobahn.ws/testsuite/reports/clients/index.html). + +SocketRocket currently conforms to all core ~300 of [Autobahn](http://autobahn.ws/testsuite/>)'s fuzzing tests +(aside from two UTF-8 ones where it is merely *non-strict* tests 6.4.2 and 6.4.4). + +## Features/Design + +- TLS (wss) support, including self-signed certificates. +- Seems to perform quite well. +- Supports HTTP Proxies. +- Supports IPv4/IPv6. +- Supports SSL certificate pinning. +- Sends `ping` and can process `pong` events. +- Asynchronous and non-blocking. Most of the work is done on a background thread. +- Supports iOS, macOS, tvOS. + +## Installing + +There are a few options. Choose one, or just figure it out: + +- **[CocoaPods](https://cocoapods.org)** + + Add the following line to your Podfile: + ```ruby + pod 'SocketRocket' + ``` + Run `pod install`, and you are all set. + +- **[Carthage](https://github.com/carthage/carthage)** + + Add the following line to your Cartfile: + ``` + github "facebook/SocketRocket" + ``` + Run `carthage update`, and you should now have the latest version of `SocketRocket` in your `Carthage` folder. + +- **Using SocketRocket as a sub-project** + + You can also include `SocketRocket` as a subproject inside of your application if you'd prefer, although we do not recommend this, as it will increase your indexing time significantly. To do so, just drag and drop the `SocketRocket.xcodeproj` file into your workspace. + +## API + +### `SRWebSocket` + +The Web Socket. + +#### Note: + +`SRWebSocket` will retain itself between `-(void)open` and when it closes, errors, or fails. +This is similar to how `NSURLConnection` behaves (unlike `NSURLConnection`, `SRWebSocket` won't retain the delegate). + +#### Interface + +```objective-c +@interface SRWebSocket : NSObject + +// Make it with this +- (instancetype)initWithURLRequest:(NSURLRequest *)request; + +// Set this before opening +@property (nonatomic, weak) id delegate; + +// Open with this +- (void)open; + +// Close it with this +- (void)close; + +// Send a Data +- (void)sendData:(nullable NSData *)data error:(NSError **)error; + +// Send a UTF8 String +- (void)sendString:(NSString *)string error:(NSError **)error; + +@end +``` + +### `SRWebSocketDelegate` + +You implement this + +```objective-c +@protocol SRWebSocketDelegate + +@optional + +- (void)webSocketDidOpen:(SRWebSocket *)webSocket; + +- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessageWithString:(NSString *)string; +- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessageWithData:(NSData *)data; + +- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error; +- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(nullable NSString *)reason wasClean:(BOOL)wasClean; + +@end +``` + +## Testing + +Included are setup scripts for the python testing environment. +It comes packaged with vitualenv so all the dependencies are installed in userland. + +To run the short test from the command line, run: +```bash + make test +``` + +To run all the tests, run: +```bash + make test_all +``` + +The short tests don't include the performance tests +(the test harness is actually the bottleneck, not SocketRocket). + +The first time this is run, it may take a while to install the dependencies. It will be smooth sailing after that. + +You can also run tests inside Xcode, which runs the same thing, but makes it easier to debug. + +- Choose the `SocketRocket` target +- Run the test action (`⌘+U`) + +### TestChat Demo Application + +SocketRocket includes a demo app, TestChat. +It will "chat" with a listening websocket on port 9900. + +#### TestChat Server + +The sever takes a message and broadcasts it to all other connected clients. + +It requires some dependencies though to run. +We also want to reuse the virtualenv we made when we ran the tests. +If you haven't run the tests yet, go into the SocketRocket root directory and type: + +```bash +make test +``` + +This will set up your [virtualenv](https://pypi.python.org/pypi/virtualenv). + +Now, in your terminal: + +```bash +source .env/bin/activate +pip install git+https://github.com/tornadoweb/tornado.git +``` + +In the same terminal session, start the chatroom server: + +```bash +python TestChatServer/py/chatroom.py +``` + +There's also a Go implementation (with the latest weekly) where you can: + +```bash +cd TestChatServer/go +go run chatroom.go +``` + +#### Chatting + +Now, start TestChat.app (just run the target in the Xcode project). +If you had it started already you can hit the refresh button to reconnect. +It should say "Connected!" on top. + +To talk with the app, open up your browser to [http://localhost:9000](http://localhost:9000) and start chatting. + + +## WebSocket Server Implementation Recommendations + +SocketRocket has been used with the following libraries: + +- [Tornado](https://github.com/tornadoweb/tornado) +- Go's [WebSocket package](https://godoc.org/golang.org/x/net/websocket) or Gorilla's [version](http://www.gorillatoolkit.org/pkg/websocket). +- [Autobahn](http://autobahn.ws/testsuite/) (using its fuzzing client). + +The Tornado one is dirt simple and works like a charm. +([IPython notebook](http://ipython.org/ipython-doc/dev/interactive/htmlnotebook.html) uses it too). +It's much easier to configure handlers and routes than in Autobahn/twisted. + +## Contributing + +We’re glad you’re interested in SocketRocket, and we’d love to see where you take it. +Please read our [contributing guidelines](https://github.com/facebook/SocketRocket/blob/master/CONTRIBUTING.md) prior to submitting a Pull Request. + + [build-status-svg]: https://img.shields.io/travis/facebook/SocketRocket/master.svg + [build-status-link]: https://travis-ci.org/facebook/SocketRocket/branches + + [license-svg]: https://img.shields.io/badge/license-BSD-lightgrey.svg + [license-link]: https://github.com/facebook/SocketRocket/blob/master/LICENSE + + [podspec-svg]: https://img.shields.io/cocoapods/v/SocketRocket.svg + [podspec-link]: https://cocoapods.org/pods/SocketRocket + + [carthage-svg]: https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat + [carthage-link]: https://github.com/carthage/carthage + + [platforms-svg]: http://img.shields.io/cocoapods/p/SocketRocket.svg?style=flat diff --git a/Sources/Vendor/SocketRocket/SocketRocket.podspec b/Sources/Vendor/SocketRocket/SocketRocket.podspec new file mode 100755 index 0000000..0dbb39d --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket.podspec @@ -0,0 +1,22 @@ +Pod::Spec.new do |s| + s.name = 'SocketRocket' + s.version = '0.5.1' + s.summary = 'A conforming WebSocket (RFC 6455) client library for iOS, macOS and tvOS.' + s.homepage = 'https://github.com/facebook/SocketRocket' + s.authors = { 'Nikita Lutsenko' => 'nlutsenko@me.com', 'Dan Federman' => 'federman@squareup.com', 'Mike Lewis' => 'mikelikespie@gmail.com' } + s.license = 'BSD' + s.source = { :git => 'https://github.com/facebook/SocketRocket.git', :tag => s.version.to_s } + s.requires_arc = true + + s.source_files = 'SocketRocket/**/*.{h,m}' + s.public_header_files = 'SocketRocket/*.h' + + s.ios.deployment_target = '6.0' + s.osx.deployment_target = '10.8' + s.tvos.deployment_target = '9.0' + + s.ios.frameworks = 'CFNetwork', 'Security' + s.osx.frameworks = 'CoreServices', 'Security' + s.tvos.frameworks = 'CFNetwork', 'Security' + s.libraries = 'icucore' +end diff --git a/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/project.pbxproj b/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/project.pbxproj new file mode 100755 index 0000000..30aa8a4 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/project.pbxproj @@ -0,0 +1,1347 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 2D42277F1BB4365C000C1A6C /* SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = F6A12CCF145119B700C1D980 /* SRWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2D4227801BB43693000C1A6C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B208301450F597009315AF /* Foundation.framework */; }; + 2D4227831BB436B1000C1A6C /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD3145122FC00C1D980 /* Security.framework */; }; + 2D4227851BB43734000C1A6C /* SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = F6A12CD0145119B700C1D980 /* SRWebSocket.m */; }; + 3345DC841C52ACD70083CCB8 /* SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = F6A12CD0145119B700C1D980 /* SRWebSocket.m */; }; + 3345DC871C52ACD70083CCB8 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD3145122FC00C1D980 /* Security.framework */; }; + 3345DC881C52ACD70083CCB8 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B208301450F597009315AF /* Foundation.framework */; }; + 3345DC8A1C52ACD70083CCB8 /* SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = F6A12CCF145119B700C1D980 /* SRWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 454A02D61D0FAD010060DFB2 /* SRSecurityPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = 454A02D41D0FAD010060DFB2 /* SRSecurityPolicy.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 454A02D71D0FAD010060DFB2 /* SRSecurityPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = 454A02D41D0FAD010060DFB2 /* SRSecurityPolicy.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 454A02D81D0FAD010060DFB2 /* SRSecurityPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = 454A02D41D0FAD010060DFB2 /* SRSecurityPolicy.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 454FEA7D1D2570F600073768 /* SRPinningSecurityPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = 454FEA791D2570D400073768 /* SRPinningSecurityPolicy.h */; }; + 454FEA7E1D2570F600073768 /* SRPinningSecurityPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = 454FEA7A1D2570D400073768 /* SRPinningSecurityPolicy.m */; }; + 454FEA7F1D2570F800073768 /* SRPinningSecurityPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = 454FEA791D2570D400073768 /* SRPinningSecurityPolicy.h */; }; + 454FEA801D2570F800073768 /* SRPinningSecurityPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = 454FEA7A1D2570D400073768 /* SRPinningSecurityPolicy.m */; }; + 454FEA811D2570F900073768 /* SRPinningSecurityPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = 454FEA791D2570D400073768 /* SRPinningSecurityPolicy.h */; }; + 454FEA821D2570F900073768 /* SRPinningSecurityPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = 454FEA7A1D2570D400073768 /* SRPinningSecurityPolicy.m */; }; + 454FEA851D25719900073768 /* SRSecurityPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = 454FEA831D25717C00073768 /* SRSecurityPolicy.m */; }; + 454FEA861D25719A00073768 /* SRSecurityPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = 454FEA831D25717C00073768 /* SRSecurityPolicy.m */; }; + 454FEA871D25719A00073768 /* SRSecurityPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = 454FEA831D25717C00073768 /* SRSecurityPolicy.m */; }; + 8105E4801CDD67B400AA12DB /* SRAutobahnTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8105E47A1CDD679A00AA12DB /* SRAutobahnTests.m */; }; + 8105E4821CDD67BD00AA12DB /* SRTWebSocketOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 8105E4771CDD679A00AA12DB /* SRTWebSocketOperation.m */; }; + 8105E4AE1CDD6E6200AA12DB /* SRAutobahnOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 8105E4AD1CDD6E6200AA12DB /* SRAutobahnOperation.m */; }; + 8105E5281CDD98E100AA12DB /* autobahn_configuration.json in Resources */ = {isa = PBXBuildFile; fileRef = 8105E5271CDD98E100AA12DB /* autobahn_configuration.json */; }; + 8117C4231D3076DF00784D79 /* NSURLRequest+SRWebSocketPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 8117C4221D3076DF00784D79 /* NSURLRequest+SRWebSocketPrivate.h */; }; + 8117C4241D3076DF00784D79 /* NSURLRequest+SRWebSocketPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 8117C4221D3076DF00784D79 /* NSURLRequest+SRWebSocketPrivate.h */; }; + 8117C4251D3076DF00784D79 /* NSURLRequest+SRWebSocketPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 8117C4221D3076DF00784D79 /* NSURLRequest+SRWebSocketPrivate.h */; }; + 8117C4261D3076DF00784D79 /* NSURLRequest+SRWebSocketPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 8117C4221D3076DF00784D79 /* NSURLRequest+SRWebSocketPrivate.h */; }; + 8117C4301D30779900784D79 /* NSRunLoop+SRWebSocketPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 8117C42F1D30779900784D79 /* NSRunLoop+SRWebSocketPrivate.h */; }; + 8117C4311D30779900784D79 /* NSRunLoop+SRWebSocketPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 8117C42F1D30779900784D79 /* NSRunLoop+SRWebSocketPrivate.h */; }; + 8117C4321D30779900784D79 /* NSRunLoop+SRWebSocketPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 8117C42F1D30779900784D79 /* NSRunLoop+SRWebSocketPrivate.h */; }; + 8117C4331D30779900784D79 /* NSRunLoop+SRWebSocketPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 8117C42F1D30779900784D79 /* NSRunLoop+SRWebSocketPrivate.h */; }; + 811934BC1CDAF725003AB243 /* SocketRocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 555E0EB11C51E56D00E6BB92 /* SocketRocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 811934BE1CDAF725003AB243 /* SocketRocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 555E0EB11C51E56D00E6BB92 /* SocketRocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 811934C01CDAF726003AB243 /* SocketRocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 555E0EB11C51E56D00E6BB92 /* SocketRocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 813364001D091E170062E28D /* SRProxyConnect.h in Headers */ = {isa = PBXBuildFile; fileRef = 4861E7731D022211002FAB1D /* SRProxyConnect.h */; }; + 813364041D091E170062E28D /* SRProxyConnect.h in Headers */ = {isa = PBXBuildFile; fileRef = 4861E7731D022211002FAB1D /* SRProxyConnect.h */; }; + 813364081D091E180062E28D /* SRProxyConnect.h in Headers */ = {isa = PBXBuildFile; fileRef = 4861E7731D022211002FAB1D /* SRProxyConnect.h */; }; + 8133640C1D091E1B0062E28D /* SRProxyConnect.m in Sources */ = {isa = PBXBuildFile; fileRef = 4861E7741D022211002FAB1D /* SRProxyConnect.m */; }; + 8133640E1D091E1B0062E28D /* SRProxyConnect.m in Sources */ = {isa = PBXBuildFile; fileRef = 4861E7741D022211002FAB1D /* SRProxyConnect.m */; }; + 8133640F1D091E1C0062E28D /* SRProxyConnect.m in Sources */ = {isa = PBXBuildFile; fileRef = 4861E7741D022211002FAB1D /* SRProxyConnect.m */; }; + 815FE7261D497D720085FDA5 /* SRConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 815FE7241D497D720085FDA5 /* SRConstants.h */; }; + 815FE7271D497D720085FDA5 /* SRConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 815FE7241D497D720085FDA5 /* SRConstants.h */; }; + 815FE7281D497D720085FDA5 /* SRConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 815FE7241D497D720085FDA5 /* SRConstants.h */; }; + 815FE7291D497D720085FDA5 /* SRConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 815FE7241D497D720085FDA5 /* SRConstants.h */; }; + 815FE72A1D497D720085FDA5 /* SRConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 815FE7251D497D720085FDA5 /* SRConstants.m */; }; + 815FE72B1D497D720085FDA5 /* SRConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 815FE7251D497D720085FDA5 /* SRConstants.m */; }; + 815FE72C1D497D720085FDA5 /* SRConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 815FE7251D497D720085FDA5 /* SRConstants.m */; }; + 815FE72D1D497D720085FDA5 /* SRConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 815FE7251D497D720085FDA5 /* SRConstants.m */; }; + 817491A91D1C8C33006E09DF /* SRMutex.h in Headers */ = {isa = PBXBuildFile; fileRef = 817491A61D1C8C33006E09DF /* SRMutex.h */; }; + 817491AA1D1C8C33006E09DF /* SRMutex.h in Headers */ = {isa = PBXBuildFile; fileRef = 817491A61D1C8C33006E09DF /* SRMutex.h */; }; + 817491AB1D1C8C33006E09DF /* SRMutex.h in Headers */ = {isa = PBXBuildFile; fileRef = 817491A61D1C8C33006E09DF /* SRMutex.h */; }; + 817491AD1D1C8C33006E09DF /* SRMutex.m in Sources */ = {isa = PBXBuildFile; fileRef = 817491A71D1C8C33006E09DF /* SRMutex.m */; }; + 817491AE1D1C8C33006E09DF /* SRMutex.m in Sources */ = {isa = PBXBuildFile; fileRef = 817491A71D1C8C33006E09DF /* SRMutex.m */; }; + 817491AF1D1C8C33006E09DF /* SRMutex.m in Sources */ = {isa = PBXBuildFile; fileRef = 817491A71D1C8C33006E09DF /* SRMutex.m */; }; + 817995871CE139700084DA37 /* SRDelegateController.h in Headers */ = {isa = PBXBuildFile; fileRef = 817995841CE139700084DA37 /* SRDelegateController.h */; }; + 817995881CE139700084DA37 /* SRDelegateController.h in Headers */ = {isa = PBXBuildFile; fileRef = 817995841CE139700084DA37 /* SRDelegateController.h */; }; + 817995891CE139700084DA37 /* SRDelegateController.h in Headers */ = {isa = PBXBuildFile; fileRef = 817995841CE139700084DA37 /* SRDelegateController.h */; }; + 8179958B1CE139700084DA37 /* SRDelegateController.m in Sources */ = {isa = PBXBuildFile; fileRef = 817995851CE139700084DA37 /* SRDelegateController.m */; }; + 8179958C1CE139700084DA37 /* SRDelegateController.m in Sources */ = {isa = PBXBuildFile; fileRef = 817995851CE139700084DA37 /* SRDelegateController.m */; }; + 8179958D1CE139700084DA37 /* SRDelegateController.m in Sources */ = {isa = PBXBuildFile; fileRef = 817995851CE139700084DA37 /* SRDelegateController.m */; }; + 817996801CE184F40084DA37 /* SRAutobahnUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 8179967F1CE184F40084DA37 /* SRAutobahnUtilities.m */; }; + 81900A4D1D18C9CC0015A290 /* SRLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 81900A4A1D18C9CC0015A290 /* SRLog.h */; }; + 81900A4E1D18C9CC0015A290 /* SRLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 81900A4A1D18C9CC0015A290 /* SRLog.h */; }; + 81900A4F1D18C9CC0015A290 /* SRLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 81900A4A1D18C9CC0015A290 /* SRLog.h */; }; + 81900A511D18C9CC0015A290 /* SRLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 81900A4B1D18C9CC0015A290 /* SRLog.m */; }; + 81900A521D18C9CC0015A290 /* SRLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 81900A4B1D18C9CC0015A290 /* SRLog.m */; }; + 81900A531D18C9CC0015A290 /* SRLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 81900A4B1D18C9CC0015A290 /* SRLog.m */; }; + 81AFCD661D4C431C00B3AFC9 /* libicucore.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 81AFCD651D4C431C00B3AFC9 /* libicucore.tbd */; }; + 81B22EC61CE42D7E0073C636 /* SRError.h in Headers */ = {isa = PBXBuildFile; fileRef = 81B22EC31CE42D7E0073C636 /* SRError.h */; }; + 81B22EC71CE42D7E0073C636 /* SRError.h in Headers */ = {isa = PBXBuildFile; fileRef = 81B22EC31CE42D7E0073C636 /* SRError.h */; }; + 81B22EC81CE42D7E0073C636 /* SRError.h in Headers */ = {isa = PBXBuildFile; fileRef = 81B22EC31CE42D7E0073C636 /* SRError.h */; }; + 81B22ECA1CE42D7E0073C636 /* SRError.m in Sources */ = {isa = PBXBuildFile; fileRef = 81B22EC41CE42D7E0073C636 /* SRError.m */; }; + 81B22ECB1CE42D7E0073C636 /* SRError.m in Sources */ = {isa = PBXBuildFile; fileRef = 81B22EC41CE42D7E0073C636 /* SRError.m */; }; + 81B22ECC1CE42D7E0073C636 /* SRError.m in Sources */ = {isa = PBXBuildFile; fileRef = 81B22EC41CE42D7E0073C636 /* SRError.m */; }; + 81B22EE51CE43ECC0073C636 /* SRURLUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 81B22EE21CE43ECC0073C636 /* SRURLUtilities.h */; }; + 81B22EE61CE43ECC0073C636 /* SRURLUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 81B22EE21CE43ECC0073C636 /* SRURLUtilities.h */; }; + 81B22EE71CE43ECC0073C636 /* SRURLUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 81B22EE21CE43ECC0073C636 /* SRURLUtilities.h */; }; + 81B22EE91CE43ECC0073C636 /* SRURLUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 81B22EE31CE43ECC0073C636 /* SRURLUtilities.m */; }; + 81B22EEA1CE43ECC0073C636 /* SRURLUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 81B22EE31CE43ECC0073C636 /* SRURLUtilities.m */; }; + 81B22EEB1CE43ECC0073C636 /* SRURLUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 81B22EE31CE43ECC0073C636 /* SRURLUtilities.m */; }; + 81B31C151CDC404100D86D43 /* SRIOConsumer.h in Headers */ = {isa = PBXBuildFile; fileRef = 81B31C0F1CDC404100D86D43 /* SRIOConsumer.h */; }; + 81B31C161CDC404100D86D43 /* SRIOConsumer.h in Headers */ = {isa = PBXBuildFile; fileRef = 81B31C0F1CDC404100D86D43 /* SRIOConsumer.h */; }; + 81B31C171CDC404100D86D43 /* SRIOConsumer.h in Headers */ = {isa = PBXBuildFile; fileRef = 81B31C0F1CDC404100D86D43 /* SRIOConsumer.h */; }; + 81B31C191CDC404100D86D43 /* SRIOConsumer.m in Sources */ = {isa = PBXBuildFile; fileRef = 81B31C101CDC404100D86D43 /* SRIOConsumer.m */; }; + 81B31C1A1CDC404100D86D43 /* SRIOConsumer.m in Sources */ = {isa = PBXBuildFile; fileRef = 81B31C101CDC404100D86D43 /* SRIOConsumer.m */; }; + 81B31C1B1CDC404100D86D43 /* SRIOConsumer.m in Sources */ = {isa = PBXBuildFile; fileRef = 81B31C101CDC404100D86D43 /* SRIOConsumer.m */; }; + 81B31C1D1CDC404100D86D43 /* SRIOConsumerPool.h in Headers */ = {isa = PBXBuildFile; fileRef = 81B31C111CDC404100D86D43 /* SRIOConsumerPool.h */; }; + 81B31C1E1CDC404100D86D43 /* SRIOConsumerPool.h in Headers */ = {isa = PBXBuildFile; fileRef = 81B31C111CDC404100D86D43 /* SRIOConsumerPool.h */; }; + 81B31C1F1CDC404100D86D43 /* SRIOConsumerPool.h in Headers */ = {isa = PBXBuildFile; fileRef = 81B31C111CDC404100D86D43 /* SRIOConsumerPool.h */; }; + 81B31C211CDC404100D86D43 /* SRIOConsumerPool.m in Sources */ = {isa = PBXBuildFile; fileRef = 81B31C121CDC404100D86D43 /* SRIOConsumerPool.m */; }; + 81B31C221CDC404100D86D43 /* SRIOConsumerPool.m in Sources */ = {isa = PBXBuildFile; fileRef = 81B31C121CDC404100D86D43 /* SRIOConsumerPool.m */; }; + 81B31C231CDC404100D86D43 /* SRIOConsumerPool.m in Sources */ = {isa = PBXBuildFile; fileRef = 81B31C121CDC404100D86D43 /* SRIOConsumerPool.m */; }; + 81B31C2E1CDC406B00D86D43 /* SRHash.h in Headers */ = {isa = PBXBuildFile; fileRef = 81B31C2B1CDC406B00D86D43 /* SRHash.h */; }; + 81B31C2F1CDC406B00D86D43 /* SRHash.h in Headers */ = {isa = PBXBuildFile; fileRef = 81B31C2B1CDC406B00D86D43 /* SRHash.h */; }; + 81B31C301CDC406B00D86D43 /* SRHash.h in Headers */ = {isa = PBXBuildFile; fileRef = 81B31C2B1CDC406B00D86D43 /* SRHash.h */; }; + 81B31C321CDC406B00D86D43 /* SRHash.m in Sources */ = {isa = PBXBuildFile; fileRef = 81B31C2C1CDC406B00D86D43 /* SRHash.m */; }; + 81B31C331CDC406B00D86D43 /* SRHash.m in Sources */ = {isa = PBXBuildFile; fileRef = 81B31C2C1CDC406B00D86D43 /* SRHash.m */; }; + 81B31C341CDC406B00D86D43 /* SRHash.m in Sources */ = {isa = PBXBuildFile; fileRef = 81B31C2C1CDC406B00D86D43 /* SRHash.m */; }; + 81B31C601CDC444900D86D43 /* SRRunLoopThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 81B31C5D1CDC444900D86D43 /* SRRunLoopThread.h */; }; + 81B31C611CDC444900D86D43 /* SRRunLoopThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 81B31C5D1CDC444900D86D43 /* SRRunLoopThread.h */; }; + 81B31C621CDC444900D86D43 /* SRRunLoopThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 81B31C5D1CDC444900D86D43 /* SRRunLoopThread.h */; }; + 81B31C641CDC444900D86D43 /* SRRunLoopThread.m in Sources */ = {isa = PBXBuildFile; fileRef = 81B31C5E1CDC444900D86D43 /* SRRunLoopThread.m */; }; + 81B31C651CDC444900D86D43 /* SRRunLoopThread.m in Sources */ = {isa = PBXBuildFile; fileRef = 81B31C5E1CDC444900D86D43 /* SRRunLoopThread.m */; }; + 81B31C661CDC444900D86D43 /* SRRunLoopThread.m in Sources */ = {isa = PBXBuildFile; fileRef = 81B31C5E1CDC444900D86D43 /* SRRunLoopThread.m */; }; + 81C22BC31D124168007BFDDF /* SRHTTPConnectMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 81C22BC01D124168007BFDDF /* SRHTTPConnectMessage.h */; }; + 81C22BC41D124168007BFDDF /* SRHTTPConnectMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 81C22BC01D124168007BFDDF /* SRHTTPConnectMessage.h */; }; + 81C22BC51D124168007BFDDF /* SRHTTPConnectMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 81C22BC01D124168007BFDDF /* SRHTTPConnectMessage.h */; }; + 81C22BC71D124168007BFDDF /* SRHTTPConnectMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 81C22BC11D124168007BFDDF /* SRHTTPConnectMessage.m */; }; + 81C22BC81D124168007BFDDF /* SRHTTPConnectMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 81C22BC11D124168007BFDDF /* SRHTTPConnectMessage.m */; }; + 81C22BC91D124168007BFDDF /* SRHTTPConnectMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 81C22BC11D124168007BFDDF /* SRHTTPConnectMessage.m */; }; + 81C22BF91D1256E1007BFDDF /* SRRandom.h in Headers */ = {isa = PBXBuildFile; fileRef = 81C22BF61D1256E1007BFDDF /* SRRandom.h */; }; + 81C22BFA1D1256E1007BFDDF /* SRRandom.h in Headers */ = {isa = PBXBuildFile; fileRef = 81C22BF61D1256E1007BFDDF /* SRRandom.h */; }; + 81C22BFB1D1256E1007BFDDF /* SRRandom.h in Headers */ = {isa = PBXBuildFile; fileRef = 81C22BF61D1256E1007BFDDF /* SRRandom.h */; }; + 81C22BFD1D1256E1007BFDDF /* SRRandom.m in Sources */ = {isa = PBXBuildFile; fileRef = 81C22BF71D1256E1007BFDDF /* SRRandom.m */; }; + 81C22BFE1D1256E1007BFDDF /* SRRandom.m in Sources */ = {isa = PBXBuildFile; fileRef = 81C22BF71D1256E1007BFDDF /* SRRandom.m */; }; + 81C22BFF1D1256E1007BFDDF /* SRRandom.m in Sources */ = {isa = PBXBuildFile; fileRef = 81C22BF71D1256E1007BFDDF /* SRRandom.m */; }; + 81C68CD41D2CBE0A00A1D005 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81C68CD31D2CBE0A00A1D005 /* CFNetwork.framework */; }; + 81C68CDF1D2CBE1900A1D005 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81C68CDE1D2CBE1900A1D005 /* CFNetwork.framework */; }; + 81C68CEE1D2CBE9400A1D005 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD51451231B00C1D980 /* CFNetwork.framework */; }; + 81C68CF11D2CBE9F00A1D005 /* libicucore.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 81C68CF01D2CBE9F00A1D005 /* libicucore.tbd */; }; + 81C68CF61D2CBED100A1D005 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD3145122FC00C1D980 /* Security.framework */; }; + 81C68D071D2CBF6A00A1D005 /* libicucore.A.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 81C68D061D2CBF6A00A1D005 /* libicucore.A.tbd */; }; + 81C68D0E1D2CBFA800A1D005 /* libicucore.A.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 81C68D0D1D2CBFA800A1D005 /* libicucore.A.tbd */; }; + 81CD05D81CEEC47300497F47 /* NSURLRequest+SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 81CD05D51CEEC47300497F47 /* NSURLRequest+SRWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 81CD05D91CEEC47300497F47 /* NSURLRequest+SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 81CD05D51CEEC47300497F47 /* NSURLRequest+SRWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 81CD05DA1CEEC47300497F47 /* NSURLRequest+SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 81CD05D51CEEC47300497F47 /* NSURLRequest+SRWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 81CD05DC1CEEC47300497F47 /* NSURLRequest+SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 81CD05D61CEEC47300497F47 /* NSURLRequest+SRWebSocket.m */; }; + 81CD05DD1CEEC47300497F47 /* NSURLRequest+SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 81CD05D61CEEC47300497F47 /* NSURLRequest+SRWebSocket.m */; }; + 81CD05DE1CEEC47300497F47 /* NSURLRequest+SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 81CD05D61CEEC47300497F47 /* NSURLRequest+SRWebSocket.m */; }; + 81CD05FE1CEEC65D00497F47 /* NSRunLoop+SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 81CD05FB1CEEC65D00497F47 /* NSRunLoop+SRWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 81CD05FF1CEEC65D00497F47 /* NSRunLoop+SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 81CD05FB1CEEC65D00497F47 /* NSRunLoop+SRWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 81CD06001CEEC65D00497F47 /* NSRunLoop+SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 81CD05FB1CEEC65D00497F47 /* NSRunLoop+SRWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 81CD06021CEEC65D00497F47 /* NSRunLoop+SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 81CD05FC1CEEC65D00497F47 /* NSRunLoop+SRWebSocket.m */; }; + 81CD06031CEEC65D00497F47 /* NSRunLoop+SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 81CD05FC1CEEC65D00497F47 /* NSRunLoop+SRWebSocket.m */; }; + 81CD06041CEEC65D00497F47 /* NSRunLoop+SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 81CD05FC1CEEC65D00497F47 /* NSRunLoop+SRWebSocket.m */; }; + 81D647611D2CA78800690609 /* NSURLRequest+SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 81CD05D61CEEC47300497F47 /* NSURLRequest+SRWebSocket.m */; }; + 81D647621D2CA78800690609 /* SRError.m in Sources */ = {isa = PBXBuildFile; fileRef = 81B22EC41CE42D7E0073C636 /* SRError.m */; }; + 81D647631D2CA78800690609 /* SRIOConsumer.m in Sources */ = {isa = PBXBuildFile; fileRef = 81B31C101CDC404100D86D43 /* SRIOConsumer.m */; }; + 81D647641D2CA78800690609 /* SRHTTPConnectMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 81C22BC11D124168007BFDDF /* SRHTTPConnectMessage.m */; }; + 81D647651D2CA78800690609 /* SRPinningSecurityPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = 454FEA7A1D2570D400073768 /* SRPinningSecurityPolicy.m */; }; + 81D647661D2CA78800690609 /* SRSecurityPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = 454FEA831D25717C00073768 /* SRSecurityPolicy.m */; }; + 81D647671D2CA78800690609 /* NSRunLoop+SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 81CD05FC1CEEC65D00497F47 /* NSRunLoop+SRWebSocket.m */; }; + 81D647681D2CA78800690609 /* SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = F6A12CD0145119B700C1D980 /* SRWebSocket.m */; }; + 81D647691D2CA78800690609 /* SRRandom.m in Sources */ = {isa = PBXBuildFile; fileRef = 81C22BF71D1256E1007BFDDF /* SRRandom.m */; }; + 81D6476A1D2CA78800690609 /* SRIOConsumerPool.m in Sources */ = {isa = PBXBuildFile; fileRef = 81B31C121CDC404100D86D43 /* SRIOConsumerPool.m */; }; + 81D6476B1D2CA78800690609 /* SRURLUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 81B22EE31CE43ECC0073C636 /* SRURLUtilities.m */; }; + 81D6476C1D2CA78800690609 /* SRProxyConnect.m in Sources */ = {isa = PBXBuildFile; fileRef = 4861E7741D022211002FAB1D /* SRProxyConnect.m */; }; + 81D6476D1D2CA78800690609 /* SRMutex.m in Sources */ = {isa = PBXBuildFile; fileRef = 817491A71D1C8C33006E09DF /* SRMutex.m */; }; + 81D6476E1D2CA78800690609 /* SRRunLoopThread.m in Sources */ = {isa = PBXBuildFile; fileRef = 81B31C5E1CDC444900D86D43 /* SRRunLoopThread.m */; }; + 81D6476F1D2CA78800690609 /* SRLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 81900A4B1D18C9CC0015A290 /* SRLog.m */; }; + 81D647701D2CA78800690609 /* SRHash.m in Sources */ = {isa = PBXBuildFile; fileRef = 81B31C2C1CDC406B00D86D43 /* SRHash.m */; }; + 81D647711D2CA78800690609 /* SRDelegateController.m in Sources */ = {isa = PBXBuildFile; fileRef = 817995851CE139700084DA37 /* SRDelegateController.m */; }; + 81D647741D2CA78800690609 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD3145122FC00C1D980 /* Security.framework */; }; + 81D647751D2CA78800690609 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B208301450F597009315AF /* Foundation.framework */; }; + 81D647771D2CA78800690609 /* SRURLUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 81B22EE21CE43ECC0073C636 /* SRURLUtilities.h */; }; + 81D647781D2CA78800690609 /* SRPinningSecurityPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = 454FEA791D2570D400073768 /* SRPinningSecurityPolicy.h */; }; + 81D647791D2CA78800690609 /* SRIOConsumer.h in Headers */ = {isa = PBXBuildFile; fileRef = 81B31C0F1CDC404100D86D43 /* SRIOConsumer.h */; }; + 81D6477A1D2CA78800690609 /* NSRunLoop+SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 81CD05FB1CEEC65D00497F47 /* NSRunLoop+SRWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 81D6477B1D2CA78800690609 /* SRSecurityPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = 454A02D41D0FAD010060DFB2 /* SRSecurityPolicy.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 81D6477C1D2CA78800690609 /* NSURLRequest+SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 81CD05D51CEEC47300497F47 /* NSURLRequest+SRWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 81D6477D1D2CA78800690609 /* SRLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 81900A4A1D18C9CC0015A290 /* SRLog.h */; }; + 81D6477E1D2CA78800690609 /* SRIOConsumerPool.h in Headers */ = {isa = PBXBuildFile; fileRef = 81B31C111CDC404100D86D43 /* SRIOConsumerPool.h */; }; + 81D6477F1D2CA78800690609 /* SRProxyConnect.h in Headers */ = {isa = PBXBuildFile; fileRef = 4861E7731D022211002FAB1D /* SRProxyConnect.h */; }; + 81D647801D2CA78800690609 /* SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = F6A12CCF145119B700C1D980 /* SRWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 81D647811D2CA78800690609 /* SRHash.h in Headers */ = {isa = PBXBuildFile; fileRef = 81B31C2B1CDC406B00D86D43 /* SRHash.h */; }; + 81D647821D2CA78800690609 /* SocketRocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 555E0EB11C51E56D00E6BB92 /* SocketRocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 81D647831D2CA78800690609 /* SRRandom.h in Headers */ = {isa = PBXBuildFile; fileRef = 81C22BF61D1256E1007BFDDF /* SRRandom.h */; }; + 81D647841D2CA78800690609 /* SRHTTPConnectMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 81C22BC01D124168007BFDDF /* SRHTTPConnectMessage.h */; }; + 81D647851D2CA78800690609 /* SRDelegateController.h in Headers */ = {isa = PBXBuildFile; fileRef = 817995841CE139700084DA37 /* SRDelegateController.h */; }; + 81D647861D2CA78800690609 /* SRMutex.h in Headers */ = {isa = PBXBuildFile; fileRef = 817491A61D1C8C33006E09DF /* SRMutex.h */; }; + 81D647871D2CA78800690609 /* SRError.h in Headers */ = {isa = PBXBuildFile; fileRef = 81B22EC31CE42D7E0073C636 /* SRError.h */; }; + 81D647881D2CA78800690609 /* SRRunLoopThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 81B31C5D1CDC444900D86D43 /* SRRunLoopThread.h */; }; + 81D647B51D2CA8E200690609 /* SocketRocket.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81D6478D1D2CA78800690609 /* SocketRocket.framework */; }; + 81DCD1241D2D9235002501A2 /* libicucore.A.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 81C68D0D1D2CBFA800A1D005 /* libicucore.A.tbd */; }; + 81E8A6A21D4C41E000916C7E /* SocketRocket.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81D6478D1D2CA78800690609 /* SocketRocket.framework */; }; + 81E8A6A31D4C41E000916C7E /* SocketRocket.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 81D6478D1D2CA78800690609 /* SocketRocket.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + F5391CBE1D2F4B4700606A81 /* SRSIMDHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = F5391CBC1D2F4B4700606A81 /* SRSIMDHelpers.h */; }; + F5391CBF1D2F4B4700606A81 /* SRSIMDHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = F5391CBC1D2F4B4700606A81 /* SRSIMDHelpers.h */; }; + F5391CC01D2F4B4700606A81 /* SRSIMDHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = F5391CBC1D2F4B4700606A81 /* SRSIMDHelpers.h */; }; + F5391CC11D2F4B4700606A81 /* SRSIMDHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = F5391CBC1D2F4B4700606A81 /* SRSIMDHelpers.h */; }; + F5391CC21D2F4B4700606A81 /* SRSIMDHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = F5391CBD1D2F4B4700606A81 /* SRSIMDHelpers.m */; }; + F5391CC31D2F4B4700606A81 /* SRSIMDHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = F5391CBD1D2F4B4700606A81 /* SRSIMDHelpers.m */; }; + F5391CC41D2F4B4700606A81 /* SRSIMDHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = F5391CBD1D2F4B4700606A81 /* SRSIMDHelpers.m */; }; + F5391CC51D2F4B4700606A81 /* SRSIMDHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = F5391CBD1D2F4B4700606A81 /* SRSIMDHelpers.m */; }; + F6016C8814620EC70037BB3D /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD3145122FC00C1D980 /* Security.framework */; }; + F61A0DC81625F44D00365EBD /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F61A0DC71625F44D00365EBD /* Default-568h@2x.png */; }; + F62417E614D52F3C003CE997 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F62417E514D52F3C003CE997 /* UIKit.framework */; }; + F62417E714D52F3C003CE997 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B208301450F597009315AF /* Foundation.framework */; }; + F62417EF14D52F3C003CE997 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F62417ED14D52F3C003CE997 /* InfoPlist.strings */; }; + F62417F114D52F3C003CE997 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F62417F014D52F3C003CE997 /* main.m */; }; + F62417F514D52F3C003CE997 /* TCAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F62417F414D52F3C003CE997 /* TCAppDelegate.m */; }; + F62417F814D52F3C003CE997 /* MainStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F62417F614D52F3C003CE997 /* MainStoryboard.storyboard */; }; + F62417FB14D52F3C003CE997 /* TCViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F62417FA14D52F3C003CE997 /* TCViewController.m */; }; + F624180114D5300C003CE997 /* TCChatCell.m in Sources */ = {isa = PBXBuildFile; fileRef = F624180014D5300C003CE997 /* TCChatCell.m */; }; + F624180314D53449003CE997 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD51451231B00C1D980 /* CFNetwork.framework */; }; + F624180414D53449003CE997 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD3145122FC00C1D980 /* Security.framework */; }; + F6396B86153E67EC00345B5E /* SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = F6A12CD0145119B700C1D980 /* SRWebSocket.m */; }; + F668C8AA153E92F90044DBAC /* SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = F6A12CCF145119B700C1D980 /* SRWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F6AE45241459071C0022AF3C /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD51451231B00C1D980 /* CFNetwork.framework */; }; + F6BDA806145900D200FE3253 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B208301450F597009315AF /* Foundation.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 81D647B31D2CA8DD00690609 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F6B208241450F597009315AF /* Project object */; + proxyType = 1; + remoteGlobalIDString = 81D6475F1D2CA78800690609; + remoteInfo = "SocketRocket-iOS"; + }; + 81E8A6A01D4C41DA00916C7E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F6B208241450F597009315AF /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2D4227611BB4358C000C1A6C; + remoteInfo = "SocketRocket-iOS-Dynamic"; + }; + 81E8A6A41D4C41E000916C7E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F6B208241450F597009315AF /* Project object */; + proxyType = 1; + remoteGlobalIDString = 81D6475F1D2CA78800690609; + remoteInfo = "SocketRocket-iOS"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 81E8A6A61D4C41E000916C7E /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 81E8A6A31D4C41E000916C7E /* SocketRocket.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 2D4227621BB4358C000C1A6C /* SocketRocket.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SocketRocket.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 3345DC901C52ACD70083CCB8 /* SocketRocket.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SocketRocket.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 454A02D41D0FAD010060DFB2 /* SRSecurityPolicy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SRSecurityPolicy.h; path = SocketRocket/SRSecurityPolicy.h; sourceTree = SOURCE_ROOT; }; + 454FEA791D2570D400073768 /* SRPinningSecurityPolicy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRPinningSecurityPolicy.h; sourceTree = ""; }; + 454FEA7A1D2570D400073768 /* SRPinningSecurityPolicy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRPinningSecurityPolicy.m; sourceTree = ""; }; + 454FEA831D25717C00073768 /* SRSecurityPolicy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRSecurityPolicy.m; sourceTree = ""; }; + 4861E7731D022211002FAB1D /* SRProxyConnect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRProxyConnect.h; sourceTree = ""; }; + 4861E7741D022211002FAB1D /* SRProxyConnect.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRProxyConnect.m; sourceTree = ""; }; + 555E0EB11C51E56D00E6BB92 /* SocketRocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SocketRocket.h; sourceTree = ""; }; + 8105E4761CDD679A00AA12DB /* SRTWebSocketOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SRTWebSocketOperation.h; sourceTree = ""; }; + 8105E4771CDD679A00AA12DB /* SRTWebSocketOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SRTWebSocketOperation.m; sourceTree = ""; }; + 8105E4791CDD679A00AA12DB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 8105E47A1CDD679A00AA12DB /* SRAutobahnTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SRAutobahnTests.m; sourceTree = ""; }; + 8105E4AC1CDD6E6200AA12DB /* SRAutobahnOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRAutobahnOperation.h; sourceTree = ""; }; + 8105E4AD1CDD6E6200AA12DB /* SRAutobahnOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRAutobahnOperation.m; sourceTree = ""; }; + 8105E5271CDD98E100AA12DB /* autobahn_configuration.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = autobahn_configuration.json; sourceTree = ""; }; + 8117C4221D3076DF00784D79 /* NSURLRequest+SRWebSocketPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSURLRequest+SRWebSocketPrivate.h"; path = "Internal/NSURLRequest+SRWebSocketPrivate.h"; sourceTree = ""; }; + 8117C42F1D30779900784D79 /* NSRunLoop+SRWebSocketPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSRunLoop+SRWebSocketPrivate.h"; path = "Internal/NSRunLoop+SRWebSocketPrivate.h"; sourceTree = ""; }; + 811934B11CDAF711003AB243 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 815FE7241D497D720085FDA5 /* SRConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SRConstants.h; path = SocketRocket/Internal/SRConstants.h; sourceTree = SOURCE_ROOT; }; + 815FE7251D497D720085FDA5 /* SRConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SRConstants.m; path = SocketRocket/Internal/SRConstants.m; sourceTree = SOURCE_ROOT; }; + 817491A61D1C8C33006E09DF /* SRMutex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRMutex.h; sourceTree = ""; }; + 817491A71D1C8C33006E09DF /* SRMutex.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRMutex.m; sourceTree = ""; }; + 817995841CE139700084DA37 /* SRDelegateController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRDelegateController.h; sourceTree = ""; }; + 817995851CE139700084DA37 /* SRDelegateController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRDelegateController.m; sourceTree = ""; }; + 8179967E1CE184F40084DA37 /* SRAutobahnUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRAutobahnUtilities.h; sourceTree = ""; }; + 8179967F1CE184F40084DA37 /* SRAutobahnUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRAutobahnUtilities.m; sourceTree = ""; }; + 81900A4A1D18C9CC0015A290 /* SRLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRLog.h; sourceTree = ""; }; + 81900A4B1D18C9CC0015A290 /* SRLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRLog.m; sourceTree = ""; }; + 81AFCD651D4C431C00B3AFC9 /* libicucore.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libicucore.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.0.sdk/usr/lib/libicucore.tbd; sourceTree = DEVELOPER_DIR; }; + 81B22EC31CE42D7E0073C636 /* SRError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRError.h; sourceTree = ""; }; + 81B22EC41CE42D7E0073C636 /* SRError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRError.m; sourceTree = ""; }; + 81B22EE21CE43ECC0073C636 /* SRURLUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRURLUtilities.h; sourceTree = ""; }; + 81B22EE31CE43ECC0073C636 /* SRURLUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRURLUtilities.m; sourceTree = ""; }; + 81B31C0F1CDC404100D86D43 /* SRIOConsumer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRIOConsumer.h; sourceTree = ""; }; + 81B31C101CDC404100D86D43 /* SRIOConsumer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRIOConsumer.m; sourceTree = ""; }; + 81B31C111CDC404100D86D43 /* SRIOConsumerPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRIOConsumerPool.h; sourceTree = ""; }; + 81B31C121CDC404100D86D43 /* SRIOConsumerPool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRIOConsumerPool.m; sourceTree = ""; }; + 81B31C2B1CDC406B00D86D43 /* SRHash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRHash.h; sourceTree = ""; }; + 81B31C2C1CDC406B00D86D43 /* SRHash.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRHash.m; sourceTree = ""; }; + 81B31C5D1CDC444900D86D43 /* SRRunLoopThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRRunLoopThread.h; sourceTree = ""; }; + 81B31C5E1CDC444900D86D43 /* SRRunLoopThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRRunLoopThread.m; sourceTree = ""; }; + 81C22BC01D124168007BFDDF /* SRHTTPConnectMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRHTTPConnectMessage.h; sourceTree = ""; }; + 81C22BC11D124168007BFDDF /* SRHTTPConnectMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRHTTPConnectMessage.m; sourceTree = ""; }; + 81C22BF61D1256E1007BFDDF /* SRRandom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRRandom.h; sourceTree = ""; }; + 81C22BF71D1256E1007BFDDF /* SRRandom.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRRandom.m; sourceTree = ""; }; + 81C68CD31D2CBE0A00A1D005 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; }; + 81C68CDE1D2CBE1900A1D005 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; }; + 81C68CF01D2CBE9F00A1D005 /* libicucore.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libicucore.tbd; path = usr/lib/libicucore.tbd; sourceTree = SDKROOT; }; + 81C68D061D2CBF6A00A1D005 /* libicucore.A.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libicucore.A.tbd; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk/usr/lib/libicucore.A.tbd; sourceTree = DEVELOPER_DIR; }; + 81C68D0D1D2CBFA800A1D005 /* libicucore.A.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libicucore.A.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/lib/libicucore.A.tbd; sourceTree = DEVELOPER_DIR; }; + 81CD05D51CEEC47300497F47 /* NSURLRequest+SRWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURLRequest+SRWebSocket.h"; sourceTree = ""; }; + 81CD05D61CEEC47300497F47 /* NSURLRequest+SRWebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSURLRequest+SRWebSocket.m"; sourceTree = ""; }; + 81CD05FB1CEEC65D00497F47 /* NSRunLoop+SRWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSRunLoop+SRWebSocket.h"; sourceTree = ""; }; + 81CD05FC1CEEC65D00497F47 /* NSRunLoop+SRWebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSRunLoop+SRWebSocket.m"; sourceTree = ""; }; + 81D6474A1D2CA6A100690609 /* Common.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Common.xcconfig; sourceTree = ""; }; + 81D6474C1D2CA6A100690609 /* iOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = iOS.xcconfig; sourceTree = ""; }; + 81D6474D1D2CA6A100690609 /* macOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = macOS.xcconfig; sourceTree = ""; }; + 81D6474E1D2CA6A100690609 /* tvOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = tvOS.xcconfig; sourceTree = ""; }; + 81D6474F1D2CA6A100690609 /* watchOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = watchOS.xcconfig; sourceTree = ""; }; + 81D647511D2CA6A100690609 /* Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Application.xcconfig; sourceTree = ""; }; + 81D647521D2CA6A100690609 /* DynamicFramework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DynamicFramework.xcconfig; sourceTree = ""; }; + 81D647531D2CA6A100690609 /* LogicTests.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = LogicTests.xcconfig; sourceTree = ""; }; + 81D647541D2CA6A100690609 /* StaticFramework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = StaticFramework.xcconfig; sourceTree = ""; }; + 81D647561D2CA6A100690609 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + 81D647571D2CA6A100690609 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 81D647581D2CA6A100690609 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 81D647591D2CA6A100690609 /* SocketRocket-iOS-Dynamic.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "SocketRocket-iOS-Dynamic.xcconfig"; sourceTree = ""; }; + 81D6475A1D2CA6A100690609 /* SocketRocket-iOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "SocketRocket-iOS.xcconfig"; sourceTree = ""; }; + 81D6475B1D2CA6A100690609 /* SocketRocket-macOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "SocketRocket-macOS.xcconfig"; sourceTree = ""; }; + 81D6475C1D2CA6A100690609 /* SocketRocket-tvOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "SocketRocket-tvOS.xcconfig"; sourceTree = ""; }; + 81D6475D1D2CA6A100690609 /* SocketRocketTests-iOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "SocketRocketTests-iOS.xcconfig"; sourceTree = ""; }; + 81D6478D1D2CA78800690609 /* SocketRocket.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SocketRocket.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 81E8A69A1D4C417A00916C7E /* TestChat-iOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "TestChat-iOS.xcconfig"; sourceTree = ""; }; + F5391CBC1D2F4B4700606A81 /* SRSIMDHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRSIMDHelpers.h; sourceTree = ""; }; + F5391CBD1D2F4B4700606A81 /* SRSIMDHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRSIMDHelpers.m; sourceTree = ""; }; + F61A0DC71625F44D00365EBD /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "en.lproj/Default-568h@2x.png"; sourceTree = ""; }; + F62417E314D52F3C003CE997 /* TestChat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TestChat.app; sourceTree = BUILT_PRODUCTS_DIR; }; + F62417E514D52F3C003CE997 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; + F62417EC14D52F3C003CE997 /* TestChat-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TestChat-Info.plist"; sourceTree = ""; }; + F62417EE14D52F3C003CE997 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + F62417F014D52F3C003CE997 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + F62417F314D52F3C003CE997 /* TCAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TCAppDelegate.h; sourceTree = ""; }; + F62417F414D52F3C003CE997 /* TCAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TCAppDelegate.m; sourceTree = ""; }; + F62417F714D52F3C003CE997 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/MainStoryboard.storyboard; sourceTree = ""; }; + F62417F914D52F3C003CE997 /* TCViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TCViewController.h; sourceTree = ""; }; + F62417FA14D52F3C003CE997 /* TCViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TCViewController.m; sourceTree = ""; }; + F62417FF14D5300C003CE997 /* TCChatCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TCChatCell.h; sourceTree = ""; }; + F624180014D5300C003CE997 /* TCChatCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TCChatCell.m; sourceTree = ""; }; + F668C880153E91210044DBAC /* SocketRocket.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SocketRocket.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F6A12CCF145119B700C1D980 /* SRWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRWebSocket.h; sourceTree = ""; }; + F6A12CD0145119B700C1D980 /* SRWebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRWebSocket.m; sourceTree = ""; }; + F6A12CD3145122FC00C1D980 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; + F6A12CD51451231B00C1D980 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; }; + F6B208301450F597009315AF /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + F6BDA802145900D200FE3253 /* SocketRocketTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SocketRocketTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 2D42275E1BB4358C000C1A6C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 81C68D0E1D2CBFA800A1D005 /* libicucore.A.tbd in Frameworks */, + 81C68CD41D2CBE0A00A1D005 /* CFNetwork.framework in Frameworks */, + 2D4227831BB436B1000C1A6C /* Security.framework in Frameworks */, + 2D4227801BB43693000C1A6C /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3345DC851C52ACD70083CCB8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 81C68D071D2CBF6A00A1D005 /* libicucore.A.tbd in Frameworks */, + 81C68CDF1D2CBE1900A1D005 /* CFNetwork.framework in Frameworks */, + 3345DC871C52ACD70083CCB8 /* Security.framework in Frameworks */, + 3345DC881C52ACD70083CCB8 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 81D647721D2CA78800690609 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 81D647741D2CA78800690609 /* Security.framework in Frameworks */, + 81D647751D2CA78800690609 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F62417E014D52F3C003CE997 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 81AFCD661D4C431C00B3AFC9 /* libicucore.tbd in Frameworks */, + F62417E614D52F3C003CE997 /* UIKit.framework in Frameworks */, + F62417E714D52F3C003CE997 /* Foundation.framework in Frameworks */, + F624180314D53449003CE997 /* CFNetwork.framework in Frameworks */, + F624180414D53449003CE997 /* Security.framework in Frameworks */, + 81E8A6A21D4C41E000916C7E /* SocketRocket.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F668C87C153E91210044DBAC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 81C68CEE1D2CBE9400A1D005 /* CFNetwork.framework in Frameworks */, + 81C68CF61D2CBED100A1D005 /* Security.framework in Frameworks */, + 81C68CF11D2CBE9F00A1D005 /* libicucore.tbd in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F6BDA7FE145900D200FE3253 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 81D647B51D2CA8E200690609 /* SocketRocket.framework in Frameworks */, + F6BDA806145900D200FE3253 /* Foundation.framework in Frameworks */, + F6AE45241459071C0022AF3C /* CFNetwork.framework in Frameworks */, + F6016C8814620EC70037BB3D /* Security.framework in Frameworks */, + 81DCD1241D2D9235002501A2 /* libicucore.A.tbd in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 4861E7721D022211002FAB1D /* Proxy */ = { + isa = PBXGroup; + children = ( + 4861E7731D022211002FAB1D /* SRProxyConnect.h */, + 4861E7741D022211002FAB1D /* SRProxyConnect.m */, + ); + path = Proxy; + sourceTree = ""; + }; + 8105E4741CDD679A00AA12DB /* Tests */ = { + isa = PBXGroup; + children = ( + 8105E47A1CDD679A00AA12DB /* SRAutobahnTests.m */, + 8105E4751CDD679A00AA12DB /* Operations */, + 8105E47C1CDD679A00AA12DB /* Utilities */, + 8105E4781CDD679A00AA12DB /* Resources */, + ); + path = Tests; + sourceTree = ""; + }; + 8105E4751CDD679A00AA12DB /* Operations */ = { + isa = PBXGroup; + children = ( + 8105E4761CDD679A00AA12DB /* SRTWebSocketOperation.h */, + 8105E4771CDD679A00AA12DB /* SRTWebSocketOperation.m */, + 8105E4AC1CDD6E6200AA12DB /* SRAutobahnOperation.h */, + 8105E4AD1CDD6E6200AA12DB /* SRAutobahnOperation.m */, + ); + path = Operations; + sourceTree = ""; + }; + 8105E4781CDD679A00AA12DB /* Resources */ = { + isa = PBXGroup; + children = ( + 8105E5271CDD98E100AA12DB /* autobahn_configuration.json */, + 8105E4791CDD679A00AA12DB /* Info.plist */, + ); + path = Resources; + sourceTree = ""; + }; + 8105E47C1CDD679A00AA12DB /* Utilities */ = { + isa = PBXGroup; + children = ( + 8179967E1CE184F40084DA37 /* SRAutobahnUtilities.h */, + 8179967F1CE184F40084DA37 /* SRAutobahnUtilities.m */, + ); + path = Utilities; + sourceTree = ""; + }; + 811934B01CDAF711003AB243 /* Resources */ = { + isa = PBXGroup; + children = ( + 811934B11CDAF711003AB243 /* Info.plist */, + ); + path = Resources; + sourceTree = ""; + }; + 817995831CE139540084DA37 /* Delegate */ = { + isa = PBXGroup; + children = ( + 817995841CE139700084DA37 /* SRDelegateController.h */, + 817995851CE139700084DA37 /* SRDelegateController.m */, + ); + path = Delegate; + sourceTree = ""; + }; + 8186892C1D08EF3C004F94C8 /* Security */ = { + isa = PBXGroup; + children = ( + 454FEA791D2570D400073768 /* SRPinningSecurityPolicy.h */, + 454FEA7A1D2570D400073768 /* SRPinningSecurityPolicy.m */, + ); + path = Security; + sourceTree = ""; + }; + 81B31C0D1CDC404100D86D43 /* Internal */ = { + isa = PBXGroup; + children = ( + 8186892C1D08EF3C004F94C8 /* Security */, + 4861E7721D022211002FAB1D /* Proxy */, + 817995831CE139540084DA37 /* Delegate */, + 81B31C0E1CDC404100D86D43 /* IOConsumer */, + 81B31C5C1CDC443A00D86D43 /* RunLoop */, + 81B31C131CDC404100D86D43 /* Utilities */, + ); + path = Internal; + sourceTree = ""; + }; + 81B31C0E1CDC404100D86D43 /* IOConsumer */ = { + isa = PBXGroup; + children = ( + 81B31C0F1CDC404100D86D43 /* SRIOConsumer.h */, + 81B31C101CDC404100D86D43 /* SRIOConsumer.m */, + 81B31C111CDC404100D86D43 /* SRIOConsumerPool.h */, + 81B31C121CDC404100D86D43 /* SRIOConsumerPool.m */, + ); + path = IOConsumer; + sourceTree = ""; + }; + 81B31C131CDC404100D86D43 /* Utilities */ = { + isa = PBXGroup; + children = ( + 81B22EC31CE42D7E0073C636 /* SRError.h */, + 81B22EC41CE42D7E0073C636 /* SRError.m */, + 81B31C2B1CDC406B00D86D43 /* SRHash.h */, + 81B31C2C1CDC406B00D86D43 /* SRHash.m */, + 81C22BC01D124168007BFDDF /* SRHTTPConnectMessage.h */, + 81C22BC11D124168007BFDDF /* SRHTTPConnectMessage.m */, + 81900A4A1D18C9CC0015A290 /* SRLog.h */, + 81900A4B1D18C9CC0015A290 /* SRLog.m */, + 817491A61D1C8C33006E09DF /* SRMutex.h */, + 817491A71D1C8C33006E09DF /* SRMutex.m */, + 81C22BF61D1256E1007BFDDF /* SRRandom.h */, + 81C22BF71D1256E1007BFDDF /* SRRandom.m */, + 815FE7241D497D720085FDA5 /* SRConstants.h */, + 815FE7251D497D720085FDA5 /* SRConstants.m */, + 81B22EE21CE43ECC0073C636 /* SRURLUtilities.h */, + 81B22EE31CE43ECC0073C636 /* SRURLUtilities.m */, + F5391CBC1D2F4B4700606A81 /* SRSIMDHelpers.h */, + F5391CBD1D2F4B4700606A81 /* SRSIMDHelpers.m */, + ); + path = Utilities; + sourceTree = ""; + }; + 81B31C5C1CDC443A00D86D43 /* RunLoop */ = { + isa = PBXGroup; + children = ( + 81B31C5D1CDC444900D86D43 /* SRRunLoopThread.h */, + 81B31C5E1CDC444900D86D43 /* SRRunLoopThread.m */, + ); + name = RunLoop; + path = SocketRocket/Internal/RunLoop; + sourceTree = SOURCE_ROOT; + }; + 81C68CF41D2CBEBD00A1D005 /* macOS */ = { + isa = PBXGroup; + children = ( + F6B208301450F597009315AF /* Foundation.framework */, + F6A12CD3145122FC00C1D980 /* Security.framework */, + F6A12CD51451231B00C1D980 /* CFNetwork.framework */, + 81C68CF01D2CBE9F00A1D005 /* libicucore.tbd */, + ); + name = macOS; + sourceTree = ""; + }; + 81C68CFD1D2CBF1800A1D005 /* tvOS */ = { + isa = PBXGroup; + children = ( + 81C68CDE1D2CBE1900A1D005 /* CFNetwork.framework */, + 81C68D061D2CBF6A00A1D005 /* libicucore.A.tbd */, + ); + name = tvOS; + sourceTree = ""; + }; + 81C68CFE1D2CBF2100A1D005 /* iOS */ = { + isa = PBXGroup; + children = ( + F62417E514D52F3C003CE997 /* UIKit.framework */, + 81C68CD31D2CBE0A00A1D005 /* CFNetwork.framework */, + 81C68D0D1D2CBFA800A1D005 /* libicucore.A.tbd */, + ); + name = iOS; + sourceTree = ""; + }; + 81D647481D2CA6A100690609 /* Configurations */ = { + isa = PBXGroup; + children = ( + 81D6475A1D2CA6A100690609 /* SocketRocket-iOS.xcconfig */, + 81D647591D2CA6A100690609 /* SocketRocket-iOS-Dynamic.xcconfig */, + 81D6475B1D2CA6A100690609 /* SocketRocket-macOS.xcconfig */, + 81D6475C1D2CA6A100690609 /* SocketRocket-tvOS.xcconfig */, + 81D6475D1D2CA6A100690609 /* SocketRocketTests-iOS.xcconfig */, + 81E8A69A1D4C417A00916C7E /* TestChat-iOS.xcconfig */, + 81D647491D2CA6A100690609 /* Shared */, + ); + path = Configurations; + sourceTree = ""; + }; + 81D647491D2CA6A100690609 /* Shared */ = { + isa = PBXGroup; + children = ( + 81D6474A1D2CA6A100690609 /* Common.xcconfig */, + 81D6474B1D2CA6A100690609 /* Platform */, + 81D647501D2CA6A100690609 /* Product */, + 81D647551D2CA6A100690609 /* Project */, + 81D647581D2CA6A100690609 /* Warnings.xcconfig */, + ); + path = Shared; + sourceTree = ""; + }; + 81D6474B1D2CA6A100690609 /* Platform */ = { + isa = PBXGroup; + children = ( + 81D6474C1D2CA6A100690609 /* iOS.xcconfig */, + 81D6474D1D2CA6A100690609 /* macOS.xcconfig */, + 81D6474E1D2CA6A100690609 /* tvOS.xcconfig */, + 81D6474F1D2CA6A100690609 /* watchOS.xcconfig */, + ); + path = Platform; + sourceTree = ""; + }; + 81D647501D2CA6A100690609 /* Product */ = { + isa = PBXGroup; + children = ( + 81D647511D2CA6A100690609 /* Application.xcconfig */, + 81D647521D2CA6A100690609 /* DynamicFramework.xcconfig */, + 81D647531D2CA6A100690609 /* LogicTests.xcconfig */, + 81D647541D2CA6A100690609 /* StaticFramework.xcconfig */, + ); + path = Product; + sourceTree = ""; + }; + 81D647551D2CA6A100690609 /* Project */ = { + isa = PBXGroup; + children = ( + 81D647561D2CA6A100690609 /* Debug.xcconfig */, + 81D647571D2CA6A100690609 /* Release.xcconfig */, + ); + path = Project; + sourceTree = ""; + }; + F62417EA14D52F3C003CE997 /* TestChat */ = { + isa = PBXGroup; + children = ( + F62417EB14D52F3C003CE997 /* Supporting Files */, + F62417F314D52F3C003CE997 /* TCAppDelegate.h */, + F62417F414D52F3C003CE997 /* TCAppDelegate.m */, + F62417F614D52F3C003CE997 /* MainStoryboard.storyboard */, + F62417F914D52F3C003CE997 /* TCViewController.h */, + F62417FA14D52F3C003CE997 /* TCViewController.m */, + F62417FF14D5300C003CE997 /* TCChatCell.h */, + F624180014D5300C003CE997 /* TCChatCell.m */, + ); + path = TestChat; + sourceTree = ""; + }; + F62417EB14D52F3C003CE997 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + F61A0DC71625F44D00365EBD /* Default-568h@2x.png */, + F62417EC14D52F3C003CE997 /* TestChat-Info.plist */, + F62417ED14D52F3C003CE997 /* InfoPlist.strings */, + F62417F014D52F3C003CE997 /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + F6B208221450F597009315AF = { + isa = PBXGroup; + children = ( + 81D647481D2CA6A100690609 /* Configurations */, + F6B208321450F597009315AF /* SocketRocket */, + 8105E4741CDD679A00AA12DB /* Tests */, + F62417EA14D52F3C003CE997 /* TestChat */, + F6B2082F1450F597009315AF /* Frameworks */, + F6B2082E1450F597009315AF /* Products */, + ); + indentWidth = 4; + sourceTree = ""; + tabWidth = 4; + }; + F6B2082E1450F597009315AF /* Products */ = { + isa = PBXGroup; + children = ( + F6BDA802145900D200FE3253 /* SocketRocketTests-iOS.xctest */, + F62417E314D52F3C003CE997 /* TestChat.app */, + F668C880153E91210044DBAC /* SocketRocket.framework */, + 2D4227621BB4358C000C1A6C /* SocketRocket.framework */, + 3345DC901C52ACD70083CCB8 /* SocketRocket.framework */, + 81D6478D1D2CA78800690609 /* SocketRocket.framework */, + ); + name = Products; + sourceTree = ""; + }; + F6B2082F1450F597009315AF /* Frameworks */ = { + isa = PBXGroup; + children = ( + 81AFCD651D4C431C00B3AFC9 /* libicucore.tbd */, + 81C68CF41D2CBEBD00A1D005 /* macOS */, + 81C68CFD1D2CBF1800A1D005 /* tvOS */, + 81C68CFE1D2CBF2100A1D005 /* iOS */, + ); + name = Frameworks; + sourceTree = ""; + }; + F6B208321450F597009315AF /* SocketRocket */ = { + isa = PBXGroup; + children = ( + 81B31C0D1CDC404100D86D43 /* Internal */, + 555E0EB11C51E56D00E6BB92 /* SocketRocket.h */, + 454A02D41D0FAD010060DFB2 /* SRSecurityPolicy.h */, + 454FEA831D25717C00073768 /* SRSecurityPolicy.m */, + F6A12CCF145119B700C1D980 /* SRWebSocket.h */, + F6A12CD0145119B700C1D980 /* SRWebSocket.m */, + 81CD05D51CEEC47300497F47 /* NSURLRequest+SRWebSocket.h */, + 8117C4221D3076DF00784D79 /* NSURLRequest+SRWebSocketPrivate.h */, + 81CD05D61CEEC47300497F47 /* NSURLRequest+SRWebSocket.m */, + 81CD05FB1CEEC65D00497F47 /* NSRunLoop+SRWebSocket.h */, + 8117C42F1D30779900784D79 /* NSRunLoop+SRWebSocketPrivate.h */, + 81CD05FC1CEEC65D00497F47 /* NSRunLoop+SRWebSocket.m */, + 811934B01CDAF711003AB243 /* Resources */, + ); + path = SocketRocket; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 2D42275F1BB4358C000C1A6C /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 81B22EE51CE43ECC0073C636 /* SRURLUtilities.h in Headers */, + 454FEA7F1D2570F800073768 /* SRPinningSecurityPolicy.h in Headers */, + 81B31C151CDC404100D86D43 /* SRIOConsumer.h in Headers */, + 8117C4241D3076DF00784D79 /* NSURLRequest+SRWebSocketPrivate.h in Headers */, + 81CD05FE1CEEC65D00497F47 /* NSRunLoop+SRWebSocket.h in Headers */, + 815FE7271D497D720085FDA5 /* SRConstants.h in Headers */, + 454A02D61D0FAD010060DFB2 /* SRSecurityPolicy.h in Headers */, + 81CD05D81CEEC47300497F47 /* NSURLRequest+SRWebSocket.h in Headers */, + 81900A4D1D18C9CC0015A290 /* SRLog.h in Headers */, + 81B31C1D1CDC404100D86D43 /* SRIOConsumerPool.h in Headers */, + 813364001D091E170062E28D /* SRProxyConnect.h in Headers */, + 2D42277F1BB4365C000C1A6C /* SRWebSocket.h in Headers */, + 81B31C2E1CDC406B00D86D43 /* SRHash.h in Headers */, + 811934BE1CDAF725003AB243 /* SocketRocket.h in Headers */, + 81C22BF91D1256E1007BFDDF /* SRRandom.h in Headers */, + 8117C4311D30779900784D79 /* NSRunLoop+SRWebSocketPrivate.h in Headers */, + 81C22BC31D124168007BFDDF /* SRHTTPConnectMessage.h in Headers */, + 817995871CE139700084DA37 /* SRDelegateController.h in Headers */, + 817491A91D1C8C33006E09DF /* SRMutex.h in Headers */, + 81B22EC61CE42D7E0073C636 /* SRError.h in Headers */, + 81B31C601CDC444900D86D43 /* SRRunLoopThread.h in Headers */, + F5391CBF1D2F4B4700606A81 /* SRSIMDHelpers.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3345DC891C52ACD70083CCB8 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 81B22EE71CE43ECC0073C636 /* SRURLUtilities.h in Headers */, + 454FEA811D2570F900073768 /* SRPinningSecurityPolicy.h in Headers */, + 81B31C171CDC404100D86D43 /* SRIOConsumer.h in Headers */, + 8117C4261D3076DF00784D79 /* NSURLRequest+SRWebSocketPrivate.h in Headers */, + 81CD06001CEEC65D00497F47 /* NSRunLoop+SRWebSocket.h in Headers */, + 815FE7291D497D720085FDA5 /* SRConstants.h in Headers */, + 454A02D81D0FAD010060DFB2 /* SRSecurityPolicy.h in Headers */, + 81CD05DA1CEEC47300497F47 /* NSURLRequest+SRWebSocket.h in Headers */, + 81900A4F1D18C9CC0015A290 /* SRLog.h in Headers */, + 81B31C1F1CDC404100D86D43 /* SRIOConsumerPool.h in Headers */, + 813364081D091E180062E28D /* SRProxyConnect.h in Headers */, + 3345DC8A1C52ACD70083CCB8 /* SRWebSocket.h in Headers */, + 81B31C301CDC406B00D86D43 /* SRHash.h in Headers */, + 811934C01CDAF726003AB243 /* SocketRocket.h in Headers */, + 81C22BFB1D1256E1007BFDDF /* SRRandom.h in Headers */, + 8117C4331D30779900784D79 /* NSRunLoop+SRWebSocketPrivate.h in Headers */, + 81C22BC51D124168007BFDDF /* SRHTTPConnectMessage.h in Headers */, + 817995891CE139700084DA37 /* SRDelegateController.h in Headers */, + 817491AB1D1C8C33006E09DF /* SRMutex.h in Headers */, + 81B22EC81CE42D7E0073C636 /* SRError.h in Headers */, + 81B31C621CDC444900D86D43 /* SRRunLoopThread.h in Headers */, + F5391CC11D2F4B4700606A81 /* SRSIMDHelpers.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 81D647761D2CA78800690609 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 81D647771D2CA78800690609 /* SRURLUtilities.h in Headers */, + 81D647781D2CA78800690609 /* SRPinningSecurityPolicy.h in Headers */, + 81D647791D2CA78800690609 /* SRIOConsumer.h in Headers */, + 8117C4231D3076DF00784D79 /* NSURLRequest+SRWebSocketPrivate.h in Headers */, + 81D6477A1D2CA78800690609 /* NSRunLoop+SRWebSocket.h in Headers */, + 815FE7261D497D720085FDA5 /* SRConstants.h in Headers */, + 81D6477B1D2CA78800690609 /* SRSecurityPolicy.h in Headers */, + 81D6477C1D2CA78800690609 /* NSURLRequest+SRWebSocket.h in Headers */, + 81D6477D1D2CA78800690609 /* SRLog.h in Headers */, + 81D6477E1D2CA78800690609 /* SRIOConsumerPool.h in Headers */, + 81D6477F1D2CA78800690609 /* SRProxyConnect.h in Headers */, + 81D647801D2CA78800690609 /* SRWebSocket.h in Headers */, + 81D647811D2CA78800690609 /* SRHash.h in Headers */, + 81D647821D2CA78800690609 /* SocketRocket.h in Headers */, + 81D647831D2CA78800690609 /* SRRandom.h in Headers */, + 8117C4301D30779900784D79 /* NSRunLoop+SRWebSocketPrivate.h in Headers */, + 81D647841D2CA78800690609 /* SRHTTPConnectMessage.h in Headers */, + 81D647851D2CA78800690609 /* SRDelegateController.h in Headers */, + 81D647861D2CA78800690609 /* SRMutex.h in Headers */, + 81D647871D2CA78800690609 /* SRError.h in Headers */, + 81D647881D2CA78800690609 /* SRRunLoopThread.h in Headers */, + F5391CBE1D2F4B4700606A81 /* SRSIMDHelpers.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F668C87D153E91210044DBAC /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 81B22EE61CE43ECC0073C636 /* SRURLUtilities.h in Headers */, + 454FEA7D1D2570F600073768 /* SRPinningSecurityPolicy.h in Headers */, + 81B31C161CDC404100D86D43 /* SRIOConsumer.h in Headers */, + 8117C4251D3076DF00784D79 /* NSURLRequest+SRWebSocketPrivate.h in Headers */, + 81CD05FF1CEEC65D00497F47 /* NSRunLoop+SRWebSocket.h in Headers */, + 815FE7281D497D720085FDA5 /* SRConstants.h in Headers */, + 454A02D71D0FAD010060DFB2 /* SRSecurityPolicy.h in Headers */, + 81CD05D91CEEC47300497F47 /* NSURLRequest+SRWebSocket.h in Headers */, + 81900A4E1D18C9CC0015A290 /* SRLog.h in Headers */, + 81B31C1E1CDC404100D86D43 /* SRIOConsumerPool.h in Headers */, + 813364041D091E170062E28D /* SRProxyConnect.h in Headers */, + F668C8AA153E92F90044DBAC /* SRWebSocket.h in Headers */, + 81B31C2F1CDC406B00D86D43 /* SRHash.h in Headers */, + 811934BC1CDAF725003AB243 /* SocketRocket.h in Headers */, + 81C22BFA1D1256E1007BFDDF /* SRRandom.h in Headers */, + 8117C4321D30779900784D79 /* NSRunLoop+SRWebSocketPrivate.h in Headers */, + 81C22BC41D124168007BFDDF /* SRHTTPConnectMessage.h in Headers */, + 817995881CE139700084DA37 /* SRDelegateController.h in Headers */, + 817491AA1D1C8C33006E09DF /* SRMutex.h in Headers */, + 81B22EC71CE42D7E0073C636 /* SRError.h in Headers */, + 81B31C611CDC444900D86D43 /* SRRunLoopThread.h in Headers */, + F5391CC01D2F4B4700606A81 /* SRSIMDHelpers.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 2D4227611BB4358C000C1A6C /* SocketRocket-iOS-Dynamic */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2D4227671BB4358C000C1A6C /* Build configuration list for PBXNativeTarget "SocketRocket-iOS-Dynamic" */; + buildPhases = ( + 2D42275D1BB4358C000C1A6C /* Sources */, + 2D42275E1BB4358C000C1A6C /* Frameworks */, + 2D42275F1BB4358C000C1A6C /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "SocketRocket-iOS-Dynamic"; + productName = "SocketRocket-iOS"; + productReference = 2D4227621BB4358C000C1A6C /* SocketRocket.framework */; + productType = "com.apple.product-type.framework"; + }; + 3345DC821C52ACD70083CCB8 /* SocketRocket-tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3345DC8D1C52ACD70083CCB8 /* Build configuration list for PBXNativeTarget "SocketRocket-tvOS" */; + buildPhases = ( + 3345DC831C52ACD70083CCB8 /* Sources */, + 3345DC851C52ACD70083CCB8 /* Frameworks */, + 3345DC891C52ACD70083CCB8 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "SocketRocket-tvOS"; + productName = "SocketRocket-iOS"; + productReference = 3345DC901C52ACD70083CCB8 /* SocketRocket.framework */; + productType = "com.apple.product-type.framework"; + }; + 81D6475F1D2CA78800690609 /* SocketRocket-iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 81D6478A1D2CA78800690609 /* Build configuration list for PBXNativeTarget "SocketRocket-iOS" */; + buildPhases = ( + 81D647601D2CA78800690609 /* Sources */, + 81D647721D2CA78800690609 /* Frameworks */, + 81D647761D2CA78800690609 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "SocketRocket-iOS"; + productName = "SocketRocket-iOS"; + productReference = 81D6478D1D2CA78800690609 /* SocketRocket.framework */; + productType = "com.apple.product-type.framework"; + }; + F62417E214D52F3C003CE997 /* TestChat */ = { + isa = PBXNativeTarget; + buildConfigurationList = F62417FC14D52F3C003CE997 /* Build configuration list for PBXNativeTarget "TestChat" */; + buildPhases = ( + F62417DF14D52F3C003CE997 /* Sources */, + F62417E014D52F3C003CE997 /* Frameworks */, + F62417E114D52F3C003CE997 /* Resources */, + 81E8A6A61D4C41E000916C7E /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 81E8A6A11D4C41DA00916C7E /* PBXTargetDependency */, + 81E8A6A51D4C41E000916C7E /* PBXTargetDependency */, + ); + name = TestChat; + productName = TestChat; + productReference = F62417E314D52F3C003CE997 /* TestChat.app */; + productType = "com.apple.product-type.application"; + }; + F668C87F153E91210044DBAC /* SocketRocket-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = F668C891153E91210044DBAC /* Build configuration list for PBXNativeTarget "SocketRocket-macOS" */; + buildPhases = ( + F6396B85153E67EC00345B5E /* Sources */, + F668C87C153E91210044DBAC /* Frameworks */, + F668C87D153E91210044DBAC /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "SocketRocket-macOS"; + productName = SocketRocketOSX; + productReference = F668C880153E91210044DBAC /* SocketRocket.framework */; + productType = "com.apple.product-type.framework"; + }; + F6BDA801145900D200FE3253 /* SocketRocketTests-iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = F6BDA813145900D200FE3253 /* Build configuration list for PBXNativeTarget "SocketRocketTests-iOS" */; + buildPhases = ( + F6BDA7FD145900D200FE3253 /* Sources */, + F6BDA7FE145900D200FE3253 /* Frameworks */, + F6BDA7FF145900D200FE3253 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 81D647B41D2CA8DD00690609 /* PBXTargetDependency */, + ); + name = "SocketRocketTests-iOS"; + productName = SRWebSocketTests; + productReference = F6BDA802145900D200FE3253 /* SocketRocketTests-iOS.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + F6B208241450F597009315AF /* Project object */ = { + isa = PBXProject; + attributes = { + LastTestingUpgradeCheck = 0640; + LastUpgradeCheck = 0800; + }; + buildConfigurationList = F6B208271450F597009315AF /* Build configuration list for PBXProject "SocketRocket" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = F6B208221450F597009315AF; + productRefGroup = F6B2082E1450F597009315AF /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 81D6475F1D2CA78800690609 /* SocketRocket-iOS */, + 2D4227611BB4358C000C1A6C /* SocketRocket-iOS-Dynamic */, + F668C87F153E91210044DBAC /* SocketRocket-macOS */, + 3345DC821C52ACD70083CCB8 /* SocketRocket-tvOS */, + F6BDA801145900D200FE3253 /* SocketRocketTests-iOS */, + F62417E214D52F3C003CE997 /* TestChat */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + F62417E114D52F3C003CE997 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F62417EF14D52F3C003CE997 /* InfoPlist.strings in Resources */, + F62417F814D52F3C003CE997 /* MainStoryboard.storyboard in Resources */, + F61A0DC81625F44D00365EBD /* Default-568h@2x.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F6BDA7FF145900D200FE3253 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8105E5281CDD98E100AA12DB /* autobahn_configuration.json in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 2D42275D1BB4358C000C1A6C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 81CD05DC1CEEC47300497F47 /* NSURLRequest+SRWebSocket.m in Sources */, + F5391CC31D2F4B4700606A81 /* SRSIMDHelpers.m in Sources */, + 81B22ECA1CE42D7E0073C636 /* SRError.m in Sources */, + 81B31C191CDC404100D86D43 /* SRIOConsumer.m in Sources */, + 81C22BC71D124168007BFDDF /* SRHTTPConnectMessage.m in Sources */, + 454FEA801D2570F800073768 /* SRPinningSecurityPolicy.m in Sources */, + 454FEA851D25719900073768 /* SRSecurityPolicy.m in Sources */, + 815FE72B1D497D720085FDA5 /* SRConstants.m in Sources */, + 81CD06021CEEC65D00497F47 /* NSRunLoop+SRWebSocket.m in Sources */, + 2D4227851BB43734000C1A6C /* SRWebSocket.m in Sources */, + 81C22BFD1D1256E1007BFDDF /* SRRandom.m in Sources */, + 81B31C211CDC404100D86D43 /* SRIOConsumerPool.m in Sources */, + 81B22EE91CE43ECC0073C636 /* SRURLUtilities.m in Sources */, + 8133640C1D091E1B0062E28D /* SRProxyConnect.m in Sources */, + 817491AD1D1C8C33006E09DF /* SRMutex.m in Sources */, + 81B31C641CDC444900D86D43 /* SRRunLoopThread.m in Sources */, + 81900A511D18C9CC0015A290 /* SRLog.m in Sources */, + 81B31C321CDC406B00D86D43 /* SRHash.m in Sources */, + 8179958B1CE139700084DA37 /* SRDelegateController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3345DC831C52ACD70083CCB8 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 81CD05DE1CEEC47300497F47 /* NSURLRequest+SRWebSocket.m in Sources */, + F5391CC51D2F4B4700606A81 /* SRSIMDHelpers.m in Sources */, + 81B22ECC1CE42D7E0073C636 /* SRError.m in Sources */, + 81B31C1B1CDC404100D86D43 /* SRIOConsumer.m in Sources */, + 81C22BC91D124168007BFDDF /* SRHTTPConnectMessage.m in Sources */, + 454FEA821D2570F900073768 /* SRPinningSecurityPolicy.m in Sources */, + 454FEA871D25719A00073768 /* SRSecurityPolicy.m in Sources */, + 815FE72D1D497D720085FDA5 /* SRConstants.m in Sources */, + 81CD06041CEEC65D00497F47 /* NSRunLoop+SRWebSocket.m in Sources */, + 3345DC841C52ACD70083CCB8 /* SRWebSocket.m in Sources */, + 81C22BFF1D1256E1007BFDDF /* SRRandom.m in Sources */, + 81B31C231CDC404100D86D43 /* SRIOConsumerPool.m in Sources */, + 81B22EEB1CE43ECC0073C636 /* SRURLUtilities.m in Sources */, + 8133640F1D091E1C0062E28D /* SRProxyConnect.m in Sources */, + 817491AF1D1C8C33006E09DF /* SRMutex.m in Sources */, + 81B31C661CDC444900D86D43 /* SRRunLoopThread.m in Sources */, + 81900A531D18C9CC0015A290 /* SRLog.m in Sources */, + 81B31C341CDC406B00D86D43 /* SRHash.m in Sources */, + 8179958D1CE139700084DA37 /* SRDelegateController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 81D647601D2CA78800690609 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 81D647611D2CA78800690609 /* NSURLRequest+SRWebSocket.m in Sources */, + F5391CC21D2F4B4700606A81 /* SRSIMDHelpers.m in Sources */, + 81D647621D2CA78800690609 /* SRError.m in Sources */, + 81D647631D2CA78800690609 /* SRIOConsumer.m in Sources */, + 81D647641D2CA78800690609 /* SRHTTPConnectMessage.m in Sources */, + 81D647651D2CA78800690609 /* SRPinningSecurityPolicy.m in Sources */, + 81D647661D2CA78800690609 /* SRSecurityPolicy.m in Sources */, + 815FE72A1D497D720085FDA5 /* SRConstants.m in Sources */, + 81D647671D2CA78800690609 /* NSRunLoop+SRWebSocket.m in Sources */, + 81D647681D2CA78800690609 /* SRWebSocket.m in Sources */, + 81D647691D2CA78800690609 /* SRRandom.m in Sources */, + 81D6476A1D2CA78800690609 /* SRIOConsumerPool.m in Sources */, + 81D6476B1D2CA78800690609 /* SRURLUtilities.m in Sources */, + 81D6476C1D2CA78800690609 /* SRProxyConnect.m in Sources */, + 81D6476D1D2CA78800690609 /* SRMutex.m in Sources */, + 81D6476E1D2CA78800690609 /* SRRunLoopThread.m in Sources */, + 81D6476F1D2CA78800690609 /* SRLog.m in Sources */, + 81D647701D2CA78800690609 /* SRHash.m in Sources */, + 81D647711D2CA78800690609 /* SRDelegateController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F62417DF14D52F3C003CE997 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F62417F114D52F3C003CE997 /* main.m in Sources */, + F62417F514D52F3C003CE997 /* TCAppDelegate.m in Sources */, + F62417FB14D52F3C003CE997 /* TCViewController.m in Sources */, + F624180114D5300C003CE997 /* TCChatCell.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F6396B85153E67EC00345B5E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 81CD05DD1CEEC47300497F47 /* NSURLRequest+SRWebSocket.m in Sources */, + F5391CC41D2F4B4700606A81 /* SRSIMDHelpers.m in Sources */, + 81B22ECB1CE42D7E0073C636 /* SRError.m in Sources */, + 81B31C1A1CDC404100D86D43 /* SRIOConsumer.m in Sources */, + 81C22BC81D124168007BFDDF /* SRHTTPConnectMessage.m in Sources */, + 454FEA7E1D2570F600073768 /* SRPinningSecurityPolicy.m in Sources */, + 454FEA861D25719A00073768 /* SRSecurityPolicy.m in Sources */, + 815FE72C1D497D720085FDA5 /* SRConstants.m in Sources */, + 81CD06031CEEC65D00497F47 /* NSRunLoop+SRWebSocket.m in Sources */, + F6396B86153E67EC00345B5E /* SRWebSocket.m in Sources */, + 81C22BFE1D1256E1007BFDDF /* SRRandom.m in Sources */, + 81B31C221CDC404100D86D43 /* SRIOConsumerPool.m in Sources */, + 81B22EEA1CE43ECC0073C636 /* SRURLUtilities.m in Sources */, + 8133640E1D091E1B0062E28D /* SRProxyConnect.m in Sources */, + 817491AE1D1C8C33006E09DF /* SRMutex.m in Sources */, + 81B31C651CDC444900D86D43 /* SRRunLoopThread.m in Sources */, + 81900A521D18C9CC0015A290 /* SRLog.m in Sources */, + 81B31C331CDC406B00D86D43 /* SRHash.m in Sources */, + 8179958C1CE139700084DA37 /* SRDelegateController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F6BDA7FD145900D200FE3253 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8105E4AE1CDD6E6200AA12DB /* SRAutobahnOperation.m in Sources */, + 817996801CE184F40084DA37 /* SRAutobahnUtilities.m in Sources */, + 8105E4801CDD67B400AA12DB /* SRAutobahnTests.m in Sources */, + 8105E4821CDD67BD00AA12DB /* SRTWebSocketOperation.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 81D647B41D2CA8DD00690609 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 81D6475F1D2CA78800690609 /* SocketRocket-iOS */; + targetProxy = 81D647B31D2CA8DD00690609 /* PBXContainerItemProxy */; + }; + 81E8A6A11D4C41DA00916C7E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2D4227611BB4358C000C1A6C /* SocketRocket-iOS-Dynamic */; + targetProxy = 81E8A6A01D4C41DA00916C7E /* PBXContainerItemProxy */; + }; + 81E8A6A51D4C41E000916C7E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 81D6475F1D2CA78800690609 /* SocketRocket-iOS */; + targetProxy = 81E8A6A41D4C41E000916C7E /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + F62417ED14D52F3C003CE997 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + F62417EE14D52F3C003CE997 /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + F62417F614D52F3C003CE997 /* MainStoryboard.storyboard */ = { + isa = PBXVariantGroup; + children = ( + F62417F714D52F3C003CE997 /* en */, + ); + name = MainStoryboard.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 2D4227681BB4358C000C1A6C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81D647591D2CA6A100690609 /* SocketRocket-iOS-Dynamic.xcconfig */; + buildSettings = { + MTL_ENABLE_DEBUG_INFO = YES; + }; + name = Debug; + }; + 2D4227691BB4358C000C1A6C /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81D647591D2CA6A100690609 /* SocketRocket-iOS-Dynamic.xcconfig */; + buildSettings = { + MTL_ENABLE_DEBUG_INFO = NO; + }; + name = Release; + }; + 3345DC8E1C52ACD70083CCB8 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81D6475C1D2CA6A100690609 /* SocketRocket-tvOS.xcconfig */; + buildSettings = { + MTL_ENABLE_DEBUG_INFO = YES; + TARGETED_DEVICE_FAMILY = 3; + }; + name = Debug; + }; + 3345DC8F1C52ACD70083CCB8 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81D6475C1D2CA6A100690609 /* SocketRocket-tvOS.xcconfig */; + buildSettings = { + MTL_ENABLE_DEBUG_INFO = NO; + TARGETED_DEVICE_FAMILY = 3; + }; + name = Release; + }; + 81D6478B1D2CA78800690609 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81D6475A1D2CA6A100690609 /* SocketRocket-iOS.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + 81D6478C1D2CA78800690609 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81D6475A1D2CA6A100690609 /* SocketRocket-iOS.xcconfig */; + buildSettings = { + }; + name = Release; + }; + F62417FD14D52F3C003CE997 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81E8A69A1D4C417A00916C7E /* TestChat-iOS.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + F62417FE14D52F3C003CE997 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81E8A69A1D4C417A00916C7E /* TestChat-iOS.xcconfig */; + buildSettings = { + }; + name = Release; + }; + F668C892153E91210044DBAC /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81D6475B1D2CA6A100690609 /* SocketRocket-macOS.xcconfig */; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + }; + name = Debug; + }; + F668C893153E91210044DBAC /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81D6475B1D2CA6A100690609 /* SocketRocket-macOS.xcconfig */; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + }; + name = Release; + }; + F6B208381450F597009315AF /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81D647561D2CA6A100690609 /* Debug.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + F6B208391450F597009315AF /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81D647571D2CA6A100690609 /* Release.xcconfig */; + buildSettings = { + }; + name = Release; + }; + F6BDA811145900D200FE3253 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81D6475D1D2CA6A100690609 /* SocketRocketTests-iOS.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + F6BDA812145900D200FE3253 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 81D6475D1D2CA6A100690609 /* SocketRocketTests-iOS.xcconfig */; + buildSettings = { + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 2D4227671BB4358C000C1A6C /* Build configuration list for PBXNativeTarget "SocketRocket-iOS-Dynamic" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2D4227681BB4358C000C1A6C /* Debug */, + 2D4227691BB4358C000C1A6C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3345DC8D1C52ACD70083CCB8 /* Build configuration list for PBXNativeTarget "SocketRocket-tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3345DC8E1C52ACD70083CCB8 /* Debug */, + 3345DC8F1C52ACD70083CCB8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 81D6478A1D2CA78800690609 /* Build configuration list for PBXNativeTarget "SocketRocket-iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 81D6478B1D2CA78800690609 /* Debug */, + 81D6478C1D2CA78800690609 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F62417FC14D52F3C003CE997 /* Build configuration list for PBXNativeTarget "TestChat" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F62417FD14D52F3C003CE997 /* Debug */, + F62417FE14D52F3C003CE997 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F668C891153E91210044DBAC /* Build configuration list for PBXNativeTarget "SocketRocket-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F668C892153E91210044DBAC /* Debug */, + F668C893153E91210044DBAC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F6B208271450F597009315AF /* Build configuration list for PBXProject "SocketRocket" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F6B208381450F597009315AF /* Debug */, + F6B208391450F597009315AF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F6BDA813145900D200FE3253 /* Build configuration list for PBXNativeTarget "SocketRocketTests-iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F6BDA811145900D200FE3253 /* Debug */, + F6BDA812145900D200FE3253 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = F6B208241450F597009315AF /* Project object */; +} diff --git a/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100755 index 0000000..998c1c0 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100755 index 0000000..08de0be --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded + + + diff --git a/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/xcshareddata/xcschemes/SocketRocket-iOS-Dynamic.xcscheme b/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/xcshareddata/xcschemes/SocketRocket-iOS-Dynamic.xcscheme new file mode 100755 index 0000000..ab26139 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/xcshareddata/xcschemes/SocketRocket-iOS-Dynamic.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/xcshareddata/xcschemes/SocketRocket-iOS.xcscheme b/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/xcshareddata/xcschemes/SocketRocket-iOS.xcscheme new file mode 100755 index 0000000..5565f08 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/xcshareddata/xcschemes/SocketRocket-iOS.xcscheme @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/xcshareddata/xcschemes/SocketRocket-macOS.xcscheme b/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/xcshareddata/xcschemes/SocketRocket-macOS.xcscheme new file mode 100755 index 0000000..1408491 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/xcshareddata/xcschemes/SocketRocket-macOS.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/xcshareddata/xcschemes/SocketRocket-tvOS.xcscheme b/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/xcshareddata/xcschemes/SocketRocket-tvOS.xcscheme new file mode 100755 index 0000000..6ef9ee9 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/xcshareddata/xcschemes/SocketRocket-tvOS.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/xcshareddata/xcschemes/SocketRocketTests.xcscheme b/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/xcshareddata/xcschemes/SocketRocketTests.xcscheme new file mode 100755 index 0000000..0610291 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/xcshareddata/xcschemes/SocketRocketTests.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/xcshareddata/xcschemes/TestChat.xcscheme b/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/xcshareddata/xcschemes/TestChat.xcscheme new file mode 100755 index 0000000..9c2f485 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket.xcodeproj/xcshareddata/xcschemes/TestChat.xcscheme @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/Delegate/SRDelegateController.h b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Delegate/SRDelegateController.h new file mode 100755 index 0000000..0004a01 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Delegate/SRDelegateController.h @@ -0,0 +1,67 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +#if OBJC_BOOL_IS_BOOL + +struct SRDelegateAvailableMethods { + BOOL didReceiveMessage : 1; + BOOL didReceiveMessageWithString : 1; + BOOL didReceiveMessageWithData : 1; + BOOL didOpen : 1; + BOOL didFailWithError : 1; + BOOL didCloseWithCode : 1; + BOOL didReceivePing : 1; + BOOL didReceivePong : 1; + BOOL shouldConvertTextFrameToString : 1; +}; + +#else + +struct SRDelegateAvailableMethods { + BOOL didReceiveMessage; + BOOL didReceiveMessageWithString; + BOOL didReceiveMessageWithData; + BOOL didOpen; + BOOL didFailWithError; + BOOL didCloseWithCode; + BOOL didReceivePing; + BOOL didReceivePong; + BOOL shouldConvertTextFrameToString; +}; + +#endif + +typedef struct SRDelegateAvailableMethods SRDelegateAvailableMethods; + +typedef void(^SRDelegateBlock)(id _Nullable delegate, SRDelegateAvailableMethods availableMethods); + +@interface SRDelegateController : NSObject + +@property (nonatomic, weak) id delegate; +@property (atomic, readonly) SRDelegateAvailableMethods availableDelegateMethods; + +@property (nullable, nonatomic, strong) dispatch_queue_t dispatchQueue; +@property (nullable, nonatomic, strong) NSOperationQueue *operationQueue; + +///-------------------------------------- +#pragma mark - Perform +///-------------------------------------- + +- (void)performDelegateBlock:(SRDelegateBlock)block; +- (void)performDelegateQueueBlock:(dispatch_block_t)block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/Delegate/SRDelegateController.m b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Delegate/SRDelegateController.m new file mode 100755 index 0000000..69642d4 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Delegate/SRDelegateController.m @@ -0,0 +1,138 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import "SRDelegateController.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface SRDelegateController () + +@property (nonatomic, strong, readonly) dispatch_queue_t accessQueue; + +@property (atomic, assign, readwrite) SRDelegateAvailableMethods availableDelegateMethods; + +@end + +@implementation SRDelegateController + +@synthesize delegate = _delegate; +@synthesize dispatchQueue = _dispatchQueue; +@synthesize operationQueue = _operationQueue; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init +{ + self = [super init]; + if (!self) return self; + + _accessQueue = dispatch_queue_create("com.facebook.socketrocket.delegate.access", DISPATCH_QUEUE_CONCURRENT); + _dispatchQueue = dispatch_get_main_queue(); + + return self; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (void)setDelegate:(id _Nullable)delegate +{ + dispatch_barrier_async(self.accessQueue, ^{ + _delegate = delegate; + + self.availableDelegateMethods = (SRDelegateAvailableMethods){ + .didReceiveMessage = [delegate respondsToSelector:@selector(webSocket:didReceiveMessage:)], + .didReceiveMessageWithString = [delegate respondsToSelector:@selector(webSocket:didReceiveMessageWithString:)], + .didReceiveMessageWithData = [delegate respondsToSelector:@selector(webSocket:didReceiveMessageWithData:)], + .didOpen = [delegate respondsToSelector:@selector(webSocketDidOpen:)], + .didFailWithError = [delegate respondsToSelector:@selector(webSocket:didFailWithError:)], + .didCloseWithCode = [delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)], + .didReceivePing = [delegate respondsToSelector:@selector(webSocket:didReceivePingWithData:)], + .didReceivePong = [delegate respondsToSelector:@selector(webSocket:didReceivePong:)], + .shouldConvertTextFrameToString = [delegate respondsToSelector:@selector(webSocketShouldConvertTextFrameToString:)] + }; + }); +} + +- (id _Nullable)delegate +{ + __block id delegate = nil; + dispatch_sync(self.accessQueue, ^{ + delegate = _delegate; + }); + return delegate; +} + +- (void)setDispatchQueue:(dispatch_queue_t _Nullable)queue +{ + dispatch_barrier_async(self.accessQueue, ^{ + _dispatchQueue = queue ?: dispatch_get_main_queue(); + _operationQueue = nil; + }); +} + +- (dispatch_queue_t _Nullable)dispatchQueue +{ + __block dispatch_queue_t queue = nil; + dispatch_sync(self.accessQueue, ^{ + queue = _dispatchQueue; + }); + return queue; +} + +- (void)setOperationQueue:(NSOperationQueue *_Nullable)queue +{ + dispatch_barrier_async(self.accessQueue, ^{ + _dispatchQueue = queue ? nil : dispatch_get_main_queue(); + _operationQueue = queue; + }); +} + +- (NSOperationQueue *_Nullable)operationQueue +{ + __block NSOperationQueue *queue = nil; + dispatch_sync(self.accessQueue, ^{ + queue = _operationQueue; + }); + return queue; +} + +///-------------------------------------- +#pragma mark - Perform +///-------------------------------------- + +- (void)performDelegateBlock:(SRDelegateBlock)block +{ + __block __strong id delegate = nil; + __block SRDelegateAvailableMethods availableMethods = {}; + dispatch_sync(self.accessQueue, ^{ + delegate = _delegate; // Not `OK` to go through `self`, since queue sync. + availableMethods = self.availableDelegateMethods; // `OK` to call through `self`, since no queue sync. + }); + [self performDelegateQueueBlock:^{ + block(delegate, availableMethods); + }]; +} + +- (void)performDelegateQueueBlock:(dispatch_block_t)block +{ + dispatch_queue_t dispatchQueue = self.dispatchQueue; + if (dispatchQueue) { + dispatch_async(dispatchQueue, block); + } else { + [self.operationQueue addOperationWithBlock:block]; + } +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/IOConsumer/SRIOConsumer.h b/Sources/Vendor/SocketRocket/SocketRocket/Internal/IOConsumer/SRIOConsumer.h new file mode 100755 index 0000000..855eef2 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/IOConsumer/SRIOConsumer.h @@ -0,0 +1,40 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import + +@class SRWebSocket; // TODO: (nlutsenko) Remove dependency on SRWebSocket here. + +// 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; +} +@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; + +- (void)resetWithScanner:(stream_scanner)scanner + handler:(data_callback)handler + bytesNeeded:(size_t)bytesNeeded + readToCurrentFrame:(BOOL)readToCurrentFrame + unmaskBytes:(BOOL)unmaskBytes; + +@end diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/IOConsumer/SRIOConsumer.m b/Sources/Vendor/SocketRocket/SocketRocket/Internal/IOConsumer/SRIOConsumer.m new file mode 100755 index 0000000..c731b95 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/IOConsumer/SRIOConsumer.m @@ -0,0 +1,36 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import "SRIOConsumer.h" + +@implementation SRIOConsumer + +@synthesize bytesNeeded = _bytesNeeded; +@synthesize consumer = _scanner; +@synthesize handler = _handler; +@synthesize readToCurrentFrame = _readToCurrentFrame; +@synthesize unmaskBytes = _unmaskBytes; + +- (void)resetWithScanner:(stream_scanner)scanner + handler:(data_callback)handler + bytesNeeded:(size_t)bytesNeeded + readToCurrentFrame:(BOOL)readToCurrentFrame + unmaskBytes:(BOOL)unmaskBytes +{ + _scanner = [scanner copy]; + _handler = [handler copy]; + _bytesNeeded = bytesNeeded; + _readToCurrentFrame = readToCurrentFrame; + _unmaskBytes = unmaskBytes; + assert(_scanner || _bytesNeeded); +} + +@end diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/IOConsumer/SRIOConsumerPool.h b/Sources/Vendor/SocketRocket/SocketRocket/Internal/IOConsumer/SRIOConsumerPool.h new file mode 100755 index 0000000..37bd24c --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/IOConsumer/SRIOConsumerPool.h @@ -0,0 +1,28 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import + +#import "SRIOConsumer.h" // TODO: (nlutsenko) Convert to @class and constants file for block types + +// This class is not thread-safe, and is expected to always be run on the same queue. +@interface SRIOConsumerPool : NSObject + +- (instancetype)initWithBufferCapacity:(NSUInteger)poolSize; + +- (SRIOConsumer *)consumerWithScanner:(stream_scanner)scanner + handler:(data_callback)handler + bytesNeeded:(size_t)bytesNeeded + readToCurrentFrame:(BOOL)readToCurrentFrame + unmaskBytes:(BOOL)unmaskBytes; +- (void)returnConsumer:(SRIOConsumer *)consumer; + +@end diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/IOConsumer/SRIOConsumerPool.m b/Sources/Vendor/SocketRocket/SocketRocket/Internal/IOConsumer/SRIOConsumerPool.m new file mode 100755 index 0000000..d8aab6f --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/IOConsumer/SRIOConsumerPool.m @@ -0,0 +1,64 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import "SRIOConsumerPool.h" + +@implementation SRIOConsumerPool { + NSUInteger _poolSize; + NSMutableArray *_bufferedConsumers; +} + +- (instancetype)initWithBufferCapacity:(NSUInteger)poolSize; +{ + self = [super init]; + if (self) { + _poolSize = poolSize; + _bufferedConsumers = [NSMutableArray arrayWithCapacity:poolSize]; + } + return self; +} + +- (instancetype)init +{ + return [self initWithBufferCapacity:8]; +} + +- (SRIOConsumer *)consumerWithScanner:(stream_scanner)scanner + handler:(data_callback)handler + bytesNeeded:(size_t)bytesNeeded + readToCurrentFrame:(BOOL)readToCurrentFrame + unmaskBytes:(BOOL)unmaskBytes +{ + SRIOConsumer *consumer = nil; + if (_bufferedConsumers.count) { + consumer = [_bufferedConsumers lastObject]; + [_bufferedConsumers removeLastObject]; + } else { + consumer = [[SRIOConsumer alloc] init]; + } + + [consumer resetWithScanner:scanner + handler:handler + bytesNeeded:bytesNeeded + readToCurrentFrame:readToCurrentFrame + unmaskBytes:unmaskBytes]; + + return consumer; +} + +- (void)returnConsumer:(SRIOConsumer *)consumer; +{ + if (_bufferedConsumers.count < _poolSize) { + [_bufferedConsumers addObject:consumer]; + } +} + +@end diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/NSRunLoop+SRWebSocketPrivate.h b/Sources/Vendor/SocketRocket/SocketRocket/Internal/NSRunLoop+SRWebSocketPrivate.h new file mode 100755 index 0000000..098f7a8 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/NSRunLoop+SRWebSocketPrivate.h @@ -0,0 +1,13 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import + +// Empty function that force links the object file for the category. +extern void import_NSRunLoop_SRWebSocket(void); diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/NSURLRequest+SRWebSocketPrivate.h b/Sources/Vendor/SocketRocket/SocketRocket/Internal/NSURLRequest+SRWebSocketPrivate.h new file mode 100755 index 0000000..b09dde4 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/NSURLRequest+SRWebSocketPrivate.h @@ -0,0 +1,13 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import + +// Empty function that force links the object file for the category. +extern void import_NSURLRequest_SRWebSocket(void); diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/Proxy/SRProxyConnect.h b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Proxy/SRProxyConnect.h new file mode 100755 index 0000000..ed17d39 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Proxy/SRProxyConnect.h @@ -0,0 +1,26 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef void(^SRProxyConnectCompletion)(NSError *_Nullable error, + NSInputStream *_Nullable readStream, + NSOutputStream *_Nullable writeStream); + +@interface SRProxyConnect : NSObject + +- (instancetype)initWithURL:(NSURL *)url; + +- (void)openNetworkStreamWithCompletion:(SRProxyConnectCompletion)completion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/Proxy/SRProxyConnect.m b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Proxy/SRProxyConnect.m new file mode 100755 index 0000000..aa119a7 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Proxy/SRProxyConnect.m @@ -0,0 +1,481 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import "SRProxyConnect.h" + +#import "NSRunLoop+SRWebSocket.h" +#import "SRConstants.h" +#import "SRError.h" +#import "SRLog.h" +#import "SRURLUtilities.h" + +@interface SRProxyConnect() + +@property (nonatomic, strong) NSURL *url; +@property (nonatomic, strong) NSInputStream *inputStream; +@property (nonatomic, strong) NSOutputStream *outputStream; + +@end + +@implementation SRProxyConnect +{ + SRProxyConnectCompletion _completion; + + NSString *_httpProxyHost; + uint32_t _httpProxyPort; + + CFHTTPMessageRef _receivedHTTPHeaders; + + NSString *_socksProxyHost; + uint32_t _socksProxyPort; + NSString *_socksProxyUsername; + NSString *_socksProxyPassword; + + BOOL _connectionRequiresSSL; + + NSMutableArray *_inputQueue; + dispatch_queue_t _writeQueue; +} + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +-(instancetype)initWithURL:(NSURL *)url +{ + self = [super init]; + if (!self) return self; + + _url = url; + _connectionRequiresSSL = SRURLRequiresSSL(url); + + _writeQueue = dispatch_queue_create("com.facebook.socketrocket.proxyconnect.write", DISPATCH_QUEUE_SERIAL); + _inputQueue = [NSMutableArray arrayWithCapacity:2]; + + return self; +} + +- (void)dealloc +{ + // If we get deallocated before the socket open finishes - we need to cleanup everything. + + [self.inputStream removeFromRunLoop:[NSRunLoop SR_networkRunLoop] forMode:NSDefaultRunLoopMode]; + self.inputStream.delegate = nil; + [self.inputStream close]; + self.inputStream = nil; + + self.outputStream.delegate = nil; + [self.outputStream close]; + self.outputStream = nil; +} + +///-------------------------------------- +#pragma mark - Open +///-------------------------------------- + +- (void)openNetworkStreamWithCompletion:(SRProxyConnectCompletion)completion +{ + _completion = completion; + [self _configureProxy]; +} + +///-------------------------------------- +#pragma mark - Flow +///-------------------------------------- + +- (void)_didConnect +{ + SRDebugLog(@"_didConnect, return streams"); + if (_connectionRequiresSSL) { + if (_httpProxyHost) { + // Must set the real peer name before turning on SSL + SRDebugLog(@"proxy set peer name to real host %@", self.url.host); + [self.outputStream setProperty:self.url.host forKey:@"_kCFStreamPropertySocketPeerName"]; + } + } + if (_receivedHTTPHeaders) { + CFRelease(_receivedHTTPHeaders); + _receivedHTTPHeaders = NULL; + } + + NSInputStream *inputStream = self.inputStream; + NSOutputStream *outputStream = self.outputStream; + + self.inputStream = nil; + self.outputStream = nil; + + [inputStream removeFromRunLoop:[NSRunLoop SR_networkRunLoop] forMode:NSDefaultRunLoopMode]; + inputStream.delegate = nil; + outputStream.delegate = nil; + + _completion(nil, inputStream, outputStream); +} + +- (void)_failWithError:(NSError *)error +{ + SRDebugLog(@"_failWithError, return error"); + if (!error) { + error = SRHTTPErrorWithCodeDescription(500, 2132,@"Proxy Error"); + } + + if (_receivedHTTPHeaders) { + CFRelease(_receivedHTTPHeaders); + _receivedHTTPHeaders = NULL; + } + + self.inputStream.delegate = nil; + self.outputStream.delegate = nil; + + [self.inputStream removeFromRunLoop:[NSRunLoop SR_networkRunLoop] + forMode:NSDefaultRunLoopMode]; + [self.inputStream close]; + [self.outputStream close]; + self.inputStream = nil; + self.outputStream = nil; + _completion(error, nil, nil); +} + +// get proxy setting from device setting +- (void)_configureProxy +{ + SRDebugLog(@"configureProxy"); + NSDictionary *proxySettings = CFBridgingRelease(CFNetworkCopySystemProxySettings()); + + // CFNetworkCopyProxiesForURL doesn't understand ws:// or wss:// + NSURL *httpURL; + if (_connectionRequiresSSL) { + httpURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://%@", _url.host]]; + } else { + httpURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@", _url.host]]; + } + + NSArray *proxies = CFBridgingRelease(CFNetworkCopyProxiesForURL((__bridge CFURLRef)httpURL, (__bridge CFDictionaryRef)proxySettings)); + if (proxies.count == 0) { + SRDebugLog(@"configureProxy no proxies"); + [self _openConnection]; + return; // no proxy + } + NSDictionary *settings = [proxies objectAtIndex:0]; + NSString *proxyType = settings[(NSString *)kCFProxyTypeKey]; + if ([proxyType isEqualToString:(NSString *)kCFProxyTypeAutoConfigurationURL]) { + NSURL *pacURL = settings[(NSString *)kCFProxyAutoConfigurationURLKey]; + if (pacURL) { + [self _fetchPAC:pacURL withProxySettings:proxySettings]; + return; + } + } + if ([proxyType isEqualToString:(__bridge NSString *)kCFProxyTypeAutoConfigurationJavaScript]) { + NSString *script = settings[(__bridge NSString *)kCFProxyAutoConfigurationJavaScriptKey]; + if (script) { + [self _runPACScript:script withProxySettings:proxySettings]; + return; + } + } + [self _readProxySettingWithType:proxyType settings:settings]; + + [self _openConnection]; +} + +- (void)_readProxySettingWithType:(NSString *)proxyType settings:(NSDictionary *)settings +{ + if ([proxyType isEqualToString:(NSString *)kCFProxyTypeHTTP] || + [proxyType isEqualToString:(NSString *)kCFProxyTypeHTTPS]) { + _httpProxyHost = settings[(NSString *)kCFProxyHostNameKey]; + NSNumber *portValue = settings[(NSString *)kCFProxyPortNumberKey]; + if (portValue != nil) { + _httpProxyPort = [portValue intValue]; + } + } + if ([proxyType isEqualToString:(NSString *)kCFProxyTypeSOCKS]) { + _socksProxyHost = settings[(NSString *)kCFProxyHostNameKey]; + NSNumber *portValue = settings[(NSString *)kCFProxyPortNumberKey]; + if (portValue != nil) + _socksProxyPort = [portValue intValue]; + _socksProxyUsername = settings[(NSString *)kCFProxyUsernameKey]; + _socksProxyPassword = settings[(NSString *)kCFProxyPasswordKey]; + } + if (_httpProxyHost) { + SRDebugLog(@"Using http proxy %@:%u", _httpProxyHost, _httpProxyPort); + } else if (_socksProxyHost) { + SRDebugLog(@"Using socks proxy %@:%u", _socksProxyHost, _socksProxyPort); + } else { + SRDebugLog(@"configureProxy no proxies"); + } +} + +- (void)_fetchPAC:(NSURL *)PACurl withProxySettings:(NSDictionary *)proxySettings +{ + SRDebugLog(@"SRWebSocket fetchPAC:%@", PACurl); + + if ([PACurl isFileURL]) { + NSError *error = nil; + NSString *script = [NSString stringWithContentsOfURL:PACurl + usedEncoding:NULL + error:&error]; + + if (error) { + [self _openConnection]; + } else { + [self _runPACScript:script withProxySettings:proxySettings]; + } + return; + } + + NSString *scheme = [PACurl.scheme lowercaseString]; + if (![scheme isEqualToString:@"http"] && ![scheme isEqualToString:@"https"]) { + // Don't know how to read data from this URL, we'll have to give up + // We'll simply assume no proxies, and start the request as normal + [self _openConnection]; + return; + } + __weak typeof(self) wself = self; + NSURLRequest *request = [NSURLRequest requestWithURL:PACurl]; + NSURLSession *session = [NSURLSession sharedSession]; + [[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + __strong typeof(wself) sself = wself; + if (!error) { + NSString *script = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + [sself _runPACScript:script withProxySettings:proxySettings]; + } else { + [sself _openConnection]; + } + }] resume]; +} + +- (void)_runPACScript:(NSString *)script withProxySettings:(NSDictionary *)proxySettings +{ + if (!script) { + [self _openConnection]; + return; + } + SRDebugLog(@"runPACScript"); + // From: http://developer.apple.com/samplecode/CFProxySupportTool/listing1.html + // Work around . This dummy call to + // CFNetworkCopyProxiesForURL initialise some state within CFNetwork + // that is required by CFNetworkCopyProxiesForAutoConfigurationScript. + CFBridgingRelease(CFNetworkCopyProxiesForURL((__bridge CFURLRef)_url, (__bridge CFDictionaryRef)proxySettings)); + + // Obtain the list of proxies by running the autoconfiguration script + CFErrorRef err = NULL; + + // CFNetworkCopyProxiesForAutoConfigurationScript doesn't understand ws:// or wss:// + NSURL *httpURL; + if (_connectionRequiresSSL) + httpURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://%@", _url.host]]; + else + httpURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@", _url.host]]; + + NSArray *proxies = CFBridgingRelease(CFNetworkCopyProxiesForAutoConfigurationScript((__bridge CFStringRef)script,(__bridge CFURLRef)httpURL, &err)); + if (!err && [proxies count] > 0) { + NSDictionary *settings = [proxies objectAtIndex:0]; + NSString *proxyType = settings[(NSString *)kCFProxyTypeKey]; + [self _readProxySettingWithType:proxyType settings:settings]; + } + [self _openConnection]; +} + +- (void)_openConnection +{ + [self _initializeStreams]; + + [self.inputStream scheduleInRunLoop:[NSRunLoop SR_networkRunLoop] + forMode:NSDefaultRunLoopMode]; + //[self.outputStream scheduleInRunLoop:[NSRunLoop SR_networkRunLoop] + // forMode:NSDefaultRunLoopMode]; + [self.outputStream open]; + [self.inputStream open]; +} + +- (void)_initializeStreams +{ + assert(_url.port.unsignedIntValue <= UINT32_MAX); + uint32_t port = _url.port.unsignedIntValue; + if (port == 0) { + port = (_connectionRequiresSSL ? 443 : 80); + } + NSString *host = _url.host; + + if (_httpProxyHost) { + host = _httpProxyHost; + port = (_httpProxyPort ?: 80); + } + + CFReadStreamRef readStream = NULL; + CFWriteStreamRef writeStream = NULL; + + SRDebugLog(@"ProxyConnect connect stream to %@:%u", host, port); + CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &readStream, &writeStream); + + self.outputStream = CFBridgingRelease(writeStream); + self.inputStream = CFBridgingRelease(readStream); + + if (_socksProxyHost) { + SRDebugLog(@"ProxyConnect set sock property stream to %@:%u user %@ password %@", _socksProxyHost, _socksProxyPort, _socksProxyUsername, _socksProxyPassword); + NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithCapacity:4]; + settings[NSStreamSOCKSProxyHostKey] = _socksProxyHost; + if (_socksProxyPort) { + settings[NSStreamSOCKSProxyPortKey] = @(_socksProxyPort); + } + if (_socksProxyUsername) { + settings[NSStreamSOCKSProxyUserKey] = _socksProxyUsername; + } + if (_socksProxyPassword) { + settings[NSStreamSOCKSProxyPasswordKey] = _socksProxyPassword; + } + [self.inputStream setProperty:settings forKey:NSStreamSOCKSProxyConfigurationKey]; + [self.outputStream setProperty:settings forKey:NSStreamSOCKSProxyConfigurationKey]; + } + self.inputStream.delegate = self; + self.outputStream.delegate = self; +} + +- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode; +{ + SRDebugLog(@"stream handleEvent %u", eventCode); + switch (eventCode) { + case NSStreamEventOpenCompleted: { + if (aStream == self.inputStream) { + if (_httpProxyHost) { + [self _proxyDidConnect]; + } else { + [self _didConnect]; + } + } + } break; + case NSStreamEventErrorOccurred: { + [self _failWithError:aStream.streamError]; + } break; + case NSStreamEventEndEncountered: { + [self _failWithError:aStream.streamError]; + } break; + case NSStreamEventHasBytesAvailable: { + if (aStream == _inputStream) { + [self _processInputStream]; + } + } break; + case NSStreamEventHasSpaceAvailable: + case NSStreamEventNone: + SRDebugLog(@"(default) %@", aStream); + break; + } +} + +- (void)_proxyDidConnect +{ + SRDebugLog(@"Proxy Connected"); + uint32_t port = _url.port.unsignedIntValue; + if (port == 0) { + port = (_connectionRequiresSSL ? 443 : 80); + } + // Send HTTP CONNECT Request + NSString *connectRequestStr = [NSString stringWithFormat:@"CONNECT %@:%u HTTP/1.1\r\nHost: %@\r\nConnection: keep-alive\r\nProxy-Connection: keep-alive\r\n\r\n", _url.host, port, _url.host]; + + NSData *message = [connectRequestStr dataUsingEncoding:NSUTF8StringEncoding]; + SRDebugLog(@"Proxy sending %@", connectRequestStr); + + [self _writeData:message]; +} + +///handles the incoming bytes and sending them to the proper processing method +- (void)_processInputStream +{ + NSMutableData *buf = [NSMutableData dataWithCapacity:SRDefaultBufferSize()]; + uint8_t *buffer = buf.mutableBytes; + NSInteger length = [_inputStream read:buffer maxLength:SRDefaultBufferSize()]; + + if (length <= 0) { + return; + } + + BOOL process = (_inputQueue.count == 0); + [_inputQueue addObject:[NSData dataWithBytes:buffer length:length]]; + + if (process) { + [self _dequeueInput]; + } +} + +// dequeue the incoming input so it is processed in order + +- (void)_dequeueInput +{ + while (_inputQueue.count > 0) { + NSData *data = _inputQueue.firstObject; + [_inputQueue removeObjectAtIndex:0]; + + // No need to process any data further, we got the full header data. + if ([self _proxyProcessHTTPResponseWithData:data]) { + break; + } + } +} +//handle checking the proxy connection status +- (BOOL)_proxyProcessHTTPResponseWithData:(NSData *)data +{ + if (_receivedHTTPHeaders == NULL) { + _receivedHTTPHeaders = CFHTTPMessageCreateEmpty(NULL, NO); + } + + CFHTTPMessageAppendBytes(_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length); + if (CFHTTPMessageIsHeaderComplete(_receivedHTTPHeaders)) { + SRDebugLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_receivedHTTPHeaders))); + [self _proxyHTTPHeadersDidFinish]; + return YES; + } + + return NO; +} + +- (void)_proxyHTTPHeadersDidFinish +{ + NSInteger responseCode = CFHTTPMessageGetResponseStatusCode(_receivedHTTPHeaders); + + if (responseCode >= 299) { + SRDebugLog(@"Connect to Proxy Request failed with response code %d", responseCode); + NSError *error = SRHTTPErrorWithCodeDescription(responseCode, 2132, + [NSString stringWithFormat:@"Received bad response code from proxy server: %d.", + (int)responseCode]); + [self _failWithError:error]; + return; + } + SRDebugLog(@"proxy connect return %d, call socket connect", responseCode); + [self _didConnect]; +} + +static NSTimeInterval const SRProxyConnectWriteTimeout = 5.0; + +- (void)_writeData:(NSData *)data +{ + const uint8_t * bytes = data.bytes; + __block NSInteger timeout = (NSInteger)(SRProxyConnectWriteTimeout * 1000000); // wait timeout before giving up + __weak typeof(self) wself = self; + dispatch_async(_writeQueue, ^{ + __strong typeof(wself) sself = self; + if (!sself) { + return; + } + NSOutputStream *outStream = sself.outputStream; + if (!outStream) { + return; + } + while (![outStream hasSpaceAvailable]) { + usleep(100); //wait until the socket is ready + timeout -= 100; + if (timeout < 0) { + NSError *error = SRHTTPErrorWithCodeDescription(408, 2132, @"Proxy timeout"); + [sself _failWithError:error]; + } else if (outStream.streamError != nil) { + [sself _failWithError:outStream.streamError]; + } + } + [outStream write:bytes maxLength:data.length]; + }); +} + +@end diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/RunLoop/SRRunLoopThread.h b/Sources/Vendor/SocketRocket/SocketRocket/Internal/RunLoop/SRRunLoopThread.h new file mode 100755 index 0000000..380cfa0 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/RunLoop/SRRunLoopThread.h @@ -0,0 +1,24 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface SRRunLoopThread : NSThread + +@property (nonatomic, strong, readonly) NSRunLoop *runLoop; + ++ (instancetype)sharedThread; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/RunLoop/SRRunLoopThread.m b/Sources/Vendor/SocketRocket/SocketRocket/Internal/RunLoop/SRRunLoopThread.m new file mode 100755 index 0000000..a02b1e0 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/RunLoop/SRRunLoopThread.m @@ -0,0 +1,83 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import "SRRunLoopThread.h" + +@interface SRRunLoopThread () +{ + dispatch_group_t _waitGroup; +} + +@property (nonatomic, strong, readwrite) NSRunLoop *runLoop; + +@end + +@implementation SRRunLoopThread + ++ (instancetype)sharedThread +{ + static SRRunLoopThread *thread; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + thread = [[SRRunLoopThread alloc] init]; + thread.name = @"com.facebook.SocketRocket.NetworkThread"; + [thread start]; + }); + return thread; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + _waitGroup = dispatch_group_create(); + dispatch_group_enter(_waitGroup); + } + return self; +} + +- (void)main +{ + @autoreleasepool { + _runLoop = [NSRunLoop currentRunLoop]; + dispatch_group_leave(_waitGroup); + + // Add an empty run loop source to prevent runloop from spinning. + CFRunLoopSourceContext sourceCtx = { + .version = 0, + .info = NULL, + .retain = NULL, + .release = NULL, + .copyDescription = NULL, + .equal = NULL, + .hash = NULL, + .schedule = NULL, + .cancel = NULL, + .perform = NULL + }; + CFRunLoopSourceRef source = CFRunLoopSourceCreate(NULL, 0, &sourceCtx); + CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); + CFRelease(source); + + while ([_runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) { + + } + assert(NO); + } +} + +- (NSRunLoop *)runLoop; +{ + dispatch_group_wait(_waitGroup, DISPATCH_TIME_FOREVER); + return _runLoop; +} + +@end diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/SRConstants.h b/Sources/Vendor/SocketRocket/SocketRocket/Internal/SRConstants.h new file mode 100755 index 0000000..2244213 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/SRConstants.h @@ -0,0 +1,26 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import + +typedef NS_ENUM(NSInteger, SROpCode) +{ + SROpCodeTextFrame = 0x1, + SROpCodeBinaryFrame = 0x2, + // 3-7 reserved. + SROpCodeConnectionClose = 0x8, + SROpCodePing = 0x9, + SROpCodePong = 0xA, + // B-F reserved. +}; + +/** + Default buffer size that is used for reading/writing to streams. + */ +extern size_t SRDefaultBufferSize(void); diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/SRConstants.m b/Sources/Vendor/SocketRocket/SocketRocket/Internal/SRConstants.m new file mode 100755 index 0000000..deb6165 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/SRConstants.m @@ -0,0 +1,19 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import "SRConstants.h" + +size_t SRDefaultBufferSize(void) { + static size_t size; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + size = getpagesize(); + }); + return size; +} diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/Security/SRPinningSecurityPolicy.h b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Security/SRPinningSecurityPolicy.h new file mode 100755 index 0000000..0d498b2 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Security/SRPinningSecurityPolicy.h @@ -0,0 +1,27 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * NOTE: While publicly, SocketRocket does not support configuring the security policy with pinned certificates, + * it is still possible to manually construct a security policy of this class. If you do this, note that you may + * be open to MitM attacks, and we will not support any issues you may have. Dive at your own risk. + */ +@interface SRPinningSecurityPolicy : SRSecurityPolicy + +- (instancetype)initWithCertificates:(NSArray *)pinnedCertificates; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/Security/SRPinningSecurityPolicy.m b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Security/SRPinningSecurityPolicy.m new file mode 100755 index 0000000..a906e2a --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Security/SRPinningSecurityPolicy.m @@ -0,0 +1,73 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import "SRPinningSecurityPolicy.h" + +#import + +#import "SRLog.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface SRPinningSecurityPolicy () + +@property (nonatomic, copy, readonly) NSArray *pinnedCertificates; + +@end + +@implementation SRPinningSecurityPolicy + +- (instancetype)initWithCertificates:(NSArray *)pinnedCertificates +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" + + // Do not validate certificate chain since we're pinning to specific certificates. + self = [super initWithCertificateChainValidationEnabled:NO]; + +#pragma clang diagnostic pop + + if (!self) { return self; } + + if (pinnedCertificates.count == 0) { + @throw [NSException exceptionWithName:@"Creating security policy failed." + reason:@"Must specify at least one certificate when creating a pinning policy." + userInfo:nil]; + } + _pinnedCertificates = [pinnedCertificates copy]; + + return self; +} + +- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain +{ + SRDebugLog(@"Pinned cert count: %d", self.pinnedCertificates.count); + NSUInteger requiredCertCount = self.pinnedCertificates.count; + + NSUInteger validatedCertCount = 0; + CFIndex serverCertCount = SecTrustGetCertificateCount(serverTrust); + for (CFIndex i = 0; i < serverCertCount; i++) { + SecCertificateRef cert = SecTrustGetCertificateAtIndex(serverTrust, i); + NSData *data = CFBridgingRelease(SecCertificateCopyData(cert)); + for (id ref in self.pinnedCertificates) { + SecCertificateRef trustedCert = (__bridge SecCertificateRef)ref; + // TODO: (nlutsenko) Add caching, so we don't copy the data for every pinned cert all the time. + NSData *trustedCertData = CFBridgingRelease(SecCertificateCopyData(trustedCert)); + if ([trustedCertData isEqualToData:data]) { + validatedCertCount++; + break; + } + } + } + return (requiredCertCount == validatedCertCount); +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRError.h b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRError.h new file mode 100755 index 0000000..7e13a82 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRError.h @@ -0,0 +1,20 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +extern NSError *SRErrorWithDomainCodeDescription(NSString *domain, NSInteger code, NSString *description); +extern NSError *SRErrorWithCodeDescription(NSInteger code, NSString *description); +extern NSError *SRErrorWithCodeDescriptionUnderlyingError(NSInteger code, NSString *description, NSError *underlyingError); + +extern NSError *SRHTTPErrorWithCodeDescription(NSInteger httpCode, NSInteger errorCode, NSString *description); + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRError.m b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRError.m new file mode 100755 index 0000000..71ae729 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRError.m @@ -0,0 +1,42 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import "SRError.h" + +#import "SRWebSocket.h" + +NS_ASSUME_NONNULL_BEGIN + +NSError *SRErrorWithDomainCodeDescription(NSString *domain, NSInteger code, NSString *description) +{ + return [NSError errorWithDomain:domain code:code userInfo:@{ NSLocalizedDescriptionKey: description }]; +} + +NSError *SRErrorWithCodeDescription(NSInteger code, NSString *description) +{ + return SRErrorWithDomainCodeDescription(SRWebSocketErrorDomain, code, description); +} + +NSError *SRErrorWithCodeDescriptionUnderlyingError(NSInteger code, NSString *description, NSError *underlyingError) +{ + return [NSError errorWithDomain:SRWebSocketErrorDomain + code:code + userInfo:@{ NSLocalizedDescriptionKey: description, + NSUnderlyingErrorKey: underlyingError }]; +} + +NSError *SRHTTPErrorWithCodeDescription(NSInteger httpCode, NSInteger errorCode, NSString *description) +{ + return [NSError errorWithDomain:SRWebSocketErrorDomain + code:errorCode + userInfo:@{ NSLocalizedDescriptionKey: description, + SRHTTPResponseErrorKey: @(httpCode) }]; +} + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRHTTPConnectMessage.h b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRHTTPConnectMessage.h new file mode 100755 index 0000000..1e2302a --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRHTTPConnectMessage.h @@ -0,0 +1,20 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +extern CFHTTPMessageRef SRHTTPConnectMessageCreate(NSURLRequest *request, + NSString *securityKey, + uint8_t webSocketProtocolVersion, + NSArray *_Nullable cookies, + NSArray *_Nullable requestedProtocols); + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRHTTPConnectMessage.m b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRHTTPConnectMessage.m new file mode 100755 index 0000000..71caa00 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRHTTPConnectMessage.m @@ -0,0 +1,79 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import "SRHTTPConnectMessage.h" + +#import "SRURLUtilities.h" + +NS_ASSUME_NONNULL_BEGIN + +static NSString *_SRHTTPConnectMessageHost(NSURL *url) +{ + NSString *host = url.host; + if (url.port != nil) { + host = [host stringByAppendingFormat:@":%@", url.port]; + } + return host; +} + +CFHTTPMessageRef SRHTTPConnectMessageCreate(NSURLRequest *request, + NSString *securityKey, + uint8_t webSocketProtocolVersion, + NSArray *_Nullable cookies, + NSArray *_Nullable requestedProtocols) +{ + NSURL *url = request.URL; + + CFHTTPMessageRef message = CFHTTPMessageCreateRequest(NULL, CFSTR("GET"), (__bridge CFURLRef)url, kCFHTTPVersion1_1); + + // Set host first so it defaults + CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Host"), (__bridge CFStringRef)_SRHTTPConnectMessageHost(url)); + + NSMutableData *keyBytes = [[NSMutableData alloc] initWithLength:16]; + int result = SecRandomCopyBytes(kSecRandomDefault, keyBytes.length, keyBytes.mutableBytes); + if (result != 0) { + //TODO: (nlutsenko) Check if there was an error. + } + + // Apply cookies if any have been provided + if (cookies) { + NSDictionary *messageCookies = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies]; + [messageCookies enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull obj, BOOL * _Nonnull stop) { + if (key.length && obj.length) { + CFHTTPMessageSetHeaderFieldValue(message, (__bridge CFStringRef)key, (__bridge CFStringRef)obj); + } + }]; + } + + // set header for http basic auth + NSString *basicAuthorizationString = SRBasicAuthorizationHeaderFromURL(url); + if (basicAuthorizationString) { + CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Authorization"), (__bridge CFStringRef)basicAuthorizationString); + } + + CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Upgrade"), CFSTR("websocket")); + CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Connection"), CFSTR("Upgrade")); + CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Sec-WebSocket-Key"), (__bridge CFStringRef)securityKey); + CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Sec-WebSocket-Version"), (__bridge CFStringRef)@(webSocketProtocolVersion).stringValue); + + CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Origin"), (__bridge CFStringRef)SRURLOrigin(url)); + + if (requestedProtocols.count) { + CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Sec-WebSocket-Protocol"), + (__bridge CFStringRef)[requestedProtocols componentsJoinedByString:@", "]); + } + + [request.allHTTPHeaderFields enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + CFHTTPMessageSetHeaderFieldValue(message, (__bridge CFStringRef)key, (__bridge CFStringRef)obj); + }]; + + return message; +} + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRHash.h b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRHash.h new file mode 100755 index 0000000..3db14de --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRHash.h @@ -0,0 +1,19 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +extern NSData *SRSHA1HashFromString(NSString *string); +extern NSData *SRSHA1HashFromBytes(const char *bytes, size_t length); + +extern NSString *SRBase64EncodedStringFromData(NSData *data); + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRHash.m b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRHash.m new file mode 100755 index 0000000..275ae90 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRHash.m @@ -0,0 +1,43 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import "SRHash.h" + +#import + +NS_ASSUME_NONNULL_BEGIN + +NSData *SRSHA1HashFromString(NSString *string) +{ + size_t length = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + return SRSHA1HashFromBytes(string.UTF8String, length); +} + +NSData *SRSHA1HashFromBytes(const char *bytes, size_t length) +{ + uint8_t outputLength = CC_SHA1_DIGEST_LENGTH; + unsigned char output[outputLength]; + CC_SHA1(bytes, (CC_LONG)length, output); + + return [NSData dataWithBytes:output length:outputLength]; +} + +NSString *SRBase64EncodedStringFromData(NSData *data) +{ + if ([data respondsToSelector:@selector(base64EncodedStringWithOptions:)]) { + return [data base64EncodedStringWithOptions:0]; + } + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [data base64Encoding]; +#pragma clang diagnostic pop +} + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRLog.h b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRLog.h new file mode 100755 index 0000000..99689ef --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRLog.h @@ -0,0 +1,20 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +// Uncomment this line to enable debug logging +//#define SR_DEBUG_LOG_ENABLED + +extern void SRErrorLog(NSString *format, ...); +extern void SRDebugLog(NSString *format, ...); + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRLog.m b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRLog.m new file mode 100755 index 0000000..1c66475 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRLog.m @@ -0,0 +1,33 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import "SRLog.h" + +NS_ASSUME_NONNULL_BEGIN + +extern void SRErrorLog(NSString *format, ...) +{ + __block va_list arg_list; + va_start (arg_list, format); + + NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list]; + + va_end(arg_list); + + NSLog(@"[SocketRocket] %@", formattedString); +} + +extern void SRDebugLog(NSString *format, ...) +{ +#ifdef SR_DEBUG_LOG_ENABLED + SRErrorLog(tag, format); +#endif +} + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRMutex.h b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRMutex.h new file mode 100755 index 0000000..8226ce6 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRMutex.h @@ -0,0 +1,22 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef __attribute__((capability("mutex"))) pthread_mutex_t *SRMutex; + +extern SRMutex SRMutexInitRecursive(void); +extern void SRMutexDestroy(SRMutex mutex); + +extern void SRMutexLock(SRMutex mutex) __attribute__((acquire_capability(mutex))); +extern void SRMutexUnlock(SRMutex mutex) __attribute__((release_capability(mutex))); + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRMutex.m b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRMutex.m new file mode 100755 index 0000000..a8b978c --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRMutex.m @@ -0,0 +1,47 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import "SRMutex.h" + +#import + +NS_ASSUME_NONNULL_BEGIN + +SRMutex SRMutexInitRecursive(void) +{ + pthread_mutex_t *mutex = malloc(sizeof(pthread_mutex_t)); + pthread_mutexattr_t attributes; + + pthread_mutexattr_init(&attributes); + pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(mutex, &attributes); + pthread_mutexattr_destroy(&attributes); + + return mutex; +} + +void SRMutexDestroy(SRMutex mutex) +{ + pthread_mutex_destroy(mutex); + free(mutex); +} + +__attribute__((no_thread_safety_analysis)) +void SRMutexLock(SRMutex mutex) +{ + pthread_mutex_lock(mutex); +} + +__attribute__((no_thread_safety_analysis)) +void SRMutexUnlock(SRMutex mutex) +{ + pthread_mutex_unlock(mutex); +} + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRRandom.h b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRRandom.h new file mode 100755 index 0000000..9b116cf --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRRandom.h @@ -0,0 +1,16 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +extern NSData *SRRandomData(NSUInteger length); + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRRandom.m b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRRandom.m new file mode 100755 index 0000000..37911bb --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRRandom.m @@ -0,0 +1,26 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import "SRRandom.h" + +#import + +NS_ASSUME_NONNULL_BEGIN + +NSData *SRRandomData(NSUInteger length) +{ + NSMutableData *data = [NSMutableData dataWithLength:length]; + int result = SecRandomCopyBytes(kSecRandomDefault, data.length, data.mutableBytes); + if (result != 0) { + [NSException raise:NSInternalInconsistencyException format:@"Failed to generate random bytes with OSStatus: %d", result]; + } + return data; +} + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRSIMDHelpers.h b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRSIMDHelpers.h new file mode 100755 index 0000000..8291cc7 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRSIMDHelpers.h @@ -0,0 +1,19 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import + +/** + Unmask bytes using XOR via SIMD. + + @param bytes The bytes to unmask. + @param length The number of bytes to unmask. + @param maskKey The mask to XOR with MUST be of length sizeof(uint32_t). + */ +void SRMaskBytesSIMD(uint8_t *bytes, size_t length, uint8_t *maskKey); diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRSIMDHelpers.m b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRSIMDHelpers.m new file mode 100755 index 0000000..aa59535 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRSIMDHelpers.m @@ -0,0 +1,73 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import "SRSIMDHelpers.h" + +typedef uint8_t uint8x32_t __attribute__((vector_size(32))); + +static void SRMaskBytesManual(uint8_t *bytes, size_t length, uint8_t *maskKey) { + for (size_t i = 0; i < length; i++) { + bytes[i] = bytes[i] ^ maskKey[i % sizeof(uint32_t)]; + } +} + +/** + Right-shift the elements of a vector, circularly. + + @param vector The vector to circular shift. + @param by The number of elements to shift by. + + @return A shifted vector. + */ +static uint8x32_t SRShiftVector(uint8x32_t vector, size_t by) { + uint8x32_t vectorCopy = vector; + by = by % _Alignof(uint8x32_t); + + uint8_t *vectorPointer = (uint8_t *)&vector; + uint8_t *vectorCopyPointer = (uint8_t *)&vectorCopy; + + memmove(vectorPointer + by, vectorPointer, sizeof(vector) - by); + memcpy(vectorPointer, vectorCopyPointer + (sizeof(vector) - by), by); + + return vector; +} + +void SRMaskBytesSIMD(uint8_t *bytes, size_t length, uint8_t *maskKey) { + size_t alignmentBytes = _Alignof(uint8x32_t) - ((uintptr_t)bytes % _Alignof(uint8x32_t)); + if (alignmentBytes == _Alignof(uint8x32_t)) { + alignmentBytes = 0; + } + + // If the number of bytes that can be processed after aligning is + // less than the number of bytes we can put into a vector, + // then there's no work to do with SIMD, just call the manual version. + if (alignmentBytes > length || (length - alignmentBytes) < sizeof(uint8x32_t)) { + SRMaskBytesManual(bytes, length, maskKey); + return; + } + + size_t vectorLength = (length - alignmentBytes) / sizeof(uint8x32_t); + size_t manualStartOffset = alignmentBytes + (vectorLength * sizeof(uint8x32_t)); + size_t manualLength = length - manualStartOffset; + + uint8x32_t *vector = (uint8x32_t *)(bytes + alignmentBytes); + uint8x32_t maskVector = { }; + + memset_pattern4(&maskVector, maskKey, sizeof(uint8x32_t)); + maskVector = SRShiftVector(maskVector, alignmentBytes); + + SRMaskBytesManual(bytes, alignmentBytes, maskKey); + + for (size_t vectorIndex = 0; vectorIndex < vectorLength; vectorIndex++) { + vector[vectorIndex] = vector[vectorIndex] ^ maskVector; + } + + // Use the shifted mask for the final manual part. + SRMaskBytesManual(bytes + manualStartOffset, manualLength, (uint8_t *) &maskVector); +} diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRURLUtilities.h b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRURLUtilities.h new file mode 100755 index 0000000..a445380 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRURLUtilities.h @@ -0,0 +1,26 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +// The origin isn't really applicable for a native application. +// So instead, just map ws -> http and wss -> https. +extern NSString *SRURLOrigin(NSURL *url); + +extern BOOL SRURLRequiresSSL(NSURL *url); + +// Extracts `user` and `password` from url (if available) into `Basic base64(user:password)`. +extern NSString *_Nullable SRBasicAuthorizationHeaderFromURL(NSURL *url); + +// Returns a valid value for `NSStreamNetworkServiceType` or `nil`. +extern NSString *_Nullable SRStreamNetworkServiceTypeFromURLRequest(NSURLRequest *request); + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRURLUtilities.m b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRURLUtilities.m new file mode 100755 index 0000000..45d618b --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Internal/Utilities/SRURLUtilities.m @@ -0,0 +1,72 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import "SRURLUtilities.h" + +#import "SRHash.h" + +NS_ASSUME_NONNULL_BEGIN + +NSString *SRURLOrigin(NSURL *url) +{ + NSMutableString *origin = [NSMutableString string]; + + NSString *scheme = url.scheme.lowercaseString; + if ([scheme isEqualToString:@"wss"]) { + scheme = @"https"; + } else if ([scheme isEqualToString:@"ws"]) { + scheme = @"http"; + } + [origin appendFormat:@"%@://%@", scheme, url.host]; + + NSNumber *port = url.port; + BOOL portIsDefault = ((port == nil) || + ([scheme isEqualToString:@"http"] && port.integerValue == 80) || + ([scheme isEqualToString:@"https"] && port.integerValue == 443)); + if (!portIsDefault) { + [origin appendFormat:@":%@", port.stringValue]; + } + return origin; +} + +extern BOOL SRURLRequiresSSL(NSURL *url) +{ + NSString *scheme = url.scheme.lowercaseString; + return ([scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]); +} + +extern NSString *_Nullable SRBasicAuthorizationHeaderFromURL(NSURL *url) +{ + NSData *data = [[NSString stringWithFormat:@"%@:%@", url.user, url.password] dataUsingEncoding:NSUTF8StringEncoding]; + return [NSString stringWithFormat:@"Basic %@", SRBase64EncodedStringFromData(data)]; +} + +extern NSString *_Nullable SRStreamNetworkServiceTypeFromURLRequest(NSURLRequest *request) +{ + NSString *networkServiceType = nil; + switch (request.networkServiceType) { + case NSURLNetworkServiceTypeDefault: + break; + case NSURLNetworkServiceTypeVoIP: + networkServiceType = NSStreamNetworkServiceTypeVoIP; + break; + case NSURLNetworkServiceTypeVideo: + networkServiceType = NSStreamNetworkServiceTypeVideo; + break; + case NSURLNetworkServiceTypeBackground: + networkServiceType = NSStreamNetworkServiceTypeBackground; + break; + case NSURLNetworkServiceTypeVoice: + networkServiceType = NSStreamNetworkServiceTypeVoice; + break; + } + return networkServiceType; +} + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/NSRunLoop+SRWebSocket.h b/Sources/Vendor/SocketRocket/SocketRocket/NSRunLoop+SRWebSocket.h new file mode 100755 index 0000000..8f419e3 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/NSRunLoop+SRWebSocket.h @@ -0,0 +1,27 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSRunLoop (SRWebSocket) + +/** + Default run loop that will be used to schedule all instances of `SRWebSocket`. + + @return An instance of `NSRunLoop`. + */ ++ (NSRunLoop *)SR_networkRunLoop; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/NSRunLoop+SRWebSocket.m b/Sources/Vendor/SocketRocket/SocketRocket/NSRunLoop+SRWebSocket.m new file mode 100755 index 0000000..f926f90 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/NSRunLoop+SRWebSocket.m @@ -0,0 +1,27 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import "NSRunLoop+SRWebSocket.h" +#import "NSRunLoop+SRWebSocketPrivate.h" + +#import "SRRunLoopThread.h" + +// Required for object file to always be linked. +void import_NSRunLoop_SRWebSocket() { } + +@implementation NSRunLoop (SRWebSocket) + ++ (NSRunLoop *)SR_networkRunLoop +{ + return [SRRunLoopThread sharedThread].runLoop; +} + +@end diff --git a/Sources/Vendor/SocketRocket/SocketRocket/NSURLRequest+SRWebSocket.h b/Sources/Vendor/SocketRocket/SocketRocket/NSURLRequest+SRWebSocket.h new file mode 100755 index 0000000..1ddf93f --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/NSURLRequest+SRWebSocket.h @@ -0,0 +1,38 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSURLRequest (SRWebSocket) + +/** + An array of pinned `SecCertificateRef` SSL certificates that `SRWebSocket` will use for validation. + */ +@property (nullable, nonatomic, copy, readonly) NSArray *SR_SSLPinnedCertificates + DEPRECATED_MSG_ATTRIBUTE("Using pinned certificates is neither secure nor supported in SocketRocket, " + "and leads to security issues. Please use a proper, trust chain validated certificate."); + +@end + +@interface NSMutableURLRequest (SRWebSocket) + +/** + An array of pinned `SecCertificateRef` SSL certificates that `SRWebSocket` will use for validation. + */ +@property (nullable, nonatomic, copy) NSArray *SR_SSLPinnedCertificates + DEPRECATED_MSG_ATTRIBUTE("Using pinned certificates is neither secure nor supported in SocketRocket, " + "and leads to security issues. Please use a proper, trust chain validated certificate."); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/NSURLRequest+SRWebSocket.m b/Sources/Vendor/SocketRocket/SocketRocket/NSURLRequest+SRWebSocket.m new file mode 100755 index 0000000..d1a8999 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/NSURLRequest+SRWebSocket.m @@ -0,0 +1,42 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import "NSURLRequest+SRWebSocket.h" +#import "NSURLRequest+SRWebSocketPrivate.h" + +// Required for object file to always be linked. +void import_NSURLRequest_SRWebSocket() { } + +NS_ASSUME_NONNULL_BEGIN + +static NSString *const SRSSLPinnnedCertificatesKey = @"SocketRocket_SSLPinnedCertificates"; + +@implementation NSURLRequest (SRWebSocket) + +- (nullable NSArray *)SR_SSLPinnedCertificates +{ + return nil; +} + +@end + +@implementation NSMutableURLRequest (SRWebSocket) + +- (void)setSR_SSLPinnedCertificates:(nullable NSArray *)SR_SSLPinnedCertificates +{ + [NSException raise:NSInvalidArgumentException + format:@"Using pinned certificates is neither secure nor supported in SocketRocket, " + "and leads to security issues. Please use a proper, trust chain validated certificate."]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/Resources/Info.plist b/Sources/Vendor/SocketRocket/SocketRocket/Resources/Info.plist new file mode 100755 index 0000000..d3de8ee --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/Resources/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/Sources/Vendor/SocketRocket/SocketRocket/SRSecurityPolicy.h b/Sources/Vendor/SocketRocket/SocketRocket/SRSecurityPolicy.h new file mode 100755 index 0000000..db6286d --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/SRSecurityPolicy.h @@ -0,0 +1,72 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface SRSecurityPolicy : NSObject + +/** + A default `SRSecurityPolicy` implementation specifies socket security and + validates the certificate chain. + + Use a subclass of `SRSecurityPolicy` for more fine grained customization. + */ ++ (instancetype)defaultPolicy; + +/** + Specifies socket security and provider certificate pinning, disregarding certificate + chain validation. + + @param pinnedCertificates Array of `SecCertificateRef` SSL certificates to use for validation. + */ ++ (instancetype)pinnningPolicyWithCertificates:(NSArray *)pinnedCertificates + DEPRECATED_MSG_ATTRIBUTE("Using pinned certificates is neither secure nor supported in SocketRocket, " + "and leads to security issues. Please use a proper, trust chain validated certificate."); + +/** + Specifies socket security and optional certificate chain validation. + + @param enabled Whether or not to validate the SSL certificate chain. If you + are considering using this method because your certificate was not issued by a + recognized certificate authority, consider using `pinningPolicyWithCertificates` instead. + */ +- (instancetype)initWithCertificateChainValidationEnabled:(BOOL)enabled + DEPRECATED_MSG_ATTRIBUTE("Disabling certificate chain validation is unsafe. " + "Please use a proper Certificate Authority to issue your TLS certificates.") + NS_DESIGNATED_INITIALIZER; + +/** + Updates all the security options for input and output streams, for example you + can set your socket security level here. + + @param stream Stream to update the options in. + */ +- (void)updateSecurityOptionsInStream:(NSStream *)stream; + +/** + Whether or not the specified server trust should be accepted, based on the security policy. + + This method should be used when responding to an authentication challenge from + a server. In the default implemenation, no further validation is done here, but + you're free to override it in a subclass. See `SRPinningSecurityPolicy.h` for + an example. + + @param serverTrust The X.509 certificate trust of the server. + @param domain The domain of serverTrust. + + @return Whether or not to trust the server. + */ +- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/SRSecurityPolicy.m b/Sources/Vendor/SocketRocket/SocketRocket/SRSecurityPolicy.m new file mode 100755 index 0000000..96e11e9 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/SRSecurityPolicy.m @@ -0,0 +1,75 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import "SRSecurityPolicy.h" +#import "SRPinningSecurityPolicy.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface SRSecurityPolicy () + +@property (nonatomic, assign, readonly) BOOL certificateChainValidationEnabled; + +@end + +@implementation SRSecurityPolicy + ++ (instancetype)defaultPolicy +{ + return [self new]; +} + ++ (instancetype)pinnningPolicyWithCertificates:(NSArray *)pinnedCertificates +{ + [NSException raise:NSInvalidArgumentException + format:@"Using pinned certificates is neither secure nor supported in SocketRocket, " + "and leads to security issues. Please use a proper, trust chain validated certificate."]; + + return nil; +} + +- (instancetype)initWithCertificateChainValidationEnabled:(BOOL)enabled +{ + self = [super init]; + if (!self) { return self; } + + _certificateChainValidationEnabled = enabled; + + return self; +} + +- (instancetype)init +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" + + return [self initWithCertificateChainValidationEnabled:YES]; + +#pragma clang diagnostic pop +} + +- (void)updateSecurityOptionsInStream:(NSStream *)stream +{ + // Enforce TLS 1.2 + [stream setProperty:(__bridge id)CFSTR("kCFStreamSocketSecurityLevelTLSv1_2") forKey:(__bridge id)kCFStreamPropertySocketSecurityLevel]; + + // Validate certificate chain for this stream if enabled. + NSDictionary *sslOptions = @{ (__bridge NSString *)kCFStreamSSLValidatesCertificateChain : @(self.certificateChainValidationEnabled) }; + [stream setProperty:sslOptions forKey:(__bridge NSString *)kCFStreamPropertySSLSettings]; +} + +- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain +{ + // No further evaluation happens in the default policy. + return YES; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/SRWebSocket.h b/Sources/Vendor/SocketRocket/SocketRocket/SRWebSocket.h new file mode 100755 index 0000000..df483c0 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/SRWebSocket.h @@ -0,0 +1,417 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSInteger, SRReadyState) { + SR_CONNECTING = 0, + SR_OPEN = 1, + SR_CLOSING = 2, + SR_CLOSED = 3, +}; + +typedef NS_ENUM(NSInteger, SRStatusCode) { + // 0-999: Reserved and not used. + SRStatusCodeNormal = 1000, + SRStatusCodeGoingAway = 1001, + SRStatusCodeProtocolError = 1002, + SRStatusCodeUnhandledType = 1003, + // 1004 reserved. + SRStatusNoStatusReceived = 1005, + SRStatusCodeAbnormal = 1006, + SRStatusCodeInvalidUTF8 = 1007, + SRStatusCodePolicyViolated = 1008, + SRStatusCodeMessageTooBig = 1009, + SRStatusCodeMissingExtension = 1010, + SRStatusCodeInternalError = 1011, + SRStatusCodeServiceRestart = 1012, + SRStatusCodeTryAgainLater = 1013, + // 1014: Reserved for future use by the WebSocket standard. + SRStatusCodeTLSHandshake = 1015, + // 1016-1999: Reserved for future use by the WebSocket standard. + // 2000-2999: Reserved for use by WebSocket extensions. + // 3000-3999: Available for use by libraries and frameworks. May not be used by applications. Available for registration at the IANA via first-come, first-serve. + // 4000-4999: Available for use by applications. +}; + +@class SRWebSocket; +@class SRSecurityPolicy; + +/** + Error domain used for errors reported by SRWebSocket. + */ +extern NSString *const SRWebSocketErrorDomain; + +/** + Key used for HTTP status code if bad response was received from the server. + */ +extern NSString *const SRHTTPResponseErrorKey; + +@protocol SRWebSocketDelegate; + +///-------------------------------------- +#pragma mark - SRWebSocket +///-------------------------------------- + +/** + A `SRWebSocket` object lets you connect, send and receive data to a remote Web Socket. + */ +@interface SRWebSocket : NSObject + +/** + The delegate of the web socket. + + The web socket delegate is notified on all state changes that happen to the web socket. + */ +@property (nonatomic, weak) id delegate; + +/** + A dispatch queue for scheduling the delegate calls. The queue doesn't need be a serial queue. + + If `nil` and `delegateOperationQueue` is `nil`, the socket uses main queue for performing all delegate method calls. + */ +@property (nullable, nonatomic, strong) dispatch_queue_t delegateDispatchQueue; + +/** + An operation queue for scheduling the delegate calls. + + If `nil` and `delegateOperationQueue` is `nil`, the socket uses main queue for performing all delegate method calls. + */ +@property (nullable, nonatomic, strong) NSOperationQueue *delegateOperationQueue; + +/** + Current ready state of the socket. Default: `SR_CONNECTING`. + + This property is Key-Value Observable and fully thread-safe. + */ +@property (atomic, assign, readonly) SRReadyState readyState; + +/** + An instance of `NSURL` that this socket connects to. + */ +@property (nullable, nonatomic, strong, readonly) NSURL *url; + +/** + All HTTP headers that were received by socket or `nil` if none were received so far. + */ +@property (nullable, nonatomic, assign, readonly) CFHTTPMessageRef receivedHTTPHeaders; + +/** + Array of `NSHTTPCookie` cookies to apply to the connection. + */ +@property (nullable, nonatomic, copy) NSArray *requestCookies; + +/** + The negotiated web socket protocol or `nil` if handshake did not yet complete. + */ +@property (nullable, nonatomic, copy, readonly) NSString *protocol; + +/** + A boolean value indicating whether this socket will allow connection without SSL trust chain evaluation. + For DEBUG builds this flag is ignored, and SSL connections are allowed regardless of the certificate trust configuration + */ +@property (nonatomic, assign, readonly) BOOL allowsUntrustedSSLCertificates; + +///-------------------------------------- +#pragma mark - Constructors +///-------------------------------------- + +/** + Initializes a web socket with a given `NSURLRequest`. + + @param request Request to initialize with. + */ +- (instancetype)initWithURLRequest:(NSURLRequest *)request; + +/** + Initializes a web socket with a given `NSURLRequest`, specifying a transport security policy (e.g. SSL configuration). + + @param request Request to initialize with. + @param securityPolicy Policy object describing transport security behavior. + */ +- (instancetype)initWithURLRequest:(NSURLRequest *)request securityPolicy:(SRSecurityPolicy *)securityPolicy; + +/** + Initializes a web socket with a given `NSURLRequest` and list of sub-protocols. + + @param request Request to initialize with. + @param protocols An array of strings that turn into `Sec-WebSocket-Protocol`. Default: `nil`. + */ +- (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(nullable NSArray *)protocols; + +/** + Initializes a web socket with a given `NSURLRequest`, list of sub-protocols and whether untrusted SSL certificates are allowed. + + @param request Request to initialize with. + @param protocols An array of strings that turn into `Sec-WebSocket-Protocol`. Default: `nil`. + @param allowsUntrustedSSLCertificates Boolean value indicating whether untrusted SSL certificates are allowed. Default: `false`. + */ +- (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(nullable NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates + DEPRECATED_MSG_ATTRIBUTE("Disabling certificate chain validation is unsafe. " + "Please use a proper Certificate Authority to issue your TLS certificates."); + +/** + Initializes a web socket with a given `NSURLRequest`, list of sub-protocols and whether untrusted SSL certificates are allowed. + + @param request Request to initialize with. + @param protocols An array of strings that turn into `Sec-WebSocket-Protocol`. Default: `nil`. + @param securityPolicy Policy object describing transport security behavior. + */ +- (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(nullable NSArray *)protocols securityPolicy:(SRSecurityPolicy *)securityPolicy NS_DESIGNATED_INITIALIZER; + +/** + Initializes a web socket with a given `NSURL`. + + @param url URL to initialize with. + */ +- (instancetype)initWithURL:(NSURL *)url; + +/** + Initializes a web socket with a given `NSURL` and list of sub-protocols. + + @param url URL to initialize with. + @param protocols An array of strings that turn into `Sec-WebSocket-Protocol`. Default: `nil`. + */ +- (instancetype)initWithURL:(NSURL *)url protocols:(nullable NSArray *)protocols; + +/** + Initializes a web socket with a given `NSURL`, specifying a transport security policy (e.g. SSL configuration). + + @param url URL to initialize with. + @param securityPolicy Policy object describing transport security behavior. + */ +- (instancetype)initWithURL:(NSURL *)url securityPolicy:(SRSecurityPolicy *)securityPolicy; + +/** + Initializes a web socket with a given `NSURL`, list of sub-protocols and whether untrusted SSL certificates are allowed. + + @param url URL to initialize with. + @param protocols An array of strings that turn into `Sec-WebSocket-Protocol`. Default: `nil`. + @param allowsUntrustedSSLCertificates Boolean value indicating whether untrusted SSL certificates are allowed. Default: `false`. + */ +- (instancetype)initWithURL:(NSURL *)url protocols:(nullable NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates + DEPRECATED_MSG_ATTRIBUTE("Disabling certificate chain validation is unsafe. " + "Please use a proper Certificate Authority to issue your TLS certificates."); + +/** + Unavailable initializer. Please use any other initializer. + */ +- (instancetype)init NS_UNAVAILABLE; + +/** + Unavailable constructor. Please use any other initializer. + */ ++ (instancetype)new NS_UNAVAILABLE; + +///-------------------------------------- +#pragma mark - Schedule +///-------------------------------------- + +/** + Schedules a received on a given run loop in a given mode. + By default, a web socket will schedule itself on `+[NSRunLoop SR_networkRunLoop]` using `NSDefaultRunLoopMode`. + + @param runLoop The run loop on which to schedule the receiver. + @param mode The mode for the run loop. + */ +- (void)scheduleInRunLoop:(NSRunLoop *)runLoop forMode:(NSString *)mode NS_SWIFT_NAME(schedule(in:forMode:)); + +/** + Removes the receiver from a given run loop running in a given mode. + + @param runLoop The run loop on which the receiver was scheduled. + @param mode The mode for the run loop. + */ +- (void)unscheduleFromRunLoop:(NSRunLoop *)runLoop forMode:(NSString *)mode NS_SWIFT_NAME(unschedule(from:forMode:)); + +///-------------------------------------- +#pragma mark - Open / Close +///-------------------------------------- + +/** + Opens web socket, which will trigger connection, authentication and start receiving/sending events. + An instance of `SRWebSocket` is intended for one-time-use only. This method should be called once and only once. + */ +- (void)open; + +/** + Closes a web socket using `SRStatusCodeNormal` code and no reason. + */ +- (void)close; + +/** + Closes a web socket using a given code and reason. + + @param code Code to close the socket with. + @param reason Reason to send to the server or `nil`. + */ +- (void)closeWithCode:(NSInteger)code reason:(nullable NSString *)reason; + +///-------------------------------------- +#pragma mark Send +///-------------------------------------- + +/** + Send a UTF-8 string or binary data to the server. + + @param message UTF-8 String or Data to send. + + @deprecated Please use `sendString:` or `sendData` instead. + */ +- (void)send:(nullable id)message __attribute__((deprecated("Please use `sendString:error:` or `sendData:error:` instead."))); + +/** + Send a UTF-8 String to the server. + + @param string String to send. + @param error On input, a pointer to variable for an `NSError` object. + If an error occurs, this pointer is set to an `NSError` object containing information about the error. + You may specify `nil` to ignore the error information. + + @return `YES` if the string was scheduled to send, otherwise - `NO`. + */ +- (BOOL)sendString:(NSString *)string error:(NSError **)error NS_SWIFT_NAME(send(string:)); + +/** + Send binary data to the server. + + @param data Data to send. + @param error On input, a pointer to variable for an `NSError` object. + If an error occurs, this pointer is set to an `NSError` object containing information about the error. + You may specify `nil` to ignore the error information. + + @return `YES` if the string was scheduled to send, otherwise - `NO`. + */ +- (BOOL)sendData:(nullable NSData *)data error:(NSError **)error NS_SWIFT_NAME(send(data:)); + +/** + Send binary data to the server, without making a defensive copy of it first. + + @param data Data to send. + @param error On input, a pointer to variable for an `NSError` object. + If an error occurs, this pointer is set to an `NSError` object containing information about the error. + You may specify `nil` to ignore the error information. + + @return `YES` if the string was scheduled to send, otherwise - `NO`. + */ +- (BOOL)sendDataNoCopy:(nullable NSData *)data error:(NSError **)error NS_SWIFT_NAME(send(dataNoCopy:)); + +/** + Send Ping message to the server with optional data. + + @param data Instance of `NSData` or `nil`. + @param error On input, a pointer to variable for an `NSError` object. + If an error occurs, this pointer is set to an `NSError` object containing information about the error. + You may specify `nil` to ignore the error information. + + @return `YES` if the string was scheduled to send, otherwise - `NO`. + */ +- (BOOL)sendPing:(nullable NSData *)data error:(NSError **)error NS_SWIFT_NAME(sendPing(_:)); + +@end + +///-------------------------------------- +#pragma mark - SRWebSocketDelegate +///-------------------------------------- + +/** + The `SRWebSocketDelegate` protocol describes the methods that `SRWebSocket` objects + call on their delegates to handle status and messsage events. + */ +@protocol SRWebSocketDelegate + +@optional + +#pragma mark Receive Messages + +/** + Called when any message was received from a web socket. + This method is suboptimal and might be deprecated in a future release. + + @param webSocket An instance of `SRWebSocket` that received a message. + @param message Received message. Either a `String` or `NSData`. + */ +- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message; + +/** + Called when a frame was received from a web socket. + + @param webSocket An instance of `SRWebSocket` that received a message. + @param string Received text in a form of UTF-8 `String`. + */ +- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessageWithString:(NSString *)string; + +/** + Called when a frame was received from a web socket. + + @param webSocket An instance of `SRWebSocket` that received a message. + @param data Received data in a form of `NSData`. + */ +- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessageWithData:(NSData *)data; + +#pragma mark Status & Connection + +/** + Called when a given web socket was open and authenticated. + + @param webSocket An instance of `SRWebSocket` that was open. + */ +- (void)webSocketDidOpen:(SRWebSocket *)webSocket; + +/** + Called when a given web socket encountered an error. + + @param webSocket An instance of `SRWebSocket` that failed with an error. + @param error An instance of `NSError`. + */ +- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error; + +/** + Called when a given web socket was closed. + + @param webSocket An instance of `SRWebSocket` that was closed. + @param code Code reported by the server. + @param reason Reason in a form of a String that was reported by the server or `nil`. + @param wasClean Boolean value indicating whether a socket was closed in a clean state. + */ +- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(nullable NSString *)reason wasClean:(BOOL)wasClean; + +/** + Called on receive of a ping message from the server. + + @param webSocket An instance of `SRWebSocket` that received a ping frame. + @param data Payload that was received or `nil` if there was no payload. + */ +- (void)webSocket:(SRWebSocket *)webSocket didReceivePingWithData:(nullable NSData *)data; + +/** + Called when a pong data was received in response to ping. + + @param webSocket An instance of `SRWebSocket` that received a pong frame. + @param pongData Payload that was received or `nil` if there was no payload. + */ +- (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(nullable NSData *)pongData; + +/** + Sent before reporting a text frame to be able to configure if it shuold be convert to a UTF-8 String or passed as `NSData`. + If the method is not implemented - it will always convert text frames to String. + + @param webSocket An instance of `SRWebSocket` that received a text frame. + + @return `YES` if text frame should be converted to UTF-8 String, otherwise - `NO`. Default: `YES`. + */ +- (BOOL)webSocketShouldConvertTextFrameToString:(SRWebSocket *)webSocket NS_SWIFT_NAME(webSocketShouldConvertTextFrameToString(_:)); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/SocketRocket/SRWebSocket.m b/Sources/Vendor/SocketRocket/SocketRocket/SRWebSocket.m new file mode 100755 index 0000000..c0e96f7 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/SRWebSocket.m @@ -0,0 +1,1628 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import "SRWebSocket.h" + +#if TARGET_OS_IPHONE +#define HAS_ICU +#endif + +#ifdef HAS_ICU +#import +#endif + +#import + +#import "SRDelegateController.h" +#import "SRIOConsumer.h" +#import "SRIOConsumerPool.h" +#import "SRHash.h" +#import "SRURLUtilities.h" +#import "SRError.h" +#import "NSURLRequest+SRWebSocket.h" +#import "NSRunLoop+SRWebSocket.h" +#import "SRProxyConnect.h" +#import "SRSecurityPolicy.h" +#import "SRHTTPConnectMessage.h" +#import "SRRandom.h" +#import "SRLog.h" +#import "SRMutex.h" +#import "SRSIMDHelpers.h" +#import "NSURLRequest+SRWebSocketPrivate.h" +#import "NSRunLoop+SRWebSocketPrivate.h" +#import "SRConstants.h" + +#if !__has_feature(objc_arc) +#error SocketRocket must be compiled with ARC enabled +#endif + +__attribute__((used)) static void importCategories() +{ + import_NSURLRequest_SRWebSocket(); + import_NSRunLoop_SRWebSocket(); +} + +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 uint8_t const SRWebSocketProtocolVersion = 13; + +NSString *const SRWebSocketErrorDomain = @"SRWebSocketErrorDomain"; +NSString *const SRHTTPResponseErrorKey = @"HTTPResponseStatusCode"; + +@interface SRWebSocket () + +@property (atomic, assign, readwrite) SRReadyState readyState; + +// Specifies whether SSL trust chain should NOT be evaluated. +// By default this flag is set to NO, meaning only secure SSL connections are allowed. +// For DEBUG builds this flag is ignored, and SSL connections are allowed regardless +// of the certificate trust configuration +@property (nonatomic, assign, readwrite) BOOL allowsUntrustedSSLCertificates; + +@property (nonatomic, strong, readonly) SRDelegateController *delegateController; + +@end + +@implementation SRWebSocket { + SRMutex _kvoLock; + OSSpinLock _propertyLock; + + dispatch_queue_t _workQueue; + NSMutableArray *_consumers; + + NSInputStream *_inputStream; + NSOutputStream *_outputStream; + + dispatch_data_t _readBuffer; + NSUInteger _readBufferOffset; + + dispatch_data_t _outputBuffer; + NSUInteger _outputBufferOffset; + + uint8_t _currentFrameOpcode; + size_t _currentFrameCount; + size_t _readOpCount; + uint32_t _currentStringScanPosition; + NSMutableData *_currentFrameData; + + NSString *_closeReason; + + NSString *_secKey; + + SRSecurityPolicy *_securityPolicy; + BOOL _requestRequiresSSL; + BOOL _streamSecurityValidated; + + uint8_t _currentReadMaskKey[4]; + size_t _currentReadMaskOffset; + + BOOL _closeWhenFinishedWriting; + BOOL _failed; + + NSURLRequest *_urlRequest; + + BOOL _sentClose; + BOOL _didFail; + BOOL _cleanupScheduled; + int _closeCode; + + BOOL _isPumping; + + NSMutableSet *_scheduledRunloops; // Set<[RunLoop, Mode]>. TODO: (nlutsenko) Fix clowntown + + // We use this to retain ourselves. + __strong SRWebSocket *_selfRetain; + + NSArray *_requestedProtocols; + SRIOConsumerPool *_consumerPool; + + // proxy support + SRProxyConnect *_proxyConnect; +} + +@synthesize readyState = _readyState; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols securityPolicy:(SRSecurityPolicy *)securityPolicy +{ + self = [super init]; + if (!self) return self; + + assert(request.URL); + _url = request.URL; + _urlRequest = request; + _requestedProtocols = [protocols copy]; + _securityPolicy = securityPolicy; + _requestRequiresSSL = SRURLRequiresSSL(_url); + + _readyState = SR_CONNECTING; + + _propertyLock = OS_SPINLOCK_INIT; + _kvoLock = SRMutexInitRecursive(); + _workQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); + + // Going to set a specific on the queue so we can validate we're on the work queue + dispatch_queue_set_specific(_workQueue, (__bridge void *)self, (__bridge void *)(_workQueue), NULL); + + _delegateController = [[SRDelegateController alloc] init]; + + _readBuffer = dispatch_data_empty; + _outputBuffer = dispatch_data_empty; + + _currentFrameData = [[NSMutableData alloc] init]; + + _consumers = [[NSMutableArray alloc] init]; + + _consumerPool = [[SRIOConsumerPool alloc] init]; + + _scheduledRunloops = [[NSMutableSet alloc] init]; + + return self; +} + +- (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates +{ + SRSecurityPolicy *securityPolicy; + BOOL certificateChainValidationEnabled = !allowsUntrustedSSLCertificates; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" + + securityPolicy = [[SRSecurityPolicy alloc] initWithCertificateChainValidationEnabled:certificateChainValidationEnabled]; + +#pragma clang diagnostic pop + + return [self initWithURLRequest:request protocols:protocols securityPolicy:securityPolicy]; +} + +- (instancetype)initWithURLRequest:(NSURLRequest *)request securityPolicy:(SRSecurityPolicy *)securityPolicy +{ + return [self initWithURLRequest:request protocols:nil securityPolicy:securityPolicy]; +} + +- (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" + + return [self initWithURLRequest:request protocols:protocols allowsUntrustedSSLCertificates:NO]; + +#pragma clang diagnostic pop +} + +- (instancetype)initWithURLRequest:(NSURLRequest *)request +{ + return [self initWithURLRequest:request protocols:nil]; +} + +- (instancetype)initWithURL:(NSURL *)url; +{ + return [self initWithURL:url protocols:nil]; +} + +- (instancetype)initWithURL:(NSURL *)url protocols:(NSArray *)protocols; +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" + + return [self initWithURL:url protocols:protocols allowsUntrustedSSLCertificates:NO]; + +#pragma clang diagnostic pop +} + +- (instancetype)initWithURL:(NSURL *)url securityPolicy:(SRSecurityPolicy *)securityPolicy +{ + NSURLRequest *request = [NSURLRequest requestWithURL:url]; + return [self initWithURLRequest:request protocols:nil securityPolicy:securityPolicy]; +} + +- (instancetype)initWithURL:(NSURL *)url protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates +{ + NSURLRequest *request = [NSURLRequest requestWithURL:url]; + return [self initWithURLRequest:request protocols:protocols allowsUntrustedSSLCertificates:allowsUntrustedSSLCertificates]; +} + +- (void)assertOnWorkQueue; +{ + assert(dispatch_get_specific((__bridge void *)self) == (__bridge void *)_workQueue); +} + +///-------------------------------------- +#pragma mark - Dealloc +///-------------------------------------- + +- (void)dealloc +{ + _inputStream.delegate = nil; + _outputStream.delegate = nil; + + [_inputStream close]; + [_outputStream close]; + + if (_receivedHTTPHeaders) { + CFRelease(_receivedHTTPHeaders); + _receivedHTTPHeaders = NULL; + } + + SRMutexDestroy(_kvoLock); +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +#pragma mark readyState + +- (void)setReadyState:(SRReadyState)readyState +{ + @try { + SRMutexLock(_kvoLock); + if (_readyState != readyState) { + [self willChangeValueForKey:@"readyState"]; + OSSpinLockLock(&_propertyLock); + _readyState = readyState; + OSSpinLockUnlock(&_propertyLock); + [self didChangeValueForKey:@"readyState"]; + } + } + @finally { + SRMutexUnlock(_kvoLock); + } +} + +- (SRReadyState)readyState +{ + SRReadyState state = 0; + OSSpinLockLock(&_propertyLock); + state = _readyState; + OSSpinLockUnlock(&_propertyLock); + return state; +} + ++ (BOOL)automaticallyNotifiesObserversOfReadyState { + return NO; +} + +///-------------------------------------- +#pragma mark - Open / Close +///-------------------------------------- + +- (void)open +{ + assert(_url); + NSAssert(self.readyState == SR_CONNECTING, @"Cannot call -(void)open on SRWebSocket more than once."); + + _selfRetain = self; + + if (_urlRequest.timeoutInterval > 0) { + dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_urlRequest.timeoutInterval * NSEC_PER_SEC)); + dispatch_after(popTime, dispatch_get_main_queue(), ^{ + if (self.readyState == SR_CONNECTING) { + NSError *error = SRErrorWithDomainCodeDescription(NSURLErrorDomain, NSURLErrorTimedOut, @"Timed out connecting to server."); + [self _failWithError:error]; + } + }); + } + + _proxyConnect = [[SRProxyConnect alloc] initWithURL:_url]; + + __weak typeof(self) wself = self; + [_proxyConnect openNetworkStreamWithCompletion:^(NSError *error, NSInputStream *readStream, NSOutputStream *writeStream) { + [wself _connectionDoneWithError:error readStream:readStream writeStream:writeStream]; + }]; +} + +- (void)_connectionDoneWithError:(NSError *)error readStream:(NSInputStream *)readStream writeStream:(NSOutputStream *)writeStream +{ + if (error != nil) { + [self _failWithError:error]; + } else { + _outputStream = writeStream; + _inputStream = readStream; + + _inputStream.delegate = self; + _outputStream.delegate = self; + [self _updateSecureStreamOptions]; + + if (!_scheduledRunloops.count) { + [self scheduleInRunLoop:[NSRunLoop SR_networkRunLoop] forMode:NSDefaultRunLoopMode]; + } + + // If we don't require SSL validation - consider that we connected. + // Otherwise `didConnect` is called when SSL validation finishes. + if (!_requestRequiresSSL) { + dispatch_async(_workQueue, ^{ + [self didConnect]; + }); + } + } + // Schedule to run on a work queue, to make sure we don't run this inline and deallocate `self` inside `SRProxyConnect`. + // TODO: (nlutsenko) Find a better structure for this, maybe Bolts Tasks? + dispatch_async(_workQueue, ^{ + _proxyConnect = nil; + }); +} + +- (BOOL)_checkHandshake:(CFHTTPMessageRef)httpMessage; +{ + NSString *acceptHeader = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(httpMessage, CFSTR("Sec-WebSocket-Accept"))); + + if (acceptHeader == nil) { + return NO; + } + + NSString *concattedString = [_secKey stringByAppendingString:SRWebSocketAppendToSecKeyString]; + NSData *hashedString = SRSHA1HashFromString(concattedString); + NSString *expectedAccept = SRBase64EncodedStringFromData(hashedString); + return [acceptHeader isEqualToString:expectedAccept]; +} + +- (void)_HTTPHeadersDidFinish; +{ + NSInteger responseCode = CFHTTPMessageGetResponseStatusCode(_receivedHTTPHeaders); + if (responseCode >= 400) { + SRDebugLog(@"Request failed with response code %d", responseCode); + NSError *error = SRHTTPErrorWithCodeDescription(responseCode, 2132, + [NSString stringWithFormat:@"Received bad response code from server: %d.", + (int)responseCode]); + [self _failWithError:error]; + return; + } + + if(![self _checkHandshake:_receivedHTTPHeaders]) { + NSError *error = SRErrorWithCodeDescription(2133, @"Invalid Sec-WebSocket-Accept response."); + [self _failWithError:error]; + return; + } + + NSString *negotiatedProtocol = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(_receivedHTTPHeaders, CFSTR("Sec-WebSocket-Protocol"))); + if (negotiatedProtocol) { + // Make sure we requested the protocol + if ([_requestedProtocols indexOfObject:negotiatedProtocol] == NSNotFound) { + NSError *error = SRErrorWithCodeDescription(2133, @"Server specified Sec-WebSocket-Protocol that wasn't requested."); + [self _failWithError:error]; + return; + } + + _protocol = negotiatedProtocol; + } + + self.readyState = SR_OPEN; + + if (!_didFail) { + [self _readFrameNew]; + } + + [self.delegateController performDelegateBlock:^(id _Nullable delegate, SRDelegateAvailableMethods availableMethods) { + if (availableMethods.didOpen) { + [delegate webSocketDidOpen:self]; + } + }]; +} + + +- (void)_readHTTPHeader; +{ + if (_receivedHTTPHeaders == NULL) { + _receivedHTTPHeaders = CFHTTPMessageCreateEmpty(NULL, NO); + } + + [self _readUntilHeaderCompleteWithCallback:^(SRWebSocket *socket, NSData *data) { + CFHTTPMessageAppendBytes(_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length); + + if (CFHTTPMessageIsHeaderComplete(_receivedHTTPHeaders)) { + SRDebugLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_receivedHTTPHeaders))); + [self _HTTPHeadersDidFinish]; + } else { + [self _readHTTPHeader]; + } + }]; +} + +- (void)didConnect; +{ + SRDebugLog(@"Connected"); + + _secKey = SRBase64EncodedStringFromData(SRRandomData(16)); + assert([_secKey length] == 24); + + CFHTTPMessageRef message = SRHTTPConnectMessageCreate(_urlRequest, + _secKey, + SRWebSocketProtocolVersion, + self.requestCookies, + _requestedProtocols); + + NSData *messageData = CFBridgingRelease(CFHTTPMessageCopySerializedMessage(message)); + + CFRelease(message); + + [self _writeData:messageData]; + [self _readHTTPHeader]; +} + +- (void)_updateSecureStreamOptions +{ + if (_requestRequiresSSL) { + SRDebugLog(@"Setting up security for streams."); + [_securityPolicy updateSecurityOptionsInStream:_inputStream]; + [_securityPolicy updateSecurityOptionsInStream:_outputStream]; + } + + NSString *networkServiceType = SRStreamNetworkServiceTypeFromURLRequest(_urlRequest); + if (networkServiceType != nil) { + [_inputStream setProperty:networkServiceType forKey:NSStreamNetworkServiceType]; + [_outputStream setProperty:networkServiceType forKey:NSStreamNetworkServiceType]; + } +} + +- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; +{ + [_outputStream scheduleInRunLoop:aRunLoop forMode:mode]; + [_inputStream scheduleInRunLoop:aRunLoop forMode:mode]; + + [_scheduledRunloops addObject:@[aRunLoop, mode]]; +} + +- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; +{ + [_outputStream removeFromRunLoop:aRunLoop forMode:mode]; + [_inputStream removeFromRunLoop:aRunLoop forMode:mode]; + + [_scheduledRunloops removeObject:@[aRunLoop, mode]]; +} + +- (void)close; +{ + [self closeWithCode:SRStatusCodeNormal reason:nil]; +} + +- (void)closeWithCode:(NSInteger)code reason:(NSString *)reason; +{ + assert(code); + dispatch_async(_workQueue, ^{ + if (self.readyState == SR_CLOSING || self.readyState == SR_CLOSED) { + return; + } + + BOOL wasConnecting = self.readyState == SR_CONNECTING; + + self.readyState = SR_CLOSING; + + SRDebugLog(@"Closing with code %d reason %@", code, reason); + + if (wasConnecting) { + [self closeConnection]; + return; + } + + size_t maxMsgSize = [reason maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + NSMutableData *mutablePayload = [[NSMutableData alloc] initWithLength:sizeof(uint16_t) + maxMsgSize]; + NSData *payload = mutablePayload; + + ((uint16_t *)mutablePayload.mutableBytes)[0] = CFSwapInt16BigToHost((uint16_t)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]; +#pragma unused (success) + + 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 + [self.delegateController performDelegateQueueBlock:^{ + [self closeWithCode:SRStatusCodeProtocolError reason:message]; + dispatch_async(_workQueue, ^{ + [self closeConnection]; + }); + }]; +} + +- (void)_failWithError:(NSError *)error; +{ + dispatch_async(_workQueue, ^{ + if (self.readyState != SR_CLOSED) { + _failed = YES; + [self.delegateController performDelegateBlock:^(id _Nullable delegate, SRDelegateAvailableMethods availableMethods) { + if (availableMethods.didFailWithError) { + [delegate webSocket:self didFailWithError:error]; + } + }]; + + self.readyState = SR_CLOSED; + + SRDebugLog(@"Failing with error %@", error.localizedDescription); + + [self closeConnection]; + [self _scheduleCleanup]; + } + }); +} + +- (void)_writeData:(NSData *)data; +{ + [self assertOnWorkQueue]; + + if (_closeWhenFinishedWriting) { + return; + } + + __block NSData *strongData = data; + dispatch_data_t newData = dispatch_data_create(data.bytes, data.length, nil, ^{ + strongData = nil; + }); + _outputBuffer = dispatch_data_create_concat(_outputBuffer, newData); + [self _pumpWriting]; +} + +- (void)send:(nullable id)message +{ + if (!message) { + [self sendData:nil error:nil]; // Send Data, but it doesn't matter since we are going to send the same text frame with 0 length. + } else if ([message isKindOfClass:[NSString class]]) { + [self sendString:message error:nil]; + } else if ([message isKindOfClass:[NSData class]]) { + [self sendData:message error:nil]; + } else { + NSAssert(NO, @"Unrecognized message. Not able to send anything other than a String or NSData."); + } +} + +- (BOOL)sendString:(NSString *)string error:(NSError **)error +{ + if (self.readyState != SR_OPEN) { + NSString *message = @"Invalid State: Cannot call `sendString:error:` until connection is open."; + if (error) { + *error = SRErrorWithCodeDescription(2134, message); + } + SRDebugLog(message); + return NO; + } + + string = [string copy]; + dispatch_async(_workQueue, ^{ + [self _sendFrameWithOpcode:SROpCodeTextFrame data:[string dataUsingEncoding:NSUTF8StringEncoding]]; + }); + return YES; +} + +- (BOOL)sendData:(nullable NSData *)data error:(NSError **)error +{ + data = [data copy]; + return [self sendDataNoCopy:data error:error]; +} + +- (BOOL)sendDataNoCopy:(nullable NSData *)data error:(NSError **)error +{ + if (self.readyState != SR_OPEN) { + NSString *message = @"Invalid State: Cannot call `sendDataNoCopy:error:` until connection is open."; + if (error) { + *error = SRErrorWithCodeDescription(2134, message); + } + SRDebugLog(message); + return NO; + } + + dispatch_async(_workQueue, ^{ + if (data) { + [self _sendFrameWithOpcode:SROpCodeBinaryFrame data:data]; + } else { + [self _sendFrameWithOpcode:SROpCodeTextFrame data:nil]; + } + }); + return YES; +} + +- (BOOL)sendPing:(nullable NSData *)data error:(NSError **)error +{ + if (self.readyState != SR_OPEN) { + NSString *message = @"Invalid State: Cannot call `sendPing:error:` until connection is open."; + if (error) { + *error = SRErrorWithCodeDescription(2134, message); + } + SRDebugLog(message); + return NO; + } + + data = [data copy] ?: [NSData data]; // It's okay for a ping to be empty + dispatch_async(_workQueue, ^{ + [self _sendFrameWithOpcode:SROpCodePing data:data]; + }); + return YES; +} + +- (void)_handlePingWithData:(nullable NSData *)data +{ + // Need to pingpong this off _callbackQueue first to make sure messages happen in order + [self.delegateController performDelegateBlock:^(id _Nullable delegate, SRDelegateAvailableMethods availableMethods) { + if (availableMethods.didReceivePing) { + [delegate webSocket:self didReceivePingWithData:data]; + } + dispatch_async(_workQueue, ^{ + [self _sendFrameWithOpcode:SROpCodePong data:data]; + }); + }]; +} + +- (void)handlePong:(NSData *)pongData; +{ + SRDebugLog(@"Received pong"); + [self.delegateController performDelegateBlock:^(id _Nullable delegate, SRDelegateAvailableMethods availableMethods) { + if (availableMethods.didReceivePong) { + [delegate webSocket:self didReceivePong:pongData]; + } + }]; +} + + +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; + + SRDebugLog(@"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 = CFSwapInt16BigToHost(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; + } + + [self assertOnWorkQueue]; + + if (self.readyState == SR_OPEN) { + [self closeWithCode:1000 reason:nil]; + } + dispatch_async(_workQueue, ^{ + [self closeConnection]; + }); +} + +- (void)closeConnection; +{ + [self assertOnWorkQueue]; + SRDebugLog(@"Trying to disconnect"); + _closeWhenFinishedWriting = YES; + [self _pumpWriting]; +} + +- (void)_handleFrameWithData:(NSData *)frameData opCode:(SROpCode)opcode +{ + // Check that the current data is valid UTF8 + + BOOL isControlFrame = (opcode == SROpCodePing || opcode == SROpCodePong || opcode == SROpCodeConnectionClose); + if (isControlFrame) { + //frameData will be copied before passing to handlers + //otherwise there can be misbehaviours when value at the pointer is changed + frameData = [frameData copy]; + + dispatch_async(_workQueue, ^{ + [self _readFrameContinue]; + }); + } else { + [self _readFrameNew]; + } + + switch (opcode) { + case SROpCodeTextFrame: { + NSString *string = [[NSString alloc] initWithData:frameData encoding:NSUTF8StringEncoding]; + if (!string && frameData) { + [self closeWithCode:SRStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8."]; + dispatch_async(_workQueue, ^{ + [self closeConnection]; + }); + return; + } + SRDebugLog(@"Received text message."); + [self.delegateController performDelegateBlock:^(id _Nullable delegate, SRDelegateAvailableMethods availableMethods) { + // Don't convert into string - iff `delegate` tells us not to. Otherwise - create UTF8 string and handle that. + if (availableMethods.shouldConvertTextFrameToString && ![delegate webSocketShouldConvertTextFrameToString:self]) { + if (availableMethods.didReceiveMessage) { + [delegate webSocket:self didReceiveMessage:frameData]; + } + if (availableMethods.didReceiveMessageWithData) { + [delegate webSocket:self didReceiveMessageWithData:frameData]; + } + } else { + if (availableMethods.didReceiveMessage) { + [delegate webSocket:self didReceiveMessage:string]; + } + if (availableMethods.didReceiveMessageWithString) { + [delegate webSocket:self didReceiveMessageWithString:string]; + } + } + }]; + break; + } + case SROpCodeBinaryFrame: { + SRDebugLog(@"Received data message."); + [self.delegateController performDelegateBlock:^(id _Nullable delegate, SRDelegateAvailableMethods availableMethods) { + if (availableMethods.didReceiveMessage) { + [delegate webSocket:self didReceiveMessage:frameData]; + } + if (availableMethods.didReceiveMessageWithData) { + [delegate webSocket:self didReceiveMessageWithData:frameData]; + } + }]; + } + break; + case SROpCodeConnectionClose: + [self handleCloseWithData:frameData]; + break; + case SROpCodePing: + [self _handlePingWithData:frameData]; + break; + case SROpCodePong: + [self handlePong:frameData]; + break; + default: + [self _closeWithProtocolError:[NSString stringWithFormat:@"Unknown opcode %ld", (long)opcode]]; + // TODO: Handle invalid opcode + break; + } +} + +- (void)_handleFrameHeader:(frame_header)frame_header curData:(NSData *)curData; +{ + assert(frame_header.opcode != 0); + + if (self.readyState == SR_CLOSED) { + 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 { + assert(frame_header.payload_length <= SIZE_T_MAX); + [self _addConsumerWithDataLength:(size_t)frame_header.payload_length callback:^(SRWebSocket *sself, NSData *newData) { + if (isControlFrame) { + [sself _handleFrameWithData:newData opCode:frame_header.opcode]; + } else { + if (frame_header.fin) { + [sself _handleFrameWithData:sself->_currentFrameData opCode:frame_header.opcode]; + } else { + // TODO add assert that opcode is not a control; + [sself _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 *sself, NSData *data) { + __block frame_header header = {0}; + + const uint8_t *headerBuffer = data.bytes; + assert(data.length >= 2); + + if (headerBuffer[0] & SRRsvMask) { + [sself _closeWithProtocolError:@"Server used RSV bits"]; + return; + } + + uint8_t receivedOpcode = (SROpCodeMask & headerBuffer[0]); + + BOOL isControlFrame = (receivedOpcode == SROpCodePing || receivedOpcode == SROpCodePong || receivedOpcode == SROpCodeConnectionClose); + + if (!isControlFrame && receivedOpcode != 0 && sself->_currentFrameCount > 0) { + [sself _closeWithProtocolError:@"all data frames after the initial data frame must have opcode 0"]; + return; + } + + if (receivedOpcode == 0 && sself->_currentFrameCount == 0) { + [sself _closeWithProtocolError:@"cannot continue a message"]; + return; + } + + header.opcode = receivedOpcode == 0 ? sself->_currentFrameOpcode : receivedOpcode; + + header.fin = !!(SRFinMask & headerBuffer[0]); + + + header.masked = !!(SRMaskMask & headerBuffer[1]); + header.payload_length = SRPayloadLenMask & headerBuffer[1]; + + headerBuffer = NULL; + + if (header.masked) { + [sself _closeWithProtocolError:@"Client must receive unmasked data"]; + return; + } + + 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) { + [sself _handleFrameHeader:header curData:sself->_currentFrameData]; + } else { + [sself _addConsumerWithDataLength:extra_bytes_needed callback:^(SRWebSocket *eself, NSData *edata) { + size_t mapped_size = edata.length; +#pragma unused (mapped_size) + const void *mapped_buffer = edata.bytes; + size_t offset = 0; + + if (header.payload_length == 126) { + assert(mapped_size >= sizeof(uint16_t)); + uint16_t payloadLength = 0; + memcpy(&payloadLength, mapped_buffer, sizeof(uint16_t)); + payloadLength = CFSwapInt16BigToHost(payloadLength); + + header.payload_length = payloadLength; + offset += sizeof(uint16_t); + } else if (header.payload_length == 127) { + assert(mapped_size >= sizeof(uint64_t)); + uint64_t payloadLength = 0; + memcpy(&payloadLength, mapped_buffer, sizeof(uint64_t)); + payloadLength = CFSwapInt64BigToHost(payloadLength); + + header.payload_length = payloadLength; + offset += sizeof(uint64_t); + } else { + assert(header.payload_length < 126 && header.payload_length >= 0); + } + + if (header.masked) { + assert(mapped_size >= sizeof(_currentReadMaskOffset) + offset); + memcpy(eself->_currentReadMaskKey, ((uint8_t *)mapped_buffer) + offset, sizeof(eself->_currentReadMaskKey)); + } + + [eself _handleFrameHeader:header curData:eself->_currentFrameData]; + } readToCurrentFrame:NO unmaskBytes:NO]; + } + } readToCurrentFrame:NO unmaskBytes:NO]; +} + +- (void)_readFrameNew; +{ + dispatch_async(_workQueue, ^{ + // Don't reset the length, since Apple doesn't guarantee that this will free the memory (and in tests on + // some platforms, it doesn't seem to, effectively causing a leak the size of the biggest frame so far). + _currentFrameData = [[NSMutableData alloc] init]; + + _currentFrameOpcode = 0; + _currentFrameCount = 0; + _readOpCount = 0; + _currentStringScanPosition = 0; + + [self _readFrameContinue]; + }); +} + +- (void)_pumpWriting; +{ + [self assertOnWorkQueue]; + + NSUInteger dataLength = dispatch_data_get_size(_outputBuffer); + if (dataLength - _outputBufferOffset > 0 && _outputStream.hasSpaceAvailable) { + __block NSInteger bytesWritten = 0; + __block BOOL streamFailed = NO; + + dispatch_data_t dataToSend = dispatch_data_create_subrange(_outputBuffer, _outputBufferOffset, dataLength - _outputBufferOffset); + dispatch_data_apply(dataToSend, ^bool(dispatch_data_t region, size_t offset, const void *buffer, size_t size) { + NSInteger sentLength = [_outputStream write:buffer maxLength:size]; + if (sentLength == -1) { + streamFailed = YES; + return false; + } + bytesWritten += sentLength; + return (sentLength >= (NSInteger)size); // If we can't write all the data into the stream - bail-out early. + }); + if (streamFailed) { + NSInteger code = 2145; + NSString *description = @"Error writing to stream."; + NSError *streamError = _outputStream.streamError; + NSError *error = streamError ? SRErrorWithCodeDescriptionUnderlyingError(code, description, streamError) : SRErrorWithCodeDescription(code, description); + [self _failWithError:error]; + return; + } + + _outputBufferOffset += bytesWritten; + + if (_outputBufferOffset > SRDefaultBufferSize() && _outputBufferOffset > dataLength / 2) { + _outputBuffer = dispatch_data_create_subrange(_outputBuffer, _outputBufferOffset, dataLength - _outputBufferOffset); + _outputBufferOffset = 0; + } + } + + if (_closeWhenFinishedWriting && + (dispatch_data_get_size(_outputBuffer) - _outputBufferOffset) == 0 && + (_inputStream.streamStatus != NSStreamStatusNotOpen && + _inputStream.streamStatus != NSStreamStatusClosed) && + !_sentClose) { + _sentClose = YES; + + @synchronized(self) { + [_outputStream close]; + [_inputStream close]; + + + for (NSArray *runLoop in [_scheduledRunloops copy]) { + [self unscheduleFromRunLoop:[runLoop objectAtIndex:0] forMode:[runLoop objectAtIndex:1]]; + } + } + + if (!_failed) { + [self.delegateController performDelegateBlock:^(id _Nullable delegate, SRDelegateAvailableMethods availableMethods) { + if (availableMethods.didCloseWithCode) { + [delegate webSocket:self didCloseWithCode:_closeCode reason:_closeReason wasClean:YES]; + } + }]; + } + + [self _scheduleCleanup]; + } +} + +- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback; +{ + [self assertOnWorkQueue]; + [self _addConsumerWithScanner:consumer callback:callback dataLength:0]; +} + +- (void)_addConsumerWithDataLength:(size_t)dataLength callback:(data_callback)callback readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; +{ + [self assertOnWorkQueue]; + assert(dataLength); + + [_consumers addObject:[_consumerPool consumerWithScanner:nil handler:callback bytesNeeded:dataLength readToCurrentFrame:readToCurrentFrame unmaskBytes:unmaskBytes]]; + [self _pumpScanner]; +} + +- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback dataLength:(size_t)dataLength; +{ + [self assertOnWorkQueue]; + [_consumers addObject:[_consumerPool consumerWithScanner:consumer handler:callback bytesNeeded:dataLength readToCurrentFrame:NO unmaskBytes:NO]]; + [self _pumpScanner]; +} + + +- (void)_scheduleCleanup +{ + @synchronized(self) { + if (_cleanupScheduled) { + return; + } + + _cleanupScheduled = YES; + + // Cleanup NSStream delegate's in the same RunLoop used by the streams themselves: + // This way we'll prevent race conditions between handleEvent and SRWebsocket's dealloc + NSTimer *timer = [NSTimer timerWithTimeInterval:(0.0f) target:self selector:@selector(_cleanupSelfReference:) userInfo:nil repeats:NO]; + [[NSRunLoop SR_networkRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; + } +} + +- (void)_cleanupSelfReference:(NSTimer *)timer +{ + @synchronized(self) { + // Nuke NSStream delegate's + _inputStream.delegate = nil; + _outputStream.delegate = nil; + + // Remove the streams, right now, from the networkRunLoop + [_inputStream close]; + [_outputStream close]; + } + + // Cleanup selfRetain in the same GCD queue as usual + dispatch_async(_workQueue, ^{ + _selfRetain = nil; + }); +} + + +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 (size_t 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_CLOSED) { + return didWork; + } + + size_t readBufferSize = dispatch_data_get_size(_readBuffer); + + if (!_consumers.count) { + return didWork; + } + + size_t curSize = readBufferSize - _readBufferOffset; + if (!curSize) { + return didWork; + } + + SRIOConsumer *consumer = [_consumers objectAtIndex:0]; + + size_t bytesNeeded = consumer.bytesNeeded; + + size_t foundSize = 0; + if (consumer.consumer) { + NSData *subdata = (NSData *)dispatch_data_create_subrange(_readBuffer, _readBufferOffset, readBufferSize - _readBufferOffset); + foundSize = consumer.consumer(subdata); + } else { + assert(consumer.bytesNeeded); + if (curSize >= bytesNeeded) { + foundSize = bytesNeeded; + } else if (consumer.readToCurrentFrame) { + foundSize = curSize; + } + } + + if (consumer.readToCurrentFrame || foundSize) { + dispatch_data_t slice = dispatch_data_create_subrange(_readBuffer, _readBufferOffset, foundSize); + + _readBufferOffset += foundSize; + + if (_readBufferOffset > SRDefaultBufferSize() && _readBufferOffset > readBufferSize / 2) { + _readBuffer = dispatch_data_create_subrange(_readBuffer, _readBufferOffset, readBufferSize - _readBufferOffset); + _readBufferOffset = 0; + } + + if (consumer.unmaskBytes) { + __block NSMutableData *mutableSlice = [slice mutableCopy]; + + NSUInteger len = mutableSlice.length; + uint8_t *bytes = mutableSlice.mutableBytes; + + for (NSUInteger i = 0; i < len; i++) { + bytes[i] = bytes[i] ^ _currentReadMaskKey[_currentReadMaskOffset % sizeof(_currentReadMaskKey)]; + _currentReadMaskOffset += 1; + } + + slice = dispatch_data_create(bytes, len, nil, ^{ + mutableSlice = nil; + }); + } + + if (consumer.readToCurrentFrame) { + dispatch_data_apply(slice, ^bool(dispatch_data_t region, size_t offset, const void *buffer, size_t size) { + [_currentFrameData appendBytes:buffer length:size]; + return true; + }); + + _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 closeConnection]; + }); + return didWork; + } else { + _currentStringScanPosition += valid_utf8_size; + } + } + + } + + consumer.bytesNeeded -= foundSize; + + if (consumer.bytesNeeded == 0) { + [_consumers removeObjectAtIndex:0]; + consumer.handler(self, nil); + [_consumerPool returnConsumer:consumer]; + didWork = YES; + } + } else if (foundSize) { + [_consumers removeObjectAtIndex:0]; + consumer.handler(self, (NSData *)slice); + [_consumerPool returnConsumer:consumer]; + didWork = YES; + } + } + return didWork; +} + +-(void)_pumpScanner; +{ + [self assertOnWorkQueue]; + + if (!_isPumping) { + _isPumping = YES; + } else { + return; + } + + while ([self _innerPumpScanner]) { + + } + + _isPumping = NO; +} + +//#define NOMASK + +static const size_t SRFrameHeaderOverhead = 32; + +- (void)_sendFrameWithOpcode:(SROpCode)opCode data:(NSData *)data +{ + [self assertOnWorkQueue]; + + if (!data) { + return; + } + + size_t payloadLength = data.length; + + NSMutableData *frameData = [[NSMutableData alloc] initWithLength:payloadLength + SRFrameHeaderOverhead]; + if (!frameData) { + [self closeWithCode:SRStatusCodeMessageTooBig reason:@"Message too big"]; + return; + } + uint8_t *frameBuffer = (uint8_t *)frameData.mutableBytes; + + // set fin + frameBuffer[0] = SRFinMask | opCode; + + // set the mask and header + frameBuffer[1] |= SRMaskMask; + + size_t frameBufferSize = 2; + + if (payloadLength < 126) { + frameBuffer[1] |= payloadLength; + } else { + uint64_t declaredPayloadLength = 0; + size_t declaredPayloadLengthSize = 0; + + if (payloadLength <= UINT16_MAX) { + frameBuffer[1] |= 126; + + declaredPayloadLength = CFSwapInt16BigToHost((uint16_t)payloadLength); + declaredPayloadLengthSize = sizeof(uint16_t); + } else { + frameBuffer[1] |= 127; + + declaredPayloadLength = CFSwapInt64BigToHost((uint64_t)payloadLength); + declaredPayloadLengthSize = sizeof(uint64_t); + } + + memcpy((frameBuffer + frameBufferSize), &declaredPayloadLength, declaredPayloadLengthSize); + frameBufferSize += declaredPayloadLengthSize; + } + + const uint8_t *unmaskedPayloadBuffer = (uint8_t *)data.bytes; + uint8_t *maskKey = frameBuffer + frameBufferSize; + + size_t randomBytesSize = sizeof(uint32_t); + int result = SecRandomCopyBytes(kSecRandomDefault, randomBytesSize, maskKey); + if (result != 0) { + //TODO: (nlutsenko) Check if there was an error. + } + frameBufferSize += randomBytesSize; + + // Copy and unmask the buffer + uint8_t *frameBufferPayloadPointer = frameBuffer + frameBufferSize; + + memcpy(frameBufferPayloadPointer, unmaskedPayloadBuffer, payloadLength); + SRMaskBytesSIMD(frameBufferPayloadPointer, payloadLength, maskKey); + frameBufferSize += payloadLength; + + assert(frameBufferSize <= frameData.length); + frameData.length = frameBufferSize; + + [self _writeData:frameData]; +} + +- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode +{ + __weak typeof(self) wself = self; + + if (_requestRequiresSSL && !_streamSecurityValidated && + (eventCode == NSStreamEventHasBytesAvailable || eventCode == NSStreamEventHasSpaceAvailable)) { + SecTrustRef trust = (__bridge SecTrustRef)[aStream propertyForKey:(__bridge id)kCFStreamPropertySSLPeerTrust]; + if (trust) { + _streamSecurityValidated = [_securityPolicy evaluateServerTrust:trust forDomain:_urlRequest.URL.host]; + } + if (!_streamSecurityValidated) { + dispatch_async(_workQueue, ^{ + NSError *error = SRErrorWithDomainCodeDescription(NSURLErrorDomain, + NSURLErrorClientCertificateRejected, + @"Invalid server certificate."); + [wself _failWithError:error]; + }); + return; + } + dispatch_async(_workQueue, ^{ + [self didConnect]; + }); + } + dispatch_async(_workQueue, ^{ + [wself safeHandleEvent:eventCode stream:aStream]; + }); +} + +- (void)safeHandleEvent:(NSStreamEvent)eventCode stream:(NSStream *)aStream +{ + switch (eventCode) { + case NSStreamEventOpenCompleted: { + SRDebugLog(@"NSStreamEventOpenCompleted %@", aStream); + if (self.readyState >= SR_CLOSING) { + return; + } + assert(_readBuffer); + + if (!_requestRequiresSSL && self.readyState == SR_CONNECTING && aStream == _inputStream) { + [self didConnect]; + } + + [self _pumpWriting]; + [self _pumpScanner]; + + break; + } + + case NSStreamEventErrorOccurred: { + SRDebugLog(@"NSStreamEventErrorOccurred %@ %@", aStream, [[aStream streamError] copy]); + /// TODO specify error better! + [self _failWithError:aStream.streamError]; + _readBufferOffset = 0; + _readBuffer = dispatch_data_empty; + break; + + } + + case NSStreamEventEndEncountered: { + [self _pumpScanner]; + SRDebugLog(@"NSStreamEventEndEncountered %@", aStream); + if (aStream.streamError) { + [self _failWithError:aStream.streamError]; + } else { + dispatch_async(_workQueue, ^{ + if (self.readyState != SR_CLOSED) { + self.readyState = SR_CLOSED; + [self _scheduleCleanup]; + } + + 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 + [self.delegateController performDelegateBlock:^(id _Nullable delegate, SRDelegateAvailableMethods availableMethods) { + if (availableMethods.didCloseWithCode) { + [delegate webSocket:self + didCloseWithCode:SRStatusCodeGoingAway + reason:@"Stream end encountered" + wasClean:NO]; + } + }]; + } + }); + } + + break; + } + + case NSStreamEventHasBytesAvailable: { + SRDebugLog(@"NSStreamEventHasBytesAvailable %@", aStream); + uint8_t buffer[SRDefaultBufferSize()]; + + while (_inputStream.hasBytesAvailable) { + NSInteger bytesRead = [_inputStream read:buffer maxLength:SRDefaultBufferSize()]; + if (bytesRead > 0) { + dispatch_data_t data = dispatch_data_create(buffer, bytesRead, nil, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + if (!data) { + NSError *error = SRErrorWithCodeDescription(SRStatusCodeMessageTooBig, + @"Unable to allocate memory to read from socket."); + [self _failWithError:error]; + return; + } + _readBuffer = dispatch_data_create_concat(_readBuffer, data); + } else if (bytesRead == -1) { + [self _failWithError:_inputStream.streamError]; + } + } + [self _pumpScanner]; + break; + } + + case NSStreamEventHasSpaceAvailable: { + SRDebugLog(@"NSStreamEventHasSpaceAvailable %@", aStream); + [self _pumpWriting]; + break; + } + + case NSStreamEventNone: + SRDebugLog(@"(default) %@", aStream); + break; + } +} + +///-------------------------------------- +#pragma mark - Delegate +///-------------------------------------- + +- (id _Nullable)delegate +{ + return self.delegateController.delegate; +} + +- (void)setDelegate:(id _Nullable)delegate +{ + self.delegateController.delegate = delegate; +} + +- (void)setDelegateDispatchQueue:(dispatch_queue_t _Nullable)queue +{ + self.delegateController.dispatchQueue = queue; +} + +- (dispatch_queue_t _Nullable)delegateDispatchQueue +{ + return self.delegateController.dispatchQueue; +} + +- (void)setDelegateOperationQueue:(NSOperationQueue *_Nullable)queue +{ + self.delegateController.operationQueue = queue; +} + +- (NSOperationQueue *_Nullable)delegateOperationQueue +{ + return self.delegateController.operationQueue; +} + +@end + +#ifdef HAS_ICU + +static inline int32_t validate_dispatch_data_partial_string(NSData *data) { + if ([data length] > INT32_MAX) { + // INT32_MAX is the limit so long as this Framework is using 32 bit ints everywhere. + return -1; + } + + int32_t size = (int32_t)[data length]; + + const void * contents = [data bytes]; + 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 (int32_t)data.length - i; + } + } + + return -1; +} + +#endif diff --git a/Sources/Vendor/SocketRocket/SocketRocket/SocketRocket.h b/Sources/Vendor/SocketRocket/SocketRocket/SocketRocket.h new file mode 100755 index 0000000..c7ab062 --- /dev/null +++ b/Sources/Vendor/SocketRocket/SocketRocket/SocketRocket.h @@ -0,0 +1,15 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import +#import +#import +#import diff --git a/Sources/Vendor/SocketRocket/TestChat/TCAppDelegate.h b/Sources/Vendor/SocketRocket/TestChat/TCAppDelegate.h new file mode 100755 index 0000000..040664d --- /dev/null +++ b/Sources/Vendor/SocketRocket/TestChat/TCAppDelegate.h @@ -0,0 +1,16 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the license found in the +// LICENSE-examples file in the root directory of this source tree. +// + +#import + +@interface TCAppDelegate : UIResponder + +@property (nonatomic, strong) UIWindow *window; + +@end diff --git a/Sources/Vendor/SocketRocket/TestChat/TCAppDelegate.m b/Sources/Vendor/SocketRocket/TestChat/TCAppDelegate.m new file mode 100755 index 0000000..7170a1e --- /dev/null +++ b/Sources/Vendor/SocketRocket/TestChat/TCAppDelegate.m @@ -0,0 +1,19 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the license found in the +// LICENSE-examples file in the root directory of this source tree. +// + +#import "TCAppDelegate.h" + +@implementation TCAppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + return YES; +} + +@end diff --git a/Sources/Vendor/SocketRocket/TestChat/TCChatCell.h b/Sources/Vendor/SocketRocket/TestChat/TCChatCell.h new file mode 100755 index 0000000..7aa5a59 --- /dev/null +++ b/Sources/Vendor/SocketRocket/TestChat/TCChatCell.h @@ -0,0 +1,17 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the license found in the +// LICENSE-examples file in the root directory of this source tree. +// + +#import + +@interface TCChatCell : UITableViewCell + +@property (nonatomic, retain) IBOutlet UITextView *textView; +@property (nonatomic, retain) IBOutlet UILabel *nameLabel; + +@end diff --git a/Sources/Vendor/SocketRocket/TestChat/TCChatCell.m b/Sources/Vendor/SocketRocket/TestChat/TCChatCell.m new file mode 100755 index 0000000..29437d4 --- /dev/null +++ b/Sources/Vendor/SocketRocket/TestChat/TCChatCell.m @@ -0,0 +1,40 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the license found in the +// LICENSE-examples file in the root directory of this source tree. +// + +#import "TCChatCell.h" + +@implementation TCChatCell + +@synthesize nameLabel = _nameLabel; +@synthesize textView = _textView; + +- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier +{ + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; + if (self) { + // Initialization code + } + return self; +} + +- (CGSize)sizeThatFits:(CGSize)size; +{ + CGSize textViewSize = _textView.bounds.size; + CGSize fitTextViewSize = CGSizeMake(textViewSize.width, size.height); + CGSize sizeThatFitsSize = [self.textView sizeThatFits:fitTextViewSize]; + + CGSize superSize = [super sizeThatFits:size]; + + sizeThatFitsSize.height = MAX(superSize.height, sizeThatFitsSize.height); + sizeThatFitsSize.width = superSize.width; + + return sizeThatFitsSize; +} + +@end diff --git a/Sources/Vendor/SocketRocket/TestChat/TCViewController.h b/Sources/Vendor/SocketRocket/TestChat/TCViewController.h new file mode 100755 index 0000000..f94cdf3 --- /dev/null +++ b/Sources/Vendor/SocketRocket/TestChat/TCViewController.h @@ -0,0 +1,19 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the license found in the +// LICENSE-examples file in the root directory of this source tree. +// + +#import + +@interface TCViewController : UITableViewController + +@property (nonatomic, strong) IBOutlet UITextView *inputView; + +- (IBAction)reconnect:(id)sender; +- (IBAction)sendPing:(id)sender; + +@end diff --git a/Sources/Vendor/SocketRocket/TestChat/TCViewController.m b/Sources/Vendor/SocketRocket/TestChat/TCViewController.m new file mode 100755 index 0000000..c68854e --- /dev/null +++ b/Sources/Vendor/SocketRocket/TestChat/TCViewController.m @@ -0,0 +1,195 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the license found in the +// LICENSE-examples file in the root directory of this source tree. +// + +#import "TCViewController.h" + +#import + +#import "TCChatCell.h" + +@interface TCMessage : NSObject + +- (instancetype)initWithMessage:(NSString *)message incoming:(BOOL)incoming; + +@property (nonatomic, copy, readonly) NSString *message; +@property (nonatomic, assign, readonly, getter=isIncoming) BOOL incoming; + +@end + +@implementation TCMessage + +- (instancetype)initWithMessage:(NSString *)message incoming:(BOOL)incoming +{ + self = [super init]; + if (!self) return self; + + _incoming = incoming; + _message = message; + + return self; +} + +@end + + +@interface TCViewController () +{ + SRWebSocket *_webSocket; + NSMutableArray *_messages; +} + +@end + +@implementation TCViewController + +///-------------------------------------- +#pragma mark - View +///-------------------------------------- + +- (void)viewDidLoad; +{ + [super viewDidLoad]; + + _messages = [[NSMutableArray alloc] init]; +} + +- (void)viewWillAppear:(BOOL)animated +{ + [super viewWillAppear:animated]; + + [self reconnect:nil]; +} + +- (void)viewDidAppear:(BOOL)animated +{ + [super viewDidAppear:animated]; + + [_inputView becomeFirstResponder]; +} + +- (void)viewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear:animated]; + + [_webSocket close]; + _webSocket = nil; +} + +///-------------------------------------- +#pragma mark - Actions +///-------------------------------------- + +- (IBAction)reconnect:(id)sender +{ + _webSocket.delegate = nil; + [_webSocket close]; + + _webSocket = [[SRWebSocket alloc] initWithURL:[NSURL URLWithString:@"wss://echo.websocket.org"]]; + _webSocket.delegate = self; + + self.title = @"Opening Connection..."; + [_webSocket open]; +} + +- (void)sendPing:(id)sender; +{ + [_webSocket sendPing:nil error:NULL]; +} + +///-------------------------------------- +#pragma mark - Messages +///-------------------------------------- + +- (void)_addMessage:(TCMessage *)message +{ + [_messages addObject:message]; + [self.tableView insertRowsAtIndexPaths:@[ [NSIndexPath indexPathForRow:_messages.count - 1 inSection:0] ] + withRowAnimation:UITableViewRowAnimationNone]; + [self.tableView scrollRectToVisible:self.tableView.tableFooterView.frame animated:YES]; +} + +///-------------------------------------- +#pragma mark - UITableViewController +///-------------------------------------- + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + return _messages.count; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + TCMessage *message = _messages[indexPath.row]; + + TCChatCell *cell = [self.tableView dequeueReusableCellWithIdentifier:message.incoming ? @"ReceivedCell" : @"SentCell" + forIndexPath:indexPath]; + + cell.textView.text = message.message; + cell.nameLabel.text = message.incoming ? @"Other" : @"Me"; + + return cell; +} + +///-------------------------------------- +#pragma mark - SRWebSocketDelegate +///-------------------------------------- + +- (void)webSocketDidOpen:(SRWebSocket *)webSocket; +{ + NSLog(@"Websocket Connected"); + self.title = @"Connected!"; +} + +- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error; +{ + NSLog(@":( Websocket Failed With Error %@", error); + + self.title = @"Connection Failed! (see logs)"; + _webSocket = nil; +} + +- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessageWithString:(nonnull NSString *)string +{ + NSLog(@"Received \"%@\"", string); + [self _addMessage:[[TCMessage alloc] initWithMessage:string incoming:YES]]; +} + +- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean; +{ + NSLog(@"WebSocket closed"); + self.title = @"Connection Closed! (see logs)"; + _webSocket = nil; +} + +- (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload; +{ + NSLog(@"WebSocket received pong"); +} + +///-------------------------------------- +#pragma mark - UITextViewDelegate +///-------------------------------------- + +- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text +{ + if ([text rangeOfString:@"\n"].location != NSNotFound) { + NSString *message = [textView.text stringByReplacingCharactersInRange:range withString:text]; + message = [message stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + + [_webSocket sendString:message error:NULL]; + + [self _addMessage:[[TCMessage alloc] initWithMessage:message incoming:NO]]; + + textView.text = nil; + return NO; + } + return YES; +} + +@end diff --git a/Sources/Vendor/SocketRocket/TestChat/TestChat-Info.plist b/Sources/Vendor/SocketRocket/TestChat/TestChat-Info.plist new file mode 100755 index 0000000..41b47ba --- /dev/null +++ b/Sources/Vendor/SocketRocket/TestChat/TestChat-Info.plist @@ -0,0 +1,42 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFiles + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + UIMainStoryboardFile + MainStoryboard + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Sources/Vendor/SocketRocket/TestChat/en.lproj/Default-568h@2x.png b/Sources/Vendor/SocketRocket/TestChat/en.lproj/Default-568h@2x.png new file mode 100755 index 0000000000000000000000000000000000000000..0891b7aabfcf3422423b109c8beed2bab838c607 GIT binary patch literal 18594 zcmeI4X;f257Jx&9fS`ixvS;&$x8J@slQFSel)6zJN=?13FB7H(lQjRkSy8x_-S~tvu2gzn1oS+dLcF#eqtq$ z%tf9TTvX?`)R@}3uBI;jzS-=ZR-Td&MHaS&;!0?Ni*#$#`n*~CcQK)Q9vAQ~TUpnI!j)a2biYK^R)M~A5wUDZhx?ULMX z3x1P&qt=trOY6P2U67L=m=U?F|5#Uj(eCueNTZaHs_ceWiHeET+j+tp3Jt9g(ekqP z2WOvfR{qV+9r+o4J5?qK>7;;^+I7tGv-i)es$X_D=EoKF+S?zsyj^oRFElP}c}JT< zd8SUs-?O?}2YD#ngKbnHgzHBcboxK_2r9l(?eNCl-pEzkJm}fY?WC*jnS?VBE4EpY zO$fEejz6fU;W2Kl>JeQBZBl-%Irg`obSlg*@4QB;Dd1H7^Oi5wvt4d{RZ!8Og?^aE z)k0$1g+V3fd(gdQ3d&q2q-FL*uy#}|bc^=VhFsl0jBgUGJ+-s3U8MK9A!YJJMxpci z5hJ%|{DwV48fZn0{n5l$N_KcSb#NKE4plB`9I6Zt=Z!~-zw0{9tg$L&Ju1F0X)Cy8 zKF;(&lJ>x)Jw(=;p~sF(Sd9VWGwFE2rnyS9!f^DZ8+aCLq zQ};>lcJ1GDLqjm6Hd>|Eabno@P`~Bn(~6^aD_#yoEH(a?Nm1S<;S+hSxI5d16^<1lEM3NPFi zkqPrpL)+ zgnseFikg`gJVBha1&7C4;O6>h=dt~`ND+;Zd?W(4v2JIb7Pt>Td42%M-Ju-XAH#Pns762L}K3 zDhvsRqN0Ni(1UrishD2YvV?4*h2iFj$+&N||Fn$4n|^NSU+o?~jq`0jVQt8T9l{7b zXiwwODFh2V!Q6sqP9S>WH$oOf$N~=d0-bqTlD61!=`&0eAP-F>XN?*|gtOXX{ zQVTWyYo4ZK0GAw!GHf|pz9`D;-bbb*5LBX*{bnz|+)$@&P9|ORM2o?95{;ejvo&r- zq8cBhTN6nn)7~W>54U)%-F_-b?YKdfk5I8MHcuzBD5)!;yv#Z&R&^y=@=>VTIMy#r zX&U<=BsPkdqcMe<_}2+>H%XKyrr5ZR8_KVe>ZqYN z^=^~TFD};;rHJ$U;{~w^hYojl4hRI@SH$^K{YEo=sg)WY87r!*7blQK&qnpDo0`Vn zkl)9u9g=mCh&ZCJS(L4yN3k0kQ zuvg$h2KEEk51T+O0JQ+r0`R>g{jvqM0Mr6d3qUOZwE!?PI7HY@CE|dr sfw?Q;rAv?G4&^^8-z_>&sWXMxvD*gPOU4CBe-*@OtE+wfmVJNyHv)PfH~;_u literal 0 HcmV?d00001 diff --git a/Sources/Vendor/SocketRocket/TestChat/en.lproj/InfoPlist.strings b/Sources/Vendor/SocketRocket/TestChat/en.lproj/InfoPlist.strings new file mode 100755 index 0000000..477b28f --- /dev/null +++ b/Sources/Vendor/SocketRocket/TestChat/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Sources/Vendor/SocketRocket/TestChat/en.lproj/MainStoryboard.storyboard b/Sources/Vendor/SocketRocket/TestChat/en.lproj/MainStoryboard.storyboard new file mode 100755 index 0000000..9c0200b --- /dev/null +++ b/Sources/Vendor/SocketRocket/TestChat/en.lproj/MainStoryboard.storyboard @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sources/Vendor/SocketRocket/TestChat/main.m b/Sources/Vendor/SocketRocket/TestChat/main.m new file mode 100755 index 0000000..5530600 --- /dev/null +++ b/Sources/Vendor/SocketRocket/TestChat/main.m @@ -0,0 +1,19 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the license found in the +// LICENSE-examples file in the root directory of this source tree. +// + +#import + +#import "TCAppDelegate.h" + +int main(int argc, char *argv[]) +{ + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([TCAppDelegate class])); + } +} diff --git a/Sources/Vendor/SocketRocket/TestChatServer/go/README b/Sources/Vendor/SocketRocket/TestChatServer/go/README new file mode 100755 index 0000000..6083b3c --- /dev/null +++ b/Sources/Vendor/SocketRocket/TestChatServer/go/README @@ -0,0 +1,3 @@ +With the latest weekly go: + + go run chatroom.go diff --git a/Sources/Vendor/SocketRocket/TestChatServer/go/chatroom.go b/Sources/Vendor/SocketRocket/TestChatServer/go/chatroom.go new file mode 100755 index 0000000..bc3a9c6 --- /dev/null +++ b/Sources/Vendor/SocketRocket/TestChatServer/go/chatroom.go @@ -0,0 +1,70 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the license found in the +// LICENSE-examples file in the root directory of this source tree. +// + +package main + +import ( + "code.google.com/p/go.net/websocket" + "net/http" +) + +// Msg stores both the message and the connection +type Msg struct { + sender *websocket.Conn + msg string +} + +func run(reg chan *websocket.Conn, unreg chan *websocket.Conn, msg chan Msg) { + conns := make(map[*websocket.Conn]int) + for { + select { + case c := <-reg: + conns[c] = 1 + case c := <-unreg: + delete(conns, c) + case msg := <-msg: + for c := range conns { + if c != msg.sender { + websocket.Message.Send(c, msg.msg) + } + } + } + } +} + +func newChatServer(reg chan *websocket.Conn, unreg chan *websocket.Conn, msg chan Msg) websocket.Handler { + return func(ws *websocket.Conn) { + reg <- ws + for { + var message string + err := websocket.Message.Receive(ws, &message) + if err != nil { + unreg <- ws + break + } + msg <- Msg{ws, message} + } + } +} + +func main() { + reg := make(chan *websocket.Conn) + unreg := make(chan *websocket.Conn) + msg := make(chan Msg) + + http.Handle("/chat", websocket.Handler(newChatServer(reg, unreg, msg))) + http.Handle("/", http.FileServer(http.Dir("../static"))) + + go run(reg, unreg, msg) + + err := http.ListenAndServe(":9000", nil) + if err != nil { + panic("ListenAndServe: " + err.Error()) + } +} diff --git a/Sources/Vendor/SocketRocket/TestChatServer/py/chatroom.py b/Sources/Vendor/SocketRocket/TestChatServer/py/chatroom.py new file mode 100755 index 0000000..b304726 --- /dev/null +++ b/Sources/Vendor/SocketRocket/TestChatServer/py/chatroom.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python +# +# Copyright 2012 Square Inc. +# Portions Copyright (c) 2016-present, Facebook, Inc. +# All rights reserved. +# +# This source code is licensed under the license found in the +# LICENSE-examples file in the root directory of this source tree. +# + +import tornado +import tornado.web +import tornado.websocket +import tornado.options + +import os + +import json +import uuid + +import argparse + +import logging + +logger = logging.getLogger('gateway') + +args = None + +def parse_args(): + global args + static_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'static')) + parser = argparse.ArgumentParser(description='Gateway server') + + parser.add_argument('-v', '--verbose', help='verbose logging', action='store_true') + + parser.add_argument('-s', '--static-path', help='path for static files [default: %(default)s]', default=static_path) + + parser.add_argument('-p', '--listen-port', help='port to listen on [default: %(default)s]', default=9000, type=int, metavar='PORT') + parser.add_argument('-i', '--listen-interface', help='interface to listen on. [default: %(default)s]', default='0.0.0.0', metavar='IFACE') + + args = parser.parse_args() + + +connections = set() + +class ChatHandler(tornado.websocket.WebSocketHandler): + def open(self): + connections.add(self) + return None + + def on_message(self, msg): + for c in connections: + if c is self: + continue + c.write_message(msg) + + def on_close(self): + connections.remove(self) + + +def main(): + global logger + #tornado.options.parse_command_line() + + parse_args() + + if args.verbose: + tornado.options.enable_pretty_logging() + logger = logging.getLogger() + logger.setLevel(logging.INFO) + + + application = tornado.web.Application([ + (r"/chat", ChatHandler), + (r"/(.*)", tornado.web.StaticFileHandler, {"path": args.static_path, "default_filename":'index.html'}), + ], + ) + + + print "Listening on %s:%s" % (args.listen_interface, args.listen_port) + application.listen(args.listen_port, args.listen_interface) + tornado.ioloop.IOLoop.instance().start() + + +if __name__ == "__main__": + main() diff --git a/Sources/Vendor/SocketRocket/TestChatServer/static/.gitignore b/Sources/Vendor/SocketRocket/TestChatServer/static/.gitignore new file mode 100755 index 0000000..91dc206 --- /dev/null +++ b/Sources/Vendor/SocketRocket/TestChatServer/static/.gitignore @@ -0,0 +1 @@ +devtools/ diff --git a/Sources/Vendor/SocketRocket/TestChatServer/static/index.html b/Sources/Vendor/SocketRocket/TestChatServer/static/index.html new file mode 100755 index 0000000..bd0692a --- /dev/null +++ b/Sources/Vendor/SocketRocket/TestChatServer/static/index.html @@ -0,0 +1,38 @@ + + + + + + + +

Chat History

+
    +
+
+ + +
+ +
    +
    + + diff --git a/Sources/Vendor/SocketRocket/TestChatServer/static/proxy.js b/Sources/Vendor/SocketRocket/TestChatServer/static/proxy.js new file mode 100755 index 0000000..fc89382 --- /dev/null +++ b/Sources/Vendor/SocketRocket/TestChatServer/static/proxy.js @@ -0,0 +1,81 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the license found in the +// LICENSE-examples file in the root directory of this source tree. +// + +function SocketClient () { + this.list_elem = document.getElementById('client_list'); + this.info_div = document.getElementById('info_div'); + var self = this; +} + +SocketClient.prototype.connect = function () { + var self = this; + + this.list_elem.innerHTML = ''; + this.info_div.innerHTML = 'status: connecting...'; + this.socket = new WebSocket("ws://" + document.location.host + "/chat"); + + this.socket.onopen = function () {self.onopen.apply(self, arguments);}; + this.socket.onmessage = function () {self.onmessage.apply(self, arguments);}; + this.socket.onclose = function () {self.onclose.apply(self, arguments);}; +}; + +SocketClient.prototype.deviceAdded = function (params) { + var el = document.createElement('li'); + el.innerHTML = '
    ' + params.device_name + '' + ' (' + params.app_id + ', ' + params.device_model + ', ' + params.device_id + ')'; + this.list_elem.appendChild(el); + this.visibleElems[params.connection_id] = el; +}; + +SocketClient.prototype.deviceRemoved = function (params) { + var li = this.visibleElems[params.connection_id]; + li.parentNode.removeChild(li); +}; + +SocketClient.prototype.onopen = function () { + this.info_div.innerHTML = 'status: connected to gateway'; + this.list_elem.innerHTML = ''; + this.visibleElems = {}; + console.log('connection to gateway opened'); +}; + +SocketClient.prototype.onmessage = function (message) { + var el = document.createElement('li'); + el.innerHTML = message.data; + window.document.getElementById('history').appendChild(el); +}; + +SocketClient.prototype.onclose = function () { + var retryInterval = 1000.0; + console.log('connection closed, retrying in ' + (retryInterval/1000.0) + ' seconds'); + var self = this; + window.setTimeout(function () {self.connect();}, retryInterval); +}; + + +window.addEventListener('load', function () { + var form = window.document.getElementById('msg_form'); + var msg_field = window.document.getElementById('msg_field'); + + var socketClient = new SocketClient(); + socketClient.connect() + + form.onsubmit = function () { + msg = msg_field.value; + msg_field.value = ''; + + socketClient.socket.send(msg); + + var el = document.createElement('li'); + el.innerHTML = msg; + window.document.getElementById('history').appendChild(el); + + return false; + }; + } +); diff --git a/Sources/Vendor/SocketRocket/TestSupport/autobahn_fuzzingserver.json b/Sources/Vendor/SocketRocket/TestSupport/autobahn_fuzzingserver.json new file mode 100755 index 0000000..6bfc651 --- /dev/null +++ b/Sources/Vendor/SocketRocket/TestSupport/autobahn_fuzzingserver.json @@ -0,0 +1,7 @@ +{ + "url": "ws://127.0.0.1:9001", + "outdir": "./pages/results", + "cases": ["*"], + "exclude-cases": [], + "exclude-agent-cases": {} +} diff --git a/Sources/Vendor/SocketRocket/TestSupport/run_test_server.sh b/Sources/Vendor/SocketRocket/TestSupport/run_test_server.sh new file mode 100755 index 0000000..bd0265c --- /dev/null +++ b/Sources/Vendor/SocketRocket/TestSupport/run_test_server.sh @@ -0,0 +1,18 @@ +# +# Copyright (c) 2016-present, Facebook, Inc. +# All rights reserved. +# +# This source code is licensed under the license found in the +# LICENSE-examples file in the root directory of this source tree. +# + +PYENV_PATH=$(pwd)/.env + +echo $PYENV_PATH +if [ -d "$PYENV_PATH" ]; then + source $PYENV_PATH/bin/activate + $PYENV_PATH/bin/wstest -m fuzzingserver -s TestSupport/autobahn_fuzzingserver.json +else + echo "Python Virtualenv not set up. Please run './TestSupport/setup_env.sh .env' first." +fi + diff --git a/Sources/Vendor/SocketRocket/TestSupport/setup_env.sh b/Sources/Vendor/SocketRocket/TestSupport/setup_env.sh new file mode 100755 index 0000000..f34f8cd --- /dev/null +++ b/Sources/Vendor/SocketRocket/TestSupport/setup_env.sh @@ -0,0 +1,36 @@ +# +# Copyright (c) 2016-present, Facebook, Inc. +# All rights reserved. +# +# This source code is licensed under the license found in the +# LICENSE-examples file in the root directory of this source tree. +# + +VIRTUALENV_PATH=$1 +VIRTUALENV_VERSION=15.0.1 + +if [ -d "$VIRTUALENV_PATH" ]; then + echo "Virtual Env already installed" +elif [ -z "$VIRTUALENV_PATH" ]; then + echo "Usage: ./setup_env.sh " +else + mkdir $VIRTUALENV_PATH + + pushd $VIRTUALENV_PATH + + curl -L -o virtualenv.tar.gz https://pypi.python.org/packages/source/v/virtualenv/virtualenv-$VIRTUALENV_VERSION.tar.gz + tar xvfz virtualenv.tar.gz + + pushd virtualenv-$VIRTUALENV_VERSION + python setup.py install --user + popd + + popd + + python $VIRTUALENV_PATH/virtualenv-$VIRTUALENV_VERSION/virtualenv.py $VIRTUALENV_PATH + + source $VIRTUALENV_PATH/bin/activate + pip install autobahntestsuite + + echo "Environment succesfully set up in $VIRTUALENV_PATH." +fi diff --git a/Sources/Vendor/SocketRocket/Tests/Operations/SRAutobahnOperation.h b/Sources/Vendor/SocketRocket/Tests/Operations/SRAutobahnOperation.h new file mode 100755 index 0000000..09d956d --- /dev/null +++ b/Sources/Vendor/SocketRocket/Tests/Operations/SRAutobahnOperation.h @@ -0,0 +1,41 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import "SRTWebSocketOperation.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef void(^SRAutobahnSocketTextMessageHandler)(SRWebSocket *socket, NSString * _Nullable message); +typedef void(^SRAutobahnSocketDataMessageHandler)(SRWebSocket *socket, NSData * _Nullable message); + +@interface SRAutobahnOperation : SRTWebSocketOperation + +- (instancetype)initWithServerURL:(NSURL *)url + testCommandPath:(NSString *)path + caseNumber:(nullable NSNumber *)caseNumber + agent:(nullable NSString *)agent + textMessageHandler:(nullable SRAutobahnSocketTextMessageHandler)textMessageHandler + dataMessageHandler:(nullable SRAutobahnSocketDataMessageHandler)dataMessageHandler; + +@end + +extern SRAutobahnOperation *SRAutobahnTestOperation(NSURL *serverURL, NSInteger caseNumber, NSString *agent); + +typedef void(^SRAutobahnTestResultHandler)(NSDictionary *_Nullable result); +extern SRAutobahnOperation *SRAutobahnTestResultOperation(NSURL *serverURL, NSInteger caseNumber, NSString *agent, SRAutobahnTestResultHandler handler); + +typedef void(^SRAutobahnTestCaseInfoHandler)(NSDictionary *_Nullable caseInfo); +extern SRAutobahnOperation *SRAutobahnTestCaseInfoOperation(NSURL *serverURL, NSInteger caseNumber, SRAutobahnTestCaseInfoHandler handler); + +typedef void(^SRAutobahnTestCaseCountHandler)(NSInteger caseCount); +extern SRAutobahnOperation *SRAutobahnTestCaseCountOperation(NSURL *serverURL, NSString *agent, SRAutobahnTestCaseCountHandler handler); + +extern SRAutobahnOperation *SRAutobahnTestUpdateReportsOperation(NSURL *serverURL, NSString *agent); + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/Tests/Operations/SRAutobahnOperation.m b/Sources/Vendor/SocketRocket/Tests/Operations/SRAutobahnOperation.m new file mode 100755 index 0000000..96871f8 --- /dev/null +++ b/Sources/Vendor/SocketRocket/Tests/Operations/SRAutobahnOperation.m @@ -0,0 +1,131 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import "SRAutobahnOperation.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface SRAutobahnOperation () + +@property (nullable, nonatomic, copy, readonly) SRAutobahnSocketTextMessageHandler textMessageHandler; +@property (nullable, nonatomic, copy, readonly) SRAutobahnSocketDataMessageHandler dataMessageHandler; + +@end + +@implementation SRAutobahnOperation + +- (instancetype)initWithServerURL:(NSURL *)url + testCommandPath:(NSString *)path + caseNumber:(nullable NSNumber *)caseNumber + agent:(nullable NSString *)agent + textMessageHandler:(nullable SRAutobahnSocketTextMessageHandler)textMessageHandler + dataMessageHandler:(nullable SRAutobahnSocketDataMessageHandler)dataMessageHandler +{ + NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO]; + components.path = (components.path ? [components.path stringByAppendingPathComponent:path] : path); + + NSMutableArray *queryItems = [NSMutableArray arrayWithCapacity:2]; + if (caseNumber) { + [queryItems addObject:[NSURLQueryItem queryItemWithName:@"case" value:caseNumber.stringValue]]; + } + if (agent) { + [queryItems addObject:[NSURLQueryItem queryItemWithName:@"agent" value:agent]]; + } + components.queryItems = queryItems; + self = [self initWithURL:components.URL]; + if (!self) return self; + + _textMessageHandler = [textMessageHandler copy]; + _dataMessageHandler = [dataMessageHandler copy]; + + return self; +} + +- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessageWithString:(NSString *)string +{ + if (self.textMessageHandler) { + self.textMessageHandler(webSocket, string); + } +} + +- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessageWithData:(NSData *)data +{ + if (self.dataMessageHandler) { + self.dataMessageHandler(webSocket, data); + } +} + +@end + +SRAutobahnOperation *SRAutobahnTestOperation(NSURL *serverURL, NSInteger caseNumber, NSString *agent) +{ + return [[SRAutobahnOperation alloc] initWithServerURL:serverURL + testCommandPath:@"/runCase" + caseNumber:@(caseNumber) + agent:agent + textMessageHandler:^(SRWebSocket * _Nonnull socket, NSString * _Nullable message) { + [socket sendString:message error:nil]; + } + dataMessageHandler:^(SRWebSocket * _Nonnull socket, NSData * _Nullable message) { + [socket sendData:message error:nil]; + }]; +} + +extern SRAutobahnOperation *SRAutobahnTestResultOperation(NSURL *serverURL, NSInteger caseNumber, NSString *agent, SRAutobahnTestResultHandler handler) +{ + return [[SRAutobahnOperation alloc] initWithServerURL:serverURL + testCommandPath:@"/getCaseStatus" + caseNumber:@(caseNumber) + agent:agent + textMessageHandler:^(SRWebSocket * _Nonnull socket, NSString * _Nullable message) { + NSData *messageData = [message dataUsingEncoding:NSUTF8StringEncoding]; + NSDictionary *result = [NSJSONSerialization JSONObjectWithData:messageData options:0 error:NULL]; + handler(result); + } + dataMessageHandler:nil]; +} + +extern SRAutobahnOperation *SRAutobahnTestCaseInfoOperation(NSURL *serverURL, NSInteger caseNumber, SRAutobahnTestCaseInfoHandler handler) +{ + return [[SRAutobahnOperation alloc] initWithServerURL:serverURL + testCommandPath:@"/getCaseInfo" + caseNumber:@(caseNumber) + agent:nil + textMessageHandler:^(SRWebSocket * _Nonnull socket, NSString * _Nullable message) { + NSData *messageData = [message dataUsingEncoding:NSUTF8StringEncoding]; + NSDictionary *result = [NSJSONSerialization JSONObjectWithData:messageData options:0 error:NULL]; + handler(result); + } + dataMessageHandler:nil]; +} + +extern SRAutobahnOperation *SRAutobahnTestCaseCountOperation(NSURL *serverURL, NSString *agent, SRAutobahnTestCaseCountHandler handler) +{ + return [[SRAutobahnOperation alloc] initWithServerURL:serverURL + testCommandPath:@"/getCaseCount" + caseNumber:nil + agent:agent + textMessageHandler:^(SRWebSocket * _Nonnull socket, NSString * _Nullable message) { + NSInteger count = [message integerValue]; + handler(count); + } + dataMessageHandler:nil]; +} + +extern SRAutobahnOperation *SRAutobahnTestUpdateReportsOperation(NSURL *serverURL, NSString *agent) +{ + return [[SRAutobahnOperation alloc] initWithServerURL:serverURL + testCommandPath:@"/updateReports" + caseNumber:nil + agent:agent + textMessageHandler:nil + dataMessageHandler:nil]; +} + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/Tests/Operations/SRTWebSocketOperation.h b/Sources/Vendor/SocketRocket/Tests/Operations/SRTWebSocketOperation.h new file mode 100755 index 0000000..c4d53c2 --- /dev/null +++ b/Sources/Vendor/SocketRocket/Tests/Operations/SRTWebSocketOperation.h @@ -0,0 +1,29 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import + +@interface SRTWebSocketOperation : NSOperation + +@property (nonatomic) BOOL isFinished; +@property (nonatomic) BOOL isExecuting; + +@property (nonatomic, strong, readonly) NSError *error; + +- (instancetype)initWithURL:(NSURL *)URL; + +// We override these methods. Please call super +- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean NS_REQUIRES_SUPER; +- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error NS_REQUIRES_SUPER; + +- (BOOL)waitUntilFinishedWithTimeout:(NSTimeInterval)timeout; + +@end diff --git a/Sources/Vendor/SocketRocket/Tests/Operations/SRTWebSocketOperation.m b/Sources/Vendor/SocketRocket/Tests/Operations/SRTWebSocketOperation.m new file mode 100755 index 0000000..27a2eb3 --- /dev/null +++ b/Sources/Vendor/SocketRocket/Tests/Operations/SRTWebSocketOperation.m @@ -0,0 +1,91 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import "SRTWebSocketOperation.h" + +#import "SRAutobahnUtilities.h" + +@interface SRTWebSocketOperation () + +@end + +@implementation SRTWebSocketOperation { + NSInteger _testNumber; + SRWebSocket *_webSocket; + NSURL *_url; +} + +@synthesize isFinished = _isFinished; +@synthesize isExecuting = _isExecuting; +@synthesize error = _error; + +- (instancetype)initWithURL:(NSURL *)URL; +{ + self = [super init]; + if (self) { + _url = URL; + _isExecuting = NO; + _isFinished = NO; + } + return self; +} + +- (BOOL)isConcurrent; +{ + return YES; +} + +- (void)start; +{ + dispatch_async(dispatch_get_main_queue(), ^{ + _webSocket = [[SRWebSocket alloc] initWithURLRequest:[NSURLRequest requestWithURL:_url]]; + _webSocket.delegate = self; + [_webSocket open]; + }); + self.isExecuting = YES; +} + +- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean; +{ + [self willChangeValueForKey:@"isExecuting"]; + [self willChangeValueForKey:@"isFinished"]; + _isFinished = YES; + _isExecuting = NO; + [self didChangeValueForKey:@"isExecuting"]; + [self didChangeValueForKey:@"isFinished"]; + _webSocket.delegate = nil; + _webSocket = nil; +} + +- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error; +{ + _error = error; + [self willChangeValueForKey:@"isExecuting"]; + [self willChangeValueForKey:@"isFinished"]; + _isFinished = YES; + _isExecuting = NO; + [self didChangeValueForKey:@"isExecuting"]; + [self didChangeValueForKey:@"isFinished"]; + _webSocket.delegate = nil; + _webSocket = nil; +} + +- (BOOL)waitUntilFinishedWithTimeout:(NSTimeInterval)timeout +{ + if (self.isFinished) { + return YES; + } + return SRRunLoopRunUntil(^BOOL{ + return self.isFinished; + }, timeout); +} + +@end diff --git a/Sources/Vendor/SocketRocket/Tests/Resources/Info.plist b/Sources/Vendor/SocketRocket/Tests/Resources/Info.plist new file mode 100755 index 0000000..169b6f7 --- /dev/null +++ b/Sources/Vendor/SocketRocket/Tests/Resources/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/Sources/Vendor/SocketRocket/Tests/Resources/autobahn_configuration.json b/Sources/Vendor/SocketRocket/Tests/Resources/autobahn_configuration.json new file mode 100755 index 0000000..e5a1d9a --- /dev/null +++ b/Sources/Vendor/SocketRocket/Tests/Resources/autobahn_configuration.json @@ -0,0 +1,15 @@ +{ + "UNIMPLEMENTED": [ + "12", + "13" + ], + "NON-STRICT": [ + "6.4.2", + "6.4.4", + ], + "INFORMATIONAL": [ + "7.1.6", + "7.13.1", + "7.13.2" + ] +} \ No newline at end of file diff --git a/Sources/Vendor/SocketRocket/Tests/SRAutobahnTests.m b/Sources/Vendor/SocketRocket/Tests/SRAutobahnTests.m new file mode 100755 index 0000000..e752d25 --- /dev/null +++ b/Sources/Vendor/SocketRocket/Tests/SRAutobahnTests.m @@ -0,0 +1,146 @@ +// +// Copyright 2012 Square Inc. +// Portions Copyright (c) 2016-present, Facebook, Inc. +// +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +@import XCTest; +@import ObjectiveC; + +#import + +#import "SRTWebSocketOperation.h" +#import "SRAutobahnOperation.h" +#import "SRAutobahnUtilities.h" + +@interface SRAutobahnTests : XCTestCase +@end + +@implementation SRAutobahnTests + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +/** + This method is called if Xcode is targeting a specific test or a set of them. + If you change this method - please make sure you test this behavior in Xcode by running all tests, then running 1+ test. + */ ++ (instancetype)testCaseWithSelector:(SEL)selector +{ + NSArray *invocations = [self testInvocations]; + for (NSInvocation *invocation in invocations) { + if (invocation.selector == selector) { + return [super testCaseWithSelector:selector]; + } + } + return nil; +} + +///-------------------------------------- +#pragma mark - Setup +///-------------------------------------- + +/** + This method is called by xctest to figure out all the tests that are available. + All the selector names are also reported back to Xcode and displayed in Test Navigator/Console. + */ ++ (NSArray *)testInvocations +{ + __block NSArray *array = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSMutableArray *invocations = [NSMutableArray array]; + for (NSUInteger i = 1; i <= SRAutobahnTestCaseCount(); i++) { + NSDictionary *caseInfo = SRAutobahnTestCaseInfo(i); + NSString *identifier = caseInfo[@"id"]; + + NSInvocation *invocation = [self invocationWithCaseNumber:i identifier:identifier]; + [invocations addObject:invocation]; + } + + array = [invocations sortedArrayUsingComparator:^NSComparisonResult(NSInvocation *_Nonnull obj1, NSInvocation *_Nonnull obj2) { + return [NSStringFromSelector(obj1.selector) compare:NSStringFromSelector(obj2.selector) options:NSNumericSearch]; + }]; + }); + return array; +} + ++ (NSInvocation *)invocationWithCaseNumber:(NSUInteger)caseNumber identifier:(NSString *)identifier +{ + SEL selector = [self addInstanceMethodForTestCaseNumber:caseNumber identifier:identifier]; + NSMethodSignature *signature = [self instanceMethodSignatureForSelector:selector]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; + invocation.selector = selector; + return invocation; +} + ++ (SEL)addInstanceMethodForTestCaseNumber:(NSInteger)caseNumber identifier:(NSString *)identifier +{ + NSString *selectorName = [NSString stringWithFormat:@"Case #%@", identifier]; + SEL selector = NSSelectorFromString(selectorName); + + IMP implementation = imp_implementationWithBlock(^(SRAutobahnTests *sself) { + [sself performTestWithCaseNumber:caseNumber identifier:identifier]; + }); + NSString *typeString = [NSString stringWithFormat:@"%s%s%s", @encode(id), @encode(id), @encode(SEL)]; + class_addMethod(self, selector, implementation, typeString.UTF8String); + + return selector; +} + +///-------------------------------------- +#pragma mark - Teardown +///-------------------------------------- + ++ (void)tearDown +{ + [self updateReports]; + [super tearDown]; +} + ++ (void)updateReports +{ + SRAutobahnOperation *operation = SRAutobahnTestUpdateReportsOperation(SRAutobahnTestServerURL(), SRAutobahnTestAgentName()); + [operation start]; + + NSAssert([operation waitUntilFinishedWithTimeout:60], @"Timed out on updating reports."); + NSAssert(!operation.error, @"Updating the report should not have errored %@", operation.error); +} + +///-------------------------------------- +#pragma mark - Test +///-------------------------------------- + +- (void)performTestWithCaseNumber:(NSInteger)caseNumber identifier:(NSString *)identifier +{ + NSURL *serverURL = SRAutobahnTestServerURL(); + NSString *agent = SRAutobahnTestAgentName(); + + NSOperationQueue *testQueue = [[NSOperationQueue alloc] init]; + testQueue.maxConcurrentOperationCount = 1; + + SRAutobahnOperation *testOp = SRAutobahnTestOperation(serverURL, caseNumber, agent); + [testQueue addOperation:testOp]; + + __block NSDictionary *resultInfo = nil; + SRAutobahnOperation *resultOp = SRAutobahnTestResultOperation(serverURL, caseNumber, agent, ^(NSDictionary * _Nullable result) { + resultInfo = result; + }); + [resultOp addDependency:testOp]; + [testQueue addOperation:resultOp]; + + + XCTAssertTrue([resultOp waitUntilFinishedWithTimeout:60 * 5], @"Test operation timed out."); + XCTAssertTrue(!testOp.error, @"Test operation should not have failed"); + if (!SRAutobahnIsValidResultBehavior(identifier, resultInfo[@"behavior"])) { + XCTFail(@"Invalid test behavior %@ for %@.", resultInfo[@"behavior"], identifier); + } +} + +@end diff --git a/Sources/Vendor/SocketRocket/Tests/Utilities/SRAutobahnUtilities.h b/Sources/Vendor/SocketRocket/Tests/Utilities/SRAutobahnUtilities.h new file mode 100755 index 0000000..4a5ff2f --- /dev/null +++ b/Sources/Vendor/SocketRocket/Tests/Utilities/SRAutobahnUtilities.h @@ -0,0 +1,41 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +@import Foundation; + +NS_ASSUME_NONNULL_BEGIN + +///-------------------------------------- +#pragma mark - Test Configuration +///-------------------------------------- + +extern NSString *SRAutobahnTestAgentName(void); +extern NSURL *SRAutobahnTestServerURL(void); + +///-------------------------------------- +#pragma mark - Validation +///-------------------------------------- + +extern NSDictionary *SRAutobahnTestConfiguration(void); +extern BOOL SRAutobahnIsValidResultBehavior(NSString *caseIdentifier, NSString *behavior); + +///-------------------------------------- +#pragma mark - Utilities +///-------------------------------------- + +extern BOOL SRRunLoopRunUntil(BOOL (^predicate)(), NSTimeInterval timeout); + +///-------------------------------------- +#pragma mark - Setup +///-------------------------------------- + +extern NSUInteger SRAutobahnTestCaseCount(void); +extern NSDictionary *SRAutobahnTestCaseInfo(NSInteger caseNumber); + +NS_ASSUME_NONNULL_END diff --git a/Sources/Vendor/SocketRocket/Tests/Utilities/SRAutobahnUtilities.m b/Sources/Vendor/SocketRocket/Tests/Utilities/SRAutobahnUtilities.m new file mode 100755 index 0000000..2ddcdf3 --- /dev/null +++ b/Sources/Vendor/SocketRocket/Tests/Utilities/SRAutobahnUtilities.m @@ -0,0 +1,120 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// + +#import "SRAutobahnUtilities.h" + +#import "SRAutobahnOperation.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface SRAutobahnUtilities : NSObject @end +@implementation SRAutobahnUtilities @end + +///-------------------------------------- +#pragma mark - Test Configuration +///-------------------------------------- + +NSString *SRAutobahnTestAgentName(void) +{ + return [NSBundle bundleForClass:[SRAutobahnUtilities class]].bundleIdentifier; +} + +NSURL *SRAutobahnTestServerURL(void) +{ + return [NSURL URLWithString:@"ws://localhost:9001"]; +} + +///-------------------------------------- +#pragma mark - Validation +///-------------------------------------- + +NSDictionary *SRAutobahnTestConfiguration(void) +{ + static NSDictionary *configuration; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSURL *configurationURL = [[NSBundle bundleForClass:[SRAutobahnUtilities class]] URLForResource:@"autobahn_configuration" + withExtension:@"json"]; + NSInputStream *readStream = [NSInputStream inputStreamWithURL:configurationURL]; + [readStream open]; + configuration = [NSJSONSerialization JSONObjectWithStream:readStream options:0 error:nil]; + [readStream close]; + }); + return configuration; +} + +BOOL SRAutobahnIsValidResultBehavior(NSString *caseIdentifier, NSString *behavior) +{ + if ([behavior isEqualToString:@"OK"]) { + return YES; + } + + NSArray *cases = SRAutobahnTestConfiguration()[behavior]; + for (NSString *caseId in cases) { + if ([caseIdentifier hasPrefix:caseId]) { + return YES; + } + } + return NO; +} + +///-------------------------------------- +#pragma mark - Utilities +///-------------------------------------- + +BOOL SRRunLoopRunUntil(BOOL (^predicate)(), NSTimeInterval timeout) +{ + NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeout]; + + NSTimeInterval timeoutTime = [timeoutDate timeIntervalSinceReferenceDate]; + NSTimeInterval currentTime = [NSDate timeIntervalSinceReferenceDate]; + + while (!predicate() && currentTime < timeoutTime) { + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + currentTime = [NSDate timeIntervalSinceReferenceDate]; + } + return (currentTime <= timeoutTime); +} + +///-------------------------------------- +#pragma mark - Setup +///-------------------------------------- + +NSUInteger SRAutobahnTestCaseCount(void) +{ + static NSUInteger count; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + SRAutobahnOperation *operation = SRAutobahnTestCaseCountOperation(SRAutobahnTestServerURL(), + SRAutobahnTestAgentName(), + ^(NSInteger caseCount) { + count = caseCount; + }); + [operation start]; + + NSCAssert([operation waitUntilFinishedWithTimeout:10], @"Timed out fetching test case count."); + NSCAssert(!operation.error, @"CaseGetter should have successfully returned the number of testCases. Instead got error %@", operation.error); + }); + return count; +} + +NSDictionary *SRAutobahnTestCaseInfo(NSInteger caseNumber) +{ + __block NSDictionary *caseInfo = nil; + SRAutobahnOperation *operation = SRAutobahnTestCaseInfoOperation(SRAutobahnTestServerURL(), caseNumber, ^(NSDictionary * _Nullable info) { + caseInfo = info; + }); + [operation start]; + + NSCAssert([operation waitUntilFinishedWithTimeout:10], @"Timed out fetching test case info %ld.", (long)caseNumber); + NSCAssert(!operation.error, @"Updating the report should not have errored"); + return caseInfo; +} + +NS_ASSUME_NONNULL_END diff --git a/Sources/com.elgato.applemail.sdPlugin/AppleMail b/Sources/com.elgato.applemail.sdPlugin/AppleMail new file mode 100755 index 0000000000000000000000000000000000000000..91d4f39a7fc7041e6b2a068812bd1d321e6915eb GIT binary patch literal 150944 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 z z*kegQRvsqpl}(BqK~hW4fk%EvOF~BTJCW)%UuDt!^|h@vzjKIE8z-y~?A!nh3>c7C zI{e06nG>LfeY#k zS7Wf7LlMeH;#_b*ClLDUC;DcP)pT->f_YTn{eievaBGP7tw*zX_Y3i^Bq<|z7=Rdf zB@RTy-Kc7=Sva!h>=_$KohMB**6jORN#{p9sHsx)XL&P2W1E!x_;KQr#wYgju&%kn zEZ{u=Y3XCmeK5;Ly)p9{;-bAGk9Vfm8SvbRh*D^{#N+VGI-a&+Zcu}F{Y zB^m>venW7iWxM%AJC}W{W%na@@HdtAmN)mKrO93_;zmy)Q=|53(di=A*b$?ET#V?+5BR#)OfA!1&0mK6Wh%G5m#OyVjU z%jt6K-QOA!sM#%fR?XW?i7LG&h6oy6;ZH|Lhgdb=@tv%HCh4UL0Sp&tQg^HZU6nT& zFCgB_P`zZUFh8INqWM<32?o2+*G5aM??rhdaSh*MDE>75K>W)|DubW%Bf>@r4=Z z;(ro4w*e>|$TvS=obZ&9N@=T1*&?Y#(yQ0*Aes@n8D4!CeM{NSl`TlstBRyQ1gj(H z)qLS9C#{A^h0$IArh!C;y030R;78&U)ztd6F0Y@<3*1+4_w}Xw+RJ@y;tN()diN9? zhWdGyol=#_L4`F;tCon^VB`I2O#NKakMavPQAvL*U}AANk%nN2{M(7pz$O5aFssoFO@ zOturBFmTFjJZyYmeW(~0B&EI#s~o4sc+0a@8i%~MTdFitd;A}^KMY|s&9!z|Ye6g~ zhPb-6!DT(+y$yLH--*1TizGouts%xxRx15F#Cev zE|DJEi$vaDq{4cM+_SAfod>MVY_dNs)|UO>_vY9rB00=ZAn_QPzf@uzvAp1~n=8c6O{<$m9O8GNsicCsTV4EimF zkx)(g)z+>xv`fU-^;OhcOtFm|#ch#ub14?tj+c_;osvGO_Tn%dOm z+xU;noU7No7g+T!WB7eC!hblTCO{3GI8VQ$lv- zU}9LM&83pF*KNWm6TUwn+PhvxG}_ySjA-xcHQYpd+a~B}?>olX*F970Q9CQzd-^=K zN6Cg)dLw#OdP9qo-iU5VZ&a70H;uScWMza}&7WF}|JQ*Jc5o*fF?Q;`iozsQ?!4lG z_H}HRou1P)4z=|79D#o4>SsTI%TCAo+KZ-m-A>7|Az@H+AexK0 z>|#I!FJ3d-9&Z?HYERrBq_QQRBVki}K2uuj?Zn(W?WrDB_R!@SkmJXfq+OR*(nvC`ta3`zzS%l=bbvCVs7$vdqi{`rR)LY95h zt7@BdVfHm-yS1{gX#X%{MmEFMIIwb?=9Wq)Dc6tpW iWKv83W?E22oAmN4vuX( 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 literal 0 HcmV?d00001 diff --git a/Sources/com.elgato.applemail.sdPlugin/MailBadgeIcon@2x.png b/Sources/com.elgato.applemail.sdPlugin/MailBadgeIcon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b4e28549d4c07a92d3c33607e1ac26b8abc6d3a8 GIT binary patch literal 29264 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 literal 0 HcmV?d00001 diff --git a/Sources/com.elgato.applemail.sdPlugin/MailIcon@2x.png b/Sources/com.elgato.applemail.sdPlugin/MailIcon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..085d260fdb6f2b809ce35042eeb364f631dce714 GIT binary patch literal 36544 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%0A^8*Iyt(q@n?$=eYpz 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 Ruguq($z;oYb@Aele*tbKqkI4W literal 0 HcmV?d00001 diff --git a/Sources/com.elgato.applemail.sdPlugin/de.json b/Sources/com.elgato.applemail.sdPlugin/de.json new file mode 100644 index 0000000..32ee8d8 --- /dev/null +++ b/Sources/com.elgato.applemail.sdPlugin/de.json @@ -0,0 +1,8 @@ +{ + "Description": "Zeigt die Anzahl ungelesener E-Mails von Apple Mail an.", + "Name": "Apple Mail", + "com.elgato.applemail.action": { + "Name": "Apple Mail", + "Tooltip": "Zeigt die Anzahl ungelesener E-Mails von Apple Mail an" + } +} \ No newline at end of file diff --git a/Sources/com.elgato.applemail.sdPlugin/en.json b/Sources/com.elgato.applemail.sdPlugin/en.json new file mode 100644 index 0000000..12227da --- /dev/null +++ b/Sources/com.elgato.applemail.sdPlugin/en.json @@ -0,0 +1,8 @@ +{ + "Description": "Display the number of unread emails from Apple Mail.", + "Name": "Apple Mail", + "com.elgato.applemail.action": { + "Name": "Apple Mail", + "Tooltip": "Display the number of unread emails from Apple Mail" + } +} \ No newline at end of file diff --git a/Sources/com.elgato.applemail.sdPlugin/es.json b/Sources/com.elgato.applemail.sdPlugin/es.json new file mode 100644 index 0000000..4527390 --- /dev/null +++ b/Sources/com.elgato.applemail.sdPlugin/es.json @@ -0,0 +1,8 @@ +{ + "Description": "Muestra el número de correos sin leer de la aplicación Mail de Apple.", + "Name": "Aplicación Mail de Apple", + "com.elgato.applemail.action": { + "Name": "Aplicación Mail de Apple", + "Tooltip": "Muestra el número de correos sin leer de la aplicación Mail de Apple" + } +} \ No newline at end of file diff --git a/Sources/com.elgato.applemail.sdPlugin/fr.json b/Sources/com.elgato.applemail.sdPlugin/fr.json new file mode 100644 index 0000000..9daddc1 --- /dev/null +++ b/Sources/com.elgato.applemail.sdPlugin/fr.json @@ -0,0 +1,8 @@ +{ + "Description": "Afficher le nombre d’e-mails non lus dans Apple Mail.", + "Name": "Apple Mail", + "com.elgato.applemail.action": { + "Name": "Apple Mail", + "Tooltip": "Afficher le nombre d’e-mails non lus dans Apple Mail" + } +} \ No newline at end of file diff --git a/Sources/com.elgato.applemail.sdPlugin/ja.json b/Sources/com.elgato.applemail.sdPlugin/ja.json new file mode 100644 index 0000000..ede9c37 --- /dev/null +++ b/Sources/com.elgato.applemail.sdPlugin/ja.json @@ -0,0 +1,8 @@ +{ + "Description": "Appleの“メール”の未開封のメール数を表示します。", + "Name": "Appleの“メール”", + "com.elgato.applemail.action": { + "Name": "Appleの“メール”", + "Tooltip": "Appleの“メール”の未開封のメール数を表示します" + } +} \ No newline at end of file diff --git a/Sources/com.elgato.applemail.sdPlugin/ko.json b/Sources/com.elgato.applemail.sdPlugin/ko.json new file mode 100644 index 0000000..4aa5e8f --- /dev/null +++ b/Sources/com.elgato.applemail.sdPlugin/ko.json @@ -0,0 +1,8 @@ +{ + "Description": "Apple Mail에서 읽지 않은 이메일 개수를 표시합니다.", + "Name": "Apple Mail", + "com.elgato.applemail.action": { + "Name": "Apple Mail", + "Tooltip": "Apple Mail에서 읽지 않은 이메일 개수를 표시합니다" + } +} \ No newline at end of file diff --git a/Sources/com.elgato.applemail.sdPlugin/manifest.json b/Sources/com.elgato.applemail.sdPlugin/manifest.json new file mode 100644 index 0000000..2889f4d --- /dev/null +++ b/Sources/com.elgato.applemail.sdPlugin/manifest.json @@ -0,0 +1,37 @@ +{ + "Actions": [ + { + "Icon": "MailIcon", + "Name": "Apple Mail", + "States": [ + { + "Image": "MailIcon", + "FontSize": "18", + "FontStyle": "Bold", + "TitleAlignment": "middle", + "TitleColor": "#ffffff" + } + ], + "Tooltip": "Display the number of unread emails from Apple Mail", + "UUID": "com.elgato.applemail.action" + } + ], + "Author": "Elgato", + "CodePath": "AppleMail", + "Description": "Display the number of unread emails from Apple Mail.", + "Name": "Apple Mail", + "Icon": "MailIcon", + "URL": "https://www.elgato.com/gaming/stream-deck", + "Version": "1.2", + "OS": [ + { + "Platform": "mac", + "MinimumVersion" : "10.11" + } + ], + "ApplicationsToMonitor": { + "mac": [ + "com.apple.mail" + ] + } +} diff --git a/Sources/com.elgato.applemail.sdPlugin/zh_CN.json b/Sources/com.elgato.applemail.sdPlugin/zh_CN.json new file mode 100644 index 0000000..dc908cc --- /dev/null +++ b/Sources/com.elgato.applemail.sdPlugin/zh_CN.json @@ -0,0 +1,8 @@ +{ + "Description": "显示 Apple“邮件”中未读电子邮件的数量。", + "Name": "Apple“邮件”", + "com.elgato.applemail.action": { + "Name": "Apple“邮件”", + "Tooltip": "显示 Apple“邮件”中未读电子邮件的数量" + } +} \ No newline at end of file