Repository: swisspol/GCDTelnetServer Branch: master Commit: d11cdc3c4891 Files: 50 Total size: 213.7 KB Directory structure: gitextract_lb0k8_qy/ ├── .clang-format ├── .gitignore ├── CLT/ │ └── main.m ├── Format-Source.sh ├── GCDNetworking/ │ ├── .clang-format │ ├── .gitignore │ ├── .travis.yml │ ├── CLT/ │ │ └── main.m │ ├── Format-Source.sh │ ├── GCDNetworking/ │ │ ├── GCDNetworking.h │ │ ├── GCDNetworkingLoggingBridgePrivate.h │ │ ├── GCDNetworkingPrivate.h │ │ ├── GCDTCPClient.h │ │ ├── GCDTCPClient.m │ │ ├── GCDTCPConnection.h │ │ ├── GCDTCPConnection.m │ │ ├── GCDTCPPeer.h │ │ ├── GCDTCPPeer.m │ │ ├── GCDTCPServer.h │ │ └── GCDTCPServer.m │ ├── GCDNetworking.xcodeproj/ │ │ ├── project.pbxproj │ │ └── xcshareddata/ │ │ └── xcschemes/ │ │ └── Tests.xcscheme │ ├── LICENSE │ ├── README.md │ ├── Run-Tests.sh │ ├── Tests/ │ │ └── GCDNetworking_Tests.m │ └── iOS/ │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Info.plist │ └── main.m ├── GCDTelnetServer/ │ ├── GCDTelnetConnection.h │ ├── GCDTelnetConnection.m │ ├── GCDTelnetLogging.h │ ├── GCDTelnetLoggingBridgePrivate.h │ ├── GCDTelnetPrivate.h │ ├── GCDTelnetServer.h │ ├── GCDTelnetServer.m │ ├── NSMutableString+ANSI.h │ └── NSMutableString+ANSI.m ├── GCDTelnetServer.podspec ├── GCDTelnetServer.xcodeproj/ │ ├── project.pbxproj │ └── xcshareddata/ │ └── xcschemes/ │ └── Tests.xcscheme ├── LICENSE ├── README.md ├── Run-Tests.sh ├── Tests/ │ └── GCDTelnetServer_Tests.m └── iOS/ ├── AppDelegate.h ├── AppDelegate.m ├── Info.plist └── main.m ================================================ FILE CONTENTS ================================================ ================================================ FILE: .clang-format ================================================ --- BasedOnStyle: Google Standard: Cpp11 ColumnLimit: 0 AlignTrailingComments: false NamespaceIndentation: All DerivePointerAlignment: false AlwaysBreakBeforeMultilineStrings: false AccessModifierOffset: -2 ObjCSpaceBeforeProtocolList: true SortIncludes: false --- Language: ObjC ... ================================================ FILE: .gitignore ================================================ .DS_Store xcuserdata project.xcworkspace ================================================ FILE: CLT/main.m ================================================ /* Copyright (c) 2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #import "GCDTelnetServer.h" #import "NSMutableString+ANSI.h" int main(int argc, const char* argv[]) { @autoreleasepool { GCDTCPServer* server = [[GCDTelnetServer alloc] initWithPort:2323 startHandler:^NSString*(GCDTelnetConnection* connection) { NSMutableString* string = [[NSMutableString alloc] init]; if (connection.colorTerminal) { [string appendANSIString:@"Welcome in color!" withColor:kANSIColor_Green bold:NO]; } else { [string appendString:@"Welcome!"]; } [string appendFormat:@"\nYou are connected using \"%@\"\n", connection.terminalType]; return string; } lineHandler:^NSString*(GCDTelnetConnection* connection, NSString* line) { return [line stringByAppendingString:@"\n"]; }]; if (![server start]) { abort(); } NSLog(@"Telnet server running on %@", GCDTCPServerGetPrimaryIPAddress(false)); CFRunLoopRun(); } return 0; } ================================================ FILE: Format-Source.sh ================================================ #!/bin/sh -ex # brew install clang-format CLANG_FORMAT_VERSION=`clang-format -version | awk '{ print $3 }'` if [[ "$CLANG_FORMAT_VERSION" != "5.0.0" ]]; then echo "Unsupported clang-format version" exit 1 fi pushd "GCDTelnetServer" clang-format -style=file -i *.h *.m popd pushd "Tests" clang-format -style=file -i *.m popd pushd "CLT" clang-format -style=file -i *.m popd pushd "iOS" clang-format -style=file -i *.h *.m popd echo "OK" ================================================ FILE: GCDNetworking/.clang-format ================================================ --- BasedOnStyle: Google Standard: Cpp11 ColumnLimit: 0 AlignTrailingComments: false NamespaceIndentation: All DerivePointerAlignment: false AlwaysBreakBeforeMultilineStrings: false AccessModifierOffset: -2 ObjCSpaceBeforeProtocolList: true SortIncludes: false --- Language: ObjC ... ================================================ FILE: GCDNetworking/.gitignore ================================================ .DS_Store xcuserdata project.xcworkspace ================================================ FILE: GCDNetworking/.travis.yml ================================================ language: objective-c script: ./Run-Tests.sh ================================================ FILE: GCDNetworking/CLT/main.m ================================================ /* Copyright (c) 2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #import "GCDNetworking.h" @interface Connection : GCDTCPServerConnection @end @implementation Connection - (void)didOpen { [super didOpen]; NSString* welcome = @"Hello World!\n"; [self writeDataAsynchronously:[welcome dataUsingEncoding:NSUTF8StringEncoding] completion:^(BOOL success) { [self close]; }]; } @end int main(int argc, const char* argv[]) { @autoreleasepool { GCDTCPServer* server = [[GCDTCPServer alloc] initWithConnectionClass:[Connection class] port:2323]; if (![server start]) { abort(); } NSLog(@"TCP server running on %@", GCDTCPServerGetPrimaryIPAddress(false)); CFRunLoopRun(); } return 0; } ================================================ FILE: GCDNetworking/Format-Source.sh ================================================ #!/bin/sh -ex # brew install clang-format CLANG_FORMAT_VERSION=`clang-format -version | awk '{ print $3 }'` if [[ "$CLANG_FORMAT_VERSION" != "5.0.0" ]]; then echo "Unsupported clang-format version" exit 1 fi pushd "GCDNetworking" clang-format -style=file -i *.h *.m popd pushd "Tests" clang-format -style=file -i *.m popd pushd "CLT" clang-format -style=file -i *.m popd pushd "iOS" clang-format -style=file -i *.h *.m popd echo "OK" ================================================ FILE: GCDNetworking/GCDNetworking/GCDNetworking.h ================================================ /* Copyright (c) 2012-2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #import "GCDTCPConnection.h" #import "GCDTCPPeer.h" #import "GCDTCPClient.h" #import "GCDTCPServer.h" ================================================ FILE: GCDNetworking/GCDNetworking/GCDNetworkingLoggingBridgePrivate.h ================================================ /* Copyright (c) 2012-2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #import /** * Automatically detect if XLFacility is available. */ #if defined(__has_include) && __has_include("XLFacilityMacros.h") #define __LOGGING_BRIDGE_XLFACILITY__ #import "XLFacilityMacros.h" #define _LOG_DEBUG(...) XLOG_DEBUG(__VA_ARGS__) #define _LOG_VERBOSE(...) XLOG_VERBOSE(__VA_ARGS__) #define _LOG_INFO(...) XLOG_INFO(__VA_ARGS__) #define _LOG_WARNING(...) XLOG_WARNING(__VA_ARGS__) #define _LOG_ERROR(...) XLOG_ERROR(__VA_ARGS__) #define _LOG_EXCEPTION(__EXCEPTION__) XLOG_EXCEPTION(__EXCEPTION__) #define _LOG_DEBUG_CHECK(__CONDITION__) XLOG_DEBUG_CHECK(__CONDITION__) #define _LOG_DEBUG_UNREACHABLE() XLOG_DEBUG_UNREACHABLE() /** * Automatically detect if CocoaLumberJack is available. */ #elif defined(__has_include) && __has_include("DDLogMacros.h") #import "DDLogMacros.h" #define __LOGGING_BRIDGE_COCOALUMBERJACK__ #undef LOG_LEVEL_DEF #define LOG_LEVEL_DEF _LoggingMinLevel extern int _LoggingMinLevel; #define _LOG_DEBUG(...) DDLogDebug(__VA_ARGS__) #define _LOG_VERBOSE(...) DDLogVerbose(__VA_ARGS__) #define _LOG_INFO(...) DDLogInfo(__VA_ARGS__) #define _LOG_WARNING(...) DDLogWarn(__VA_ARGS__) #define _LOG_ERROR(...) DDLogError(__VA_ARGS__) #define _LOG_EXCEPTION(__EXCEPTION__) DDLogError(@"%@", __EXCEPTION__) /** * Check if a custom logging header should be used instead. */ #elif defined(__LOGGING_CUSTOM_HEADER__) #define __LOGGING_BRIDGE_CUSTOM__ #import __LOGGING_CUSTOM_HEADER__ /** * If all of the above fail, fall back to NSLog(). */ #else #define __LOGGING_BRIDGE_BUILTIN__ #if DEBUG #define _LOG_DEBUG(...) NSLog(__VA_ARGS__) #define _LOG_VERBOSE(...) NSLog(__VA_ARGS__) #define _LOG_INFO(...) NSLog(__VA_ARGS__) #else #define _LOG_DEBUG(...) #define _LOG_VERBOSE(...) #define _LOG_INFO(...) #endif #define _LOG_WARNING(...) NSLog(__VA_ARGS__) #define _LOG_ERROR(...) NSLog(__VA_ARGS__) #define _LOG_EXCEPTION(__EXCEPTION__) NSLog(@"%@", __EXCEPTION__) #endif /** * Consistency check macros. */ #if !defined(_LOG_CHECK) #define _LOG_CHECK(__CONDITION__) \ do { \ if (!(__CONDITION__)) { \ abort(); \ } \ } while (0) #endif #if !defined(_LOG_DEBUG_CHECK) #if DEBUG #define _LOG_DEBUG_CHECK(__CONDITION__) _LOG_CHECK(__CONDITION__) #else #define _LOG_DEBUG_CHECK(__CONDITION__) #endif #endif #if !defined(_LOG_UNREACHABLE) #define _LOG_UNREACHABLE() abort() #endif #if !defined(_LOG_DEBUG_UNREACHABLE) #if DEBUG #define _LOG_DEBUG_UNREACHABLE() _LOG_UNREACHABLE() #else #define _LOG_DEBUG_UNREACHABLE() #endif #endif ================================================ FILE: GCDNetworking/GCDNetworking/GCDNetworkingPrivate.h ================================================ /* Copyright (c) 2012-2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ /** * All GCDNetworking headers. */ #import "GCDNetworking.h" #import "GCDNetworkingLoggingBridgePrivate.h" /** * GCDNetworking internal constants and APIs. */ #define GN_QUEUE_LABEL object_getClassName(self) #define GN_GLOBAL_DISPATCH_QUEUE dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0) ================================================ FILE: GCDNetworking/GCDNetworking/GCDTCPClient.h ================================================ /* Copyright (c) 2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #import "GCDTCPPeer.h" @class GCDTCPClient; /** * The GCDTCPClientConnection is an abstract class to implement connections * for GCDTCPClient: it cannot be used directly. */ @interface GCDTCPClientConnection : GCDTCPPeerConnection @end /** * The GCDTCPClient is a base class that implements a TCP client. It connects * to IPv4 or IPv6 TCP servers and can be configured to reconnect automatically * if the connection is lost. * * @warning GCDTCPClient will only ever have a single connection at a time. */ @interface GCDTCPClient : GCDTCPPeer /** * Returns the host as specified when the client was initialized. */ @property(nonatomic, readonly) NSString* host; /** * Returns the port as specified when the client was initialized. */ @property(nonatomic, readonly) NSUInteger port; /** * Sets the connection timeout. * * The default value is 10 seconds. */ @property(nonatomic) NSTimeInterval connectionTimeout; /** * Sets if GCDTCPClient automatically attempts to reconnnect to the server * if the connection is lost. * * If enabled, after the connection is lost, GCDTCPClientLogger will attempt * to reconnect after the minimal allowed interval and then multiply this * interval by 2 after each failure to reconnect until the maximal allowed * interval is reached e.g. 10s, 20s, 40s, 80s... * * The default value is YES. */ @property(nonatomic) BOOL automaticallyReconnects; /** * Sets the minimal reconnection interval after the connection was lost. * * The default value is 1 second. */ @property(nonatomic) NSTimeInterval minReconnectInterval; /** * Sets the maximal reconnection interval after the connection was lost. * * The default value is 300 seconds. */ @property(nonatomic) NSTimeInterval maxReconnectInterval; /** * This method is the designated initializer for the class. * * Connection class must be [GCDTCPClientConnection class] or a subclass of it. */ - (instancetype)initWithConnectionClass:(Class)connectionClass host:(NSString*)hostname port:(NSUInteger)port; @end @interface GCDTCPClient (Extensions) /** * Convenience method that returns the single connection. */ @property(nonatomic, readonly) GCDTCPClientConnection* connection; @end ================================================ FILE: GCDNetworking/GCDNetworking/GCDTCPClient.m ================================================ /* Copyright (c) 2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #if !__has_feature(objc_arc) #error GCDNetworking requires ARC #endif #import "GCDNetworkingPrivate.h" @implementation GCDTCPClientConnection @end @interface GCDTCPClient () { @private NSUInteger _generation; NSTimeInterval _reconnectionDelay; } @end @implementation GCDTCPClient - (id)init { [self doesNotRecognizeSelector:_cmd]; return nil; } - (instancetype)initWithConnectionClass:(Class)connectionClass host:(NSString*)hostname port:(NSUInteger)port { _LOG_DEBUG_CHECK([connectionClass isSubclassOfClass:[GCDTCPClientConnection class]]); _LOG_DEBUG_CHECK(hostname); _LOG_DEBUG_CHECK(port > 0); if ((self = [super initWithConnectionClass:connectionClass])) { _host = [hostname copy]; _port = port; _connectionTimeout = 10.0; _automaticallyReconnects = YES; _minReconnectInterval = 1.0; _maxReconnectInterval = 300.0; _reconnectionDelay = 1.0; } return self; } // Must be called inside lock queue - (void)_scheduleReconnection { _LOG_DEBUG(@"%@ will attempt to reconnect to \"%@:%i\" in %.0f seconds", [self class], _host, (int)_port, _reconnectionDelay); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_reconnectionDelay * NSEC_PER_SEC)), GN_GLOBAL_DISPATCH_QUEUE, ^{ dispatch_sync(self.lockQueue, ^{ if (_reconnectionDelay > 0.0) { [self _reconnect]; } }); }); _reconnectionDelay = MIN(2.0 * _reconnectionDelay, _maxReconnectInterval); } - (void)_reconnect { _LOG_DEBUG(@"%@ attempting to connect to \"%@:%i\"", [self class], _host, (int)_port); _generation += 1; NSUInteger lastGeneration = _generation; [self.connectionClass connectAsynchronouslyToHost:_host port:_port timeout:_connectionTimeout completion:^(GCDTCPConnection* connection) { if (connection) { if (lastGeneration == _generation) { [self willOpenConnection:(GCDTCPPeerConnection*)connection]; dispatch_sync(self.lockQueue, ^{ _reconnectionDelay = _minReconnectInterval; }); } else { _LOG_DEBUG(@"%@ ignoring stalled connection to \"%@:%i\"", [self class], _host, (int)_port); [connection close]; } } else if (_automaticallyReconnects) { dispatch_sync(self.lockQueue, ^{ if (_reconnectionDelay > 0.0) { [self _scheduleReconnection]; } }); } }]; } - (BOOL)willStart { dispatch_sync(self.lockQueue, ^{ _reconnectionDelay = _minReconnectInterval; }); [self _reconnect]; return YES; } - (void)didStop { dispatch_sync(self.lockQueue, ^{ _reconnectionDelay = 0.0; }); } - (void)didCloseConnection:(GCDTCPPeerConnection*)connection { [super didCloseConnection:connection]; dispatch_sync(self.lockQueue, ^{ if (_reconnectionDelay > 0.0) { [self _scheduleReconnection]; } }); } @end @implementation GCDTCPClient (Extensions) - (GCDTCPClientConnection*)connection { return [self.connections anyObject]; } @end ================================================ FILE: GCDNetworking/GCDNetworking/GCDTCPConnection.h ================================================ /* Copyright (c) 2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #import #import /** * Constants representing the state of a GCDTCPConnection. */ typedef NS_ENUM(int, GCDTCPConnectionState) { kXLTCPConnectionState_Closed = -1, kXLTCPConnectionState_Initialized = 0, kXLTCPConnectionState_Opened = 1 }; /** * The GCDTCPConnection is a base class that handles TCP connections. */ @interface GCDTCPConnection : NSObject /** * Returns the state of the connection. */ @property(nonatomic, readonly) GCDTCPConnectionState state; /** * Returns the address of the local peer of the connection * as a raw "struct sockaddr". */ @property(nonatomic, readonly) NSData* localAddressData; /** * Returns the address of the remote peer of the connection * as a raw "struct sockaddr". */ @property(nonatomic, readonly) NSData* remoteAddressData; /** * Returns the underlying socket for the connection. * * This will be 0 if the connection has been closed. * * @warning Do not close the socket directly but use the -close method instead. */ @property(nonatomic, readonly) int socket; /** * Opens a new connection to a host. * * The returned connection will be nil on error. */ + (void)connectAsynchronouslyToHost:(NSString*)hostname port:(NSUInteger)port timeout:(NSTimeInterval)timeout completion:(void (^)(GCDTCPConnection* connection))completion; /** * This method is the designated initializer for the class. * * @warning The ownership of the socket is transferred to the connection. */ - (instancetype)initWithSocket:(int)socket; /** * Opens the connection (required before reading or writing from it). */ - (void)open; /** * Reads data synchronously to the socket. * * Pass 0 as "timeout" to block indefinitely. */ - (NSData*)readDataWithTimeout:(NSTimeInterval)timeout; /** * Reads data asynchronously from the socket. */ - (void)readDataAsynchronously:(void (^)(NSData* data))completion; /** * Writes data synchronously to the socket. * * Pass 0 as "timeout" to block indefinitely. */ - (BOOL)writeData:(NSData*)data withTimeout:(NSTimeInterval)timeout; /** * Writes data asynchronously to the socket. */ - (void)writeDataAsynchronously:(NSData*)data completion:(void (^)(BOOL success))completion; /** * Closes the connection. */ - (void)close; @end @interface GCDTCPConnection (Subclassing) /** * Called after the underlying socket was opened. * * The default implementation does nothing. */ - (void)didOpen; /** * Called after the underlying socket was closed. * * The default implementation does nothing. */ - (void)didClose; @end @interface GCDTCPConnection (Extensions) /** * Returns YES if the connection is using IPv6. */ @property(nonatomic, readonly, getter=isUsingIPv6) BOOL usingIPv6; /** * Returns the port of the local peer of the connection. */ @property(nonatomic, readonly) NSUInteger localPort; /** * Returns the IP address of the local peer of the connection as a string. */ @property(nonatomic, readonly) NSString* localIPAddress; /** * Returns the port of the remote peer of the connection. */ @property(nonatomic, readonly) NSUInteger remotePort; /** * Returns the IP address of the remote peer of the connection as a string. */ @property(nonatomic, readonly) NSString* remoteIPAddress; /** * Writes a buffer synchronously to the socket. */ - (BOOL)writeBuffer:(const void*)buffer length:(NSUInteger)length withTimeout:(NSTimeInterval)timeout; /** * Writes a NULL terminated string synchronously to the socket. */ - (BOOL)writeCString:(const char*)string withTimeout:(NSTimeInterval)timeout; /** * Writes string synchronously to the socket using UTF8 encoding. */ - (BOOL)writeString:(NSString*)string withTimeout:(NSTimeInterval)timeout; /** * Writes string asynchronously to the socket using UTF8 encoding. */ - (void)writeStringAsynchronously:(NSString*)string completion:(void (^)(BOOL success))completion; @end ================================================ FILE: GCDNetworking/GCDNetworking/GCDTCPConnection.m ================================================ /* Copyright (c) 2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #if !__has_feature(objc_arc) #error GCDNetworking requires ARC #endif #import #import #import "GCDNetworkingPrivate.h" #define kReadBufferSize (64 * 1024) // Max IP packet size typedef union { struct sockaddr addr; struct sockaddr_in addr4; struct sockaddr_in6 addr6; } SocketAddress; static NSString* _IPAddressFromAddressData(const struct sockaddr* address) { NSString* string = nil; if (address) { char hostBuffer[NI_MAXHOST]; if (getnameinfo(address, address->sa_len, hostBuffer, sizeof(hostBuffer), NULL, 0, NI_NUMERICHOST | NI_NOFQDN) >= 0) { string = [NSString stringWithUTF8String:hostBuffer]; } else { _LOG_ERROR(@"Failed converting IP address data to string: %s", strerror(errno)); } } return string; } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wcast-align" #pragma clang diagnostic ignored "-Wunreachable-code-return" static NSUInteger _PortFromAddressData(const struct sockaddr* address) { switch (address->sa_family) { case AF_INET: return ntohs(((const struct sockaddr_in*)address)->sin_port); case AF_INET6: return ntohs(((const struct sockaddr_in6*)address)->sin6_port); } _LOG_DEBUG_UNREACHABLE(); return 0; } #pragma clang diagnostic pop @interface GCDTCPConnection () { @private dispatch_queue_t _lockQueue; GCDTCPConnectionState _state; dispatch_group_t _writeGroup; } @end @implementation GCDTCPConnection static int _CreateConnectedSocket(NSString* hostname, NSUInteger port, const struct sockaddr* addr, socklen_t len, NSTimeInterval timeout, BOOL isIPv6) { int connectedSocket = socket(isIPv6 ? PF_INET6 : PF_INET, SOCK_STREAM, IPPROTO_TCP); if (connectedSocket >= 0) { BOOL success = NO; fcntl(connectedSocket, F_SETFL, O_NONBLOCK); _LOG_DEBUG(@"Connecting %s socket to \"%@:%i\" (%@)...", isIPv6 ? "IPv6" : "IPv4", hostname, (int)port, _IPAddressFromAddressData(addr)); int result = connect(connectedSocket, addr, len); if ((result == -1) && (errno == EINPROGRESS)) { fd_set fdset; FD_ZERO(&fdset); FD_SET(connectedSocket, &fdset); struct timeval tv; tv.tv_sec = timeout; tv.tv_usec = fmod(timeout * 1000000.0, 1.0); result = select(connectedSocket + 1, NULL, &fdset, NULL, &tv); if (result == 1) { int error; socklen_t errorlen = sizeof(error); result = getsockopt(connectedSocket, SOL_SOCKET, SO_ERROR, &error, &errorlen); if (result == 0) { if (error == 0) { success = YES; } else { _LOG_ERROR(@"Failed connecting %s socket to \"%@:%i\" (%@): %s", isIPv6 ? "IPv6" : "IPv4", hostname, (int)port, _IPAddressFromAddressData(addr), strerror(error)); } } else { _LOG_ERROR(@"Failed retrieving %s socket option: %s", isIPv6 ? "IPv6" : "IPv4", strerror(errno)); } } else if (result == 0) { _LOG_ERROR(@"Timed out connecting %s socket to \"%@:%i\" (%@)", isIPv6 ? "IPv6" : "IPv4", hostname, (int)port, _IPAddressFromAddressData(addr)); } } else { _LOG_ERROR(@"Failed connecting %s socket to \"%@:%i\" (%@): %s", isIPv6 ? "IPv6" : "IPv4", hostname, (int)port, _IPAddressFromAddressData(addr), strerror(errno)); } if (success) { fcntl(connectedSocket, F_SETFL, 0); } else { close(connectedSocket); connectedSocket = -1; } } else { _LOG_ERROR(@"Failed creating %s socket: %s", isIPv6 ? "IPv6" : "IPv4", strerror(errno)); } return connectedSocket; } + (void)connectAsynchronouslyToHost:(NSString*)hostname port:(NSUInteger)port timeout:(NSTimeInterval)timeout completion:(void (^)(GCDTCPConnection* connection))completion { dispatch_async(GN_GLOBAL_DISPATCH_QUEUE, ^{ GCDTCPConnection* connection = nil; CFHostRef host = CFHostCreateWithName(kCFAllocatorDefault, (__bridge CFStringRef)hostname); // Consider using low-level getaddrinfo() instead CFStreamError error = {0}; if (CFHostStartInfoResolution(host, kCFHostAddresses, &error)) { NSArray* addressing = (__bridge NSArray*)CFHostGetAddressing(host, NULL); for (NSData* addressData in addressing) { SocketAddress address; bcopy(addressData.bytes, &address, addressData.length); if (((address.addr.sa_family == AF_INET) && (address.addr.sa_len == sizeof(struct sockaddr_in))) // Allow IPv4 hosts || ((address.addr.sa_family == AF_INET6) && (address.addr.sa_len == sizeof(struct sockaddr_in6)))) { // Allow IPv6 hosts if (address.addr.sa_family == AF_INET6) { address.addr6.sin6_port = htons(port); } else { address.addr4.sin_port = htons(port); } int socket = _CreateConnectedSocket(hostname, port, &address.addr, address.addr.sa_len, timeout, address.addr.sa_family == AF_INET6); if (socket >= 0) { connection = [[self alloc] initWithSocket:socket]; if (connection) { break; } else { _LOG_ERROR(@"Failed creating %@ instance with connected socket", NSStringFromClass([self class])); close(socket); } } } } } else { _LOG_ERROR(@"Failed resolving host \"%@\": (%i, %i)", hostname, (int)error.domain, (int)error.error); } CFRelease(host); completion(connection); }); } - (void)_setSocketOption:(int)option valuePtr:(const void*)valuePtr valueLength:(socklen_t)valueLength { if (setsockopt(_socket, SOL_SOCKET, option, valuePtr, valueLength)) { _LOG_ERROR(@"Failed setting socket option: %s", strerror(errno)); } } - (void)_setSocketOption:(int)option withIntValue:(int)value { [self _setSocketOption:option valuePtr:&value valueLength:sizeof(int)]; } - (id)init { [self doesNotRecognizeSelector:_cmd]; return nil; } - (instancetype)initWithSocket:(int)socket { _LOG_DEBUG_CHECK(socket >= 0); if ((self = [super init])) { _lockQueue = dispatch_queue_create(GN_QUEUE_LABEL, DISPATCH_QUEUE_SERIAL); _state = kXLTCPConnectionState_Initialized; _socket = socket; [self _setSocketOption:SO_NOSIGPIPE withIntValue:1]; // Make sure this socket cannot generate SIG_PIPE when closed [self _setSocketOption:SO_KEEPALIVE withIntValue:1]; // Enable TCP keep-alive struct sockaddr_storage localSockAddr; socklen_t localAddrLen = sizeof(localSockAddr); if (getsockname(_socket, (struct sockaddr*)&localSockAddr, &localAddrLen) == 0) { _localAddressData = [[NSData alloc] initWithBytes:&localSockAddr length:localAddrLen]; } else { _LOG_ERROR(@"Failed retrieving local socket address: %s", strerror(errno)); } struct sockaddr_storage remoteSockAddr; socklen_t remoteAddrLen = sizeof(remoteSockAddr); if (getpeername(_socket, (struct sockaddr*)&remoteSockAddr, &remoteAddrLen) == 0) { _remoteAddressData = [[NSData alloc] initWithBytes:&remoteSockAddr length:remoteAddrLen]; } else { _LOG_ERROR(@"Failed retrieving remote socket address: %s", strerror(errno)); } } return self; } - (void)dealloc { if (_socket >= 0) { close(_socket); } if (_state == kXLTCPConnectionState_Opened) { _state = kXLTCPConnectionState_Closed; [self didClose]; } #if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE dispatch_release(_lockQueue); #endif } - (GCDTCPConnectionState)state { __block GCDTCPConnectionState state; dispatch_sync(_lockQueue, ^{ state = _state; }); return state; } - (void)open { __block BOOL didOpen = NO; dispatch_sync(_lockQueue, ^{ if (_state == kXLTCPConnectionState_Initialized) { _state = kXLTCPConnectionState_Opened; didOpen = YES; } }); if (didOpen) { [self didOpen]; } } - (NSData*)readDataWithTimeout:(NSTimeInterval)timeout { __block NSMutableData* data = nil; dispatch_sync(_lockQueue, ^{ if (_state == kXLTCPConnectionState_Opened) { struct timeval tv; tv.tv_sec = timeout; tv.tv_usec = fmod(timeout * 1000000.0, 1.0); [self _setSocketOption:SO_RCVTIMEO valuePtr:&tv valueLength:sizeof(tv)]; data = [[NSMutableData alloc] initWithLength:kReadBufferSize]; ssize_t len = recv(_socket, data.mutableBytes, data.length, 0); if (len >= 0) { data.length = len; } else { if (errno != EAGAIN) { _LOG_ERROR(@"Failed reading synchronously from socket: %s", strerror(errno)); } data = nil; } } }); return data; } - (void)_readBufferAsynchronously:(void (^)(dispatch_data_t buffer))completion { dispatch_sync(_lockQueue, ^{ if (_state == kXLTCPConnectionState_Opened) { dispatch_read(_socket, SIZE_MAX, GN_GLOBAL_DISPATCH_QUEUE, ^(dispatch_data_t data, int error) { @autoreleasepool { if (error) { _LOG_ERROR(@"Failed reading asynchronously from socket: %s", strerror(error)); if (completion) { completion(NULL); } } else if (completion) { completion(data); } } }); } else if (completion) { dispatch_async(GN_GLOBAL_DISPATCH_QUEUE, ^{ @autoreleasepool { completion(NULL); } }); } }); } - (void)readDataAsynchronously:(void (^)(NSData* data))completion { [self _readBufferAsynchronously:^(dispatch_data_t buffer) { if (buffer) { NSMutableData* data = [[NSMutableData alloc] init]; dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t offset, const void* bytes, size_t length) { [data appendBytes:bytes length:length]; return true; }); completion(data); } else { completion(nil); } }]; } - (BOOL)_writeBytes:(const void*)bytes length:(size_t)length withTimeout:(NSTimeInterval)timeout { __block BOOL result = NO; dispatch_sync(_lockQueue, ^{ if (_state == kXLTCPConnectionState_Opened) { struct timeval tv; tv.tv_sec = timeout; tv.tv_usec = fmod(timeout * 1000000.0, 1.0); [self _setSocketOption:SO_SNDTIMEO valuePtr:&tv valueLength:sizeof(tv)]; ssize_t len = send(_socket, bytes, length, 0); if (len == (ssize_t)length) { result = YES; } else if (errno != EAGAIN) { _LOG_ERROR(@"Failed writing synchronously to socket: %s", strerror(errno)); } } }); return result; } - (BOOL)writeData:(NSData*)data withTimeout:(NSTimeInterval)timeout { return [self _writeBytes:data.bytes length:data.length withTimeout:timeout]; } - (void)_writeBufferAsynchronously:(dispatch_data_t)buffer completion:(void (^)(BOOL success))completion { dispatch_sync(_lockQueue, ^{ if (_state == kXLTCPConnectionState_Opened) { dispatch_write(_socket, buffer, GN_GLOBAL_DISPATCH_QUEUE, ^(dispatch_data_t data, int error) { @autoreleasepool { if (error) { if (error != EPIPE) { _LOG_ERROR(@"Failed writing asynchronously to socket: %s", strerror(error)); } if (completion) { completion(NO); } } else if (completion) { completion(YES); } } }); } else if (completion) { dispatch_async(GN_GLOBAL_DISPATCH_QUEUE, ^{ @autoreleasepool { completion(NO); } }); } }); } - (void)writeDataAsynchronously:(NSData*)data completion:(void (^)(BOOL success))completion { dispatch_data_t buffer = dispatch_data_create(data.bytes, data.length, GN_GLOBAL_DISPATCH_QUEUE, ^{ [data self]; // Keeps ARC from releasing data too early }); [self _writeBufferAsynchronously:buffer completion:completion]; #if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE dispatch_release(buffer); #endif } - (void)close { __block BOOL didClose = NO; dispatch_sync(_lockQueue, ^{ if (_state == kXLTCPConnectionState_Opened) { close(_socket); _socket = -1; _state = kXLTCPConnectionState_Closed; didClose = YES; } }); if (didClose) { [self didClose]; } } @end @implementation GCDTCPConnection (Subclassing) - (void)didOpen { _LOG_DEBUG(@"%@ did open over %s from %@ (%i) to %@ (%i)", [self class], self.usingIPv6 ? "IPv6" : "IPv4", self.localIPAddress, (int)self.localPort, self.remoteIPAddress, (int)self.remotePort); } - (void)didClose { _LOG_DEBUG(@"%@ did close over %s from %@ (%i) to %@ (%i)", [self class], self.usingIPv6 ? "IPv6" : "IPv4", self.localIPAddress, (int)self.localPort, self.remoteIPAddress, (int)self.remotePort); } @end @implementation GCDTCPConnection (Extensions) - (BOOL)isUsingIPv6 { const struct sockaddr* localSockAddr = _localAddressData.bytes; return (localSockAddr->sa_family == AF_INET6); } - (NSUInteger)localPort { return _PortFromAddressData(_localAddressData.bytes); } - (NSString*)localIPAddress { return _IPAddressFromAddressData(_localAddressData.bytes); } - (NSUInteger)remotePort { return _PortFromAddressData(_remoteAddressData.bytes); } - (NSString*)remoteIPAddress { return _IPAddressFromAddressData(_remoteAddressData.bytes); } - (BOOL)writeBuffer:(const void*)buffer length:(NSUInteger)length withTimeout:(NSTimeInterval)timeout { return [self _writeBytes:buffer length:length withTimeout:timeout]; } - (BOOL)writeCString:(const char*)string withTimeout:(NSTimeInterval)timeout { return [self _writeBytes:string length:strlen(string) withTimeout:timeout]; } - (BOOL)writeString:(NSString*)string withTimeout:(NSTimeInterval)timeout { return [self writeData:[string dataUsingEncoding:NSUTF8StringEncoding] withTimeout:timeout]; } - (void)writeStringAsynchronously:(NSString*)string completion:(void (^)(BOOL success))completion { [self writeDataAsynchronously:[string dataUsingEncoding:NSUTF8StringEncoding] completion:completion]; } @end ================================================ FILE: GCDNetworking/GCDNetworking/GCDTCPPeer.h ================================================ /* Copyright (c) 2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #import "GCDTCPConnection.h" @class GCDTCPPeer; /** * The GCDTCPPeerConnection is an abstract class to implement connections * for GCDTCPPeer: it cannot be used directly. */ @interface GCDTCPPeerConnection : GCDTCPConnection /** * Returns the GCDTCPPeer that owns the connection. * * @warning This returns nil after the connection has been closed. */ @property(nonatomic, assign, readonly) GCDTCPPeer* peer; @end /** * The GCDTCPPeer is an abstract class to implement TCP peers: it cannot be * used directly. */ @interface GCDTCPPeer : NSObject /** * Returns the connection class as specified when the peer was initialized. */ @property(nonatomic, readonly) Class connectionClass; /** * Returns YES if the peer is running. */ @property(nonatomic, readonly, getter=isRunning) BOOL running; /** * Returns all currently opened connections. */ @property(nonatomic, readonly) NSSet* connections; #if TARGET_OS_IPHONE /** * Sets if the peer automatically stops and starts while entering background * and foreground on iOS. * * When an app enters background on iOS and is suspended, it cannot leave * listening sockets open, so it must either close them or start a background * task to prevent the app from getting suspended while in background. * * If this property is set to NO then the peer will automatically create a * background task when it is started and end it when it is stopped. Note that * this task can only run for a limited time while the app is in background * before iOS eventually force ends it. At this point the peer will be stopped * no matter what. * * The default value is NO. */ @property(nonatomic) BOOL suspendInBackground; #endif /** * This method is the designated initializer for the class. * * Connection class must be [GCDTCPPeerConnection class] or a subclass of it. */ - (instancetype)initWithConnectionClass:(Class)connectionClass; /** * Starts the peer. * * Returns NO on error. */ - (BOOL)start; /** * Stops the peer. * * @warning This blocks until all opened connections have been closed. */ - (void)stop; @end @interface GCDTCPPeer (Subclassing) /** * Returns a GCD serial queue subclasses can use to protect internal data * that can be accessed concurrently from multiple threads. */ @property(nonatomic, readonly) dispatch_queue_t lockQueue; /** * This method is called whenever the peer starts. * * @warning This method can be called on arbitrary threads. */ - (BOOL)willStart; /** * This method is called whenever a new connection is opened. * * @warning This method can be called on arbitrary threads. */ - (void)willOpenConnection:(GCDTCPPeerConnection*)connection; /** * This method is called after a connection has been closed. * * @warning This method can be called on arbitrary threads. */ - (void)didCloseConnection:(GCDTCPPeerConnection*)connection; /** * This method is called whenever the peer stops. * * @warning This method can be called on arbitrary threads. */ - (void)didStop; @end @interface GCDTCPPeer (Extensions) /** * Enumerates all currently opened connections. */ - (void)enumerateConnectionsUsingBlock:(void (^)(GCDTCPPeerConnection* connection, BOOL* stop))block; @end ================================================ FILE: GCDNetworking/GCDNetworking/GCDTCPPeer.m ================================================ /* Copyright (c) 2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #if !__has_feature(objc_arc) #error GCDNetworking requires ARC #endif #import #if TARGET_OS_IPHONE #import #endif #import "GCDNetworkingPrivate.h" @interface GCDTCPPeerConnection () @property(nonatomic, assign) GCDTCPPeer* peer; @end @implementation GCDTCPPeerConnection - (void)didClose { [super didClose]; [_peer didCloseConnection:self]; _peer = nil; } @end @interface GCDTCPPeer () { @private dispatch_queue_t _lockQueue; dispatch_group_t _syncGroup; NSMutableSet* _connections; #if TARGET_OS_IPHONE UIBackgroundTaskIdentifier _backgroundTask; BOOL _restart; #endif } @end @implementation GCDTCPPeer - (id)init { [self doesNotRecognizeSelector:_cmd]; return nil; } - (instancetype)initWithConnectionClass:(Class)connectionClass { _LOG_DEBUG_CHECK([connectionClass isSubclassOfClass:[GCDTCPPeerConnection class]]); if ((self = [super init])) { _connectionClass = connectionClass; _lockQueue = dispatch_queue_create(GN_QUEUE_LABEL, DISPATCH_QUEUE_SERIAL); _syncGroup = dispatch_group_create(); _connections = [[NSMutableSet alloc] init]; #if TARGET_OS_IPHONE _backgroundTask = UIBackgroundTaskInvalid; #endif #if TARGET_OS_IPHONE [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_didEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_willEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil]; #endif } return self; } - (void)dealloc { #if TARGET_OS_IPHONE [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil]; #endif #if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE dispatch_release(_syncGroup); dispatch_release(_lockQueue); #endif } - (NSSet*)connections { __block NSSet* connections; dispatch_sync(_lockQueue, ^{ connections = [_connections copy]; }); return connections; } - (BOOL)start { _LOG_DEBUG_CHECK(!_running); if (![self willStart]) { return NO; } #if TARGET_OS_IPHONE if (!_suspendInBackground) { _backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ [self stop]; _restart = YES; [[UIApplication sharedApplication] endBackgroundTask:_backgroundTask]; _backgroundTask = UIBackgroundTaskInvalid; }]; } _restart = NO; #endif _running = YES; _LOG_DEBUG(@"%@ started", [self class]); return YES; } #if TARGET_OS_IPHONE - (void)_didEnterBackground:(NSNotification*)notification { if (_running && _suspendInBackground) { [self stop]; _restart = YES; } } - (void)_willEnterForeground:(NSNotification*)notification { if (_restart) { [self start]; // Not much we can do on failure } } #endif - (void)stop { _LOG_DEBUG_CHECK(_running); #if TARGET_OS_IPHONE if (_backgroundTask != UIBackgroundTaskInvalid) { [[UIApplication sharedApplication] endBackgroundTask:_backgroundTask]; _backgroundTask = UIBackgroundTaskInvalid; } #endif [self didStop]; NSSet* connections = self.connections; for (GCDTCPPeerConnection* connection in connections) { // No need to use "_lockQueue" since no new connections can be created anymore and it would deadlock with -didCloseConnection: [connection close]; } dispatch_group_wait(_syncGroup, DISPATCH_TIME_FOREVER); // Wait until all connections are closed _running = NO; _LOG_DEBUG(@"%@ stopped", [self class]); } @end @implementation GCDTCPPeer (Subclassing) - (dispatch_queue_t)lockQueue { return _lockQueue; } - (BOOL)willStart { return YES; } - (void)didStop { ; } - (void)willOpenConnection:(GCDTCPPeerConnection*)connection { _LOG_DEBUG(@"%@ did connect to peer at \"%@\" (%i)", [self class], connection.remoteIPAddress, (int)connection.remotePort); connection.peer = self; dispatch_sync(_lockQueue, ^{ dispatch_group_enter(_syncGroup); [_connections addObject:connection]; }); [connection open]; } - (void)didCloseConnection:(GCDTCPPeerConnection*)connection { dispatch_sync(_lockQueue, ^{ if ([_connections containsObject:connection]) { [_connections removeObject:connection]; dispatch_group_leave(_syncGroup); } }); _LOG_DEBUG(@"%@ did disconnect from peer at \"%@\" (%i)", [self class], connection.remoteIPAddress, (int)connection.remotePort); } @end @implementation GCDTCPPeer (Extensions) - (void)enumerateConnectionsUsingBlock:(void (^)(GCDTCPPeerConnection* connection, BOOL* stop))block { dispatch_sync(_lockQueue, ^{ BOOL stop = NO; for (GCDTCPPeerConnection* connection in _connections) { block(connection, &stop); if (stop) { break; } } }); } @end ================================================ FILE: GCDNetworking/GCDNetworking/GCDTCPServer.h ================================================ /* Copyright (c) 2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #import "GCDTCPPeer.h" #ifdef __cplusplus extern "C" { #endif /** * On OS X, returns the IPv4 or IPv6 address as a string of the primary * connected service or nil if not available. * * On iOS, returns the IPv4 or IPv6 address as a string of the WiFi * interface if connected or nil otherwise. */ NSString* GCDTCPServerGetPrimaryIPAddress(BOOL useIPv6); #ifdef __cplusplus } #endif @class GCDTCPServer; /** * The GCDTCPServerConnection is an abstract class to implement connections * for GCDTCPServer: it cannot be used directly. */ @interface GCDTCPServerConnection : GCDTCPPeerConnection @end /** * The GCDTCPServer is a base class that implements a TCP server. It listens for * IPv4 or IPv6 TCP connections on a port and then creates GCDTCPServerConnection * instances for each. * * @warning On iOS, connecting to the server will not work while your app has * been suspended by the OS while in background. */ @interface GCDTCPServer : GCDTCPPeer /** * Returns the port as specified when the server was initialized. */ @property(nonatomic, readonly) NSUInteger port; /** * This method is the designated initializer for the class. * * Connection class must be [GCDTCPServerConnection class] or a subclass of it. */ - (instancetype)initWithConnectionClass:(Class)connectionClass port:(NSUInteger)port; @end ================================================ FILE: GCDNetworking/GCDNetworking/GCDTCPServer.m ================================================ /* Copyright (c) 2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #if !__has_feature(objc_arc) #error GCDNetworking requires ARC #endif #import #if TARGET_OS_IPHONE #import #else #import #endif #import #import #import #import "GCDNetworkingPrivate.h" #define kMaxPendingConnections 4 static NSString* _StringFromSockAddr(const struct sockaddr* addr, BOOL includeService) { NSString* string = nil; char hostBuffer[NI_MAXHOST]; char serviceBuffer[NI_MAXSERV]; if (getnameinfo(addr, addr->sa_len, hostBuffer, sizeof(hostBuffer), serviceBuffer, sizeof(serviceBuffer), NI_NUMERICHOST | NI_NUMERICSERV | NI_NOFQDN) >= 0) { string = includeService ? [NSString stringWithFormat:@"%s:%s", hostBuffer, serviceBuffer] : [NSString stringWithUTF8String:hostBuffer]; } else { _LOG_DEBUG_UNREACHABLE(); } return string; } NSString* GCDTCPServerGetPrimaryIPAddress(BOOL useIPv6) { NSString* address = nil; #if TARGET_OS_IPHONE #if !TARGET_IPHONE_SIMULATOR const char* primaryInterface = "en0"; // WiFi interface on iOS #endif #else const char* primaryInterface = NULL; SCDynamicStoreRef store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("XLTCPServer"), NULL, NULL); if (store) { CFPropertyListRef info = SCDynamicStoreCopyValue(store, CFSTR("State:/Network/Global/IPv4")); // There is no equivalent for IPv6 but the primary interface should be the same if (info) { primaryInterface = [[NSString stringWithString:(id)[(__bridge NSDictionary*)info objectForKey:@"PrimaryInterface"]] UTF8String]; CFRelease(info); } CFRelease(store); } if (primaryInterface == NULL) { primaryInterface = "lo0"; } #endif struct ifaddrs* list; if (getifaddrs(&list) >= 0) { for (struct ifaddrs* ifap = list; ifap; ifap = ifap->ifa_next) { #if TARGET_IPHONE_SIMULATOR // Assume en0 is Ethernet and en1 is WiFi since there is no way to use SystemConfiguration framework in iOS Simulator if (strcmp(ifap->ifa_name, "en0") && strcmp(ifap->ifa_name, "en1")) #else if (strcmp(ifap->ifa_name, primaryInterface)) #endif { continue; } if ((ifap->ifa_flags & IFF_UP) && ((!useIPv6 && (ifap->ifa_addr->sa_family == AF_INET)) || (useIPv6 && (ifap->ifa_addr->sa_family == AF_INET6)))) { address = _StringFromSockAddr(ifap->ifa_addr, NO); break; } } freeifaddrs(list); } return address; } @implementation GCDTCPServerConnection @end @interface GCDTCPServer () { @private dispatch_group_t _sourceGroup; dispatch_source_t _source4; dispatch_source_t _source6; } @end @implementation GCDTCPServer - (instancetype)initWithConnectionClass:(Class)connectionClass port:(NSUInteger)port { _LOG_DEBUG_CHECK([connectionClass isSubclassOfClass:[GCDTCPServerConnection class]]); if ((self = [super initWithConnectionClass:connectionClass])) { _port = port; _sourceGroup = dispatch_group_create(); } return self; } #if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE - (void)dealloc { dispatch_release(_sourceGroup); } #endif - (int)_createListeningSocket:(BOOL)useIPv6 localAddress:(const void*)address length:(socklen_t)length { int listeningSocket = socket(useIPv6 ? PF_INET6 : PF_INET, SOCK_STREAM, IPPROTO_TCP); if (listeningSocket >= 0) { int yes = 1; setsockopt(listeningSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); if (bind(listeningSocket, address, length) == 0) { if (listen(listeningSocket, kMaxPendingConnections) == 0) { return listeningSocket; } else { _LOG_ERROR(@"Failed starting %s listening socket: %s", useIPv6 ? "IPv6" : "IPv4", strerror(errno)); close(listeningSocket); } } else { _LOG_ERROR(@"Failed binding %s listening socket: %s", useIPv6 ? "IPv6" : "IPv4", strerror(errno)); close(listeningSocket); } } else { _LOG_ERROR(@"Failed creating %s listening socket: %s", useIPv6 ? "IPv6" : "IPv4", strerror(errno)); } return -1; } - (dispatch_source_t)_createDispatchSourceWithListeningSocket:(int)listeningSocket isIPv6:(BOOL)isIPv6 { dispatch_group_enter(_sourceGroup); dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, listeningSocket, 0, GN_GLOBAL_DISPATCH_QUEUE); dispatch_source_set_cancel_handler(source, ^{ close(listeningSocket); dispatch_group_leave(_sourceGroup); }); dispatch_source_set_event_handler(source, ^{ @autoreleasepool { struct sockaddr remoteSockAddr; socklen_t remoteAddrLen = sizeof(remoteSockAddr); int socket = accept(listeningSocket, &remoteSockAddr, &remoteAddrLen); if (socket >= 0) { GCDTCPServerConnection* connection = [[self.connectionClass alloc] initWithSocket:socket]; if (connection) { [self willOpenConnection:connection]; } else { _LOG_ERROR(@"Failed creating %@ instance", NSStringFromClass(self.connectionClass)); close(socket); } } else { _LOG_ERROR(@"Failed accepting %s socket: %s", isIPv6 ? "IPv6" : "IPv4", strerror(errno)); } } }); return source; } - (BOOL)willStart { struct sockaddr_in addr4; bzero(&addr4, sizeof(addr4)); addr4.sin_len = sizeof(addr4); addr4.sin_family = AF_INET; addr4.sin_port = htons(_port); addr4.sin_addr.s_addr = htonl(INADDR_ANY); int listeningSocket4 = [self _createListeningSocket:NO localAddress:&addr4 length:sizeof(addr4)]; struct sockaddr_in6 addr6; bzero(&addr6, sizeof(addr6)); addr6.sin6_len = sizeof(addr6); addr6.sin6_family = AF_INET6; addr6.sin6_port = htons(_port); addr6.sin6_addr = in6addr_any; int listeningSocket6 = [self _createListeningSocket:YES localAddress:&addr6 length:sizeof(addr6)]; if ((listeningSocket4 < 0) || (listeningSocket6 < 0)) { close(listeningSocket4); close(listeningSocket6); return NO; } _source4 = [self _createDispatchSourceWithListeningSocket:listeningSocket4 isIPv6:NO]; dispatch_resume(_source4); _source6 = [self _createDispatchSourceWithListeningSocket:listeningSocket6 isIPv6:YES]; dispatch_resume(_source6); return YES; } - (void)didStop { dispatch_source_cancel(_source6); dispatch_source_cancel(_source4); dispatch_group_wait(_sourceGroup, DISPATCH_TIME_FOREVER); // Wait until the cancellation handlers have been called which guarantees the listening sockets are closed #if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE dispatch_release(_source6); #endif _source6 = NULL; #if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE dispatch_release(_source4); #endif _source4 = NULL; } @end ================================================ FILE: GCDNetworking/GCDNetworking.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 48; objects = { /* Begin PBXAggregateTarget section */ E2B3CEE619E91770003ED065 /* Build All */ = { isa = PBXAggregateTarget; buildConfigurationList = E2B3CEE719E91770003ED065 /* Build configuration list for PBXAggregateTarget "Build All" */; buildPhases = ( ); dependencies = ( E2B3CEEB19E91784003ED065 /* PBXTargetDependency */, E2B3CF2B19E9189C003ED065 /* PBXTargetDependency */, E2168F9619EE04B200865350 /* PBXTargetDependency */, ); name = "Build All"; productName = "Build All"; }; /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ E26ABC2C19EC9F9A00654D9F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E26ABC2B19EC9F9A00654D9F /* main.m */; }; E280BF1419E9FF5000D85595 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E280BF1319E9FF5000D85595 /* CFNetwork.framework */; }; E280BF1619E9FF5500D85595 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E280BF1519E9FF5500D85595 /* CFNetwork.framework */; }; E2812BB919F2E9780046DDC1 /* GCDTCPClient.m in Sources */ = {isa = PBXBuildFile; fileRef = E2812BB219F2E9780046DDC1 /* GCDTCPClient.m */; }; E2812BBA19F2E9780046DDC1 /* GCDTCPClient.m in Sources */ = {isa = PBXBuildFile; fileRef = E2812BB219F2E9780046DDC1 /* GCDTCPClient.m */; }; E2812BBC19F2E9780046DDC1 /* GCDTCPClient.m in Sources */ = {isa = PBXBuildFile; fileRef = E2812BB219F2E9780046DDC1 /* GCDTCPClient.m */; }; E2812BBD19F2E9780046DDC1 /* GCDTCPConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = E2812BB419F2E9780046DDC1 /* GCDTCPConnection.m */; }; E2812BBE19F2E9780046DDC1 /* GCDTCPConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = E2812BB419F2E9780046DDC1 /* GCDTCPConnection.m */; }; E2812BC019F2E9780046DDC1 /* GCDTCPConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = E2812BB419F2E9780046DDC1 /* GCDTCPConnection.m */; }; E2812BC119F2E9780046DDC1 /* GCDTCPPeer.m in Sources */ = {isa = PBXBuildFile; fileRef = E2812BB619F2E9780046DDC1 /* GCDTCPPeer.m */; }; E2812BC219F2E9780046DDC1 /* GCDTCPPeer.m in Sources */ = {isa = PBXBuildFile; fileRef = E2812BB619F2E9780046DDC1 /* GCDTCPPeer.m */; }; E2812BC419F2E9780046DDC1 /* GCDTCPPeer.m in Sources */ = {isa = PBXBuildFile; fileRef = E2812BB619F2E9780046DDC1 /* GCDTCPPeer.m */; }; E2812BC519F2E9780046DDC1 /* GCDTCPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = E2812BB819F2E9780046DDC1 /* GCDTCPServer.m */; }; E2812BC619F2E9780046DDC1 /* GCDTCPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = E2812BB819F2E9780046DDC1 /* GCDTCPServer.m */; }; E2812BC819F2E9780046DDC1 /* GCDTCPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = E2812BB819F2E9780046DDC1 /* GCDTCPServer.m */; }; E295AF8C1E6B4B1F00EAC2FF /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E295AF8B1E6B4B1F00EAC2FF /* SystemConfiguration.framework */; }; E295AF8D1E6B4B5C00EAC2FF /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E295AF8B1E6B4B1F00EAC2FF /* SystemConfiguration.framework */; }; E298C47A19ED866100C76821 /* GCDNetworking_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = E298C47919ED866100C76821 /* GCDNetworking_Tests.m */; }; E298C48619ED892500C76821 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E280BF1319E9FF5000D85595 /* CFNetwork.framework */; }; E2B3CF1B19E91825003ED065 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B3CF1819E91825003ED065 /* AppDelegate.m */; }; E2B3CF1D19E91825003ED065 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B3CF1A19E91825003ED065 /* main.m */; }; E2B3CF3019E919DD003ED065 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2B3CF2F19E919DD003ED065 /* UIKit.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ E2168F9519EE04B200865350 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = E26DC15F19E8494700C68DDC /* Project object */; proxyType = 1; remoteGlobalIDString = E298C46B19ED859F00C76821; remoteInfo = "XLFacility (Tests)"; }; E2B3CEEA19E91784003ED065 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = E26DC15F19E8494700C68DDC /* Project object */; proxyType = 1; remoteGlobalIDString = E26DC16619E8494700C68DDC; remoteInfo = GCDLogger; }; E2B3CF2A19E9189C003ED065 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = E26DC15F19E8494700C68DDC /* Project object */; proxyType = 1; remoteGlobalIDString = E2B3CEEF19E917BB003ED065; remoteInfo = "GCDLogger (iOS)"; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ E22E917A19F2EBD40043457E /* GCDNetworkingPrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDNetworkingPrivate.h; sourceTree = ""; }; E22E917F19F2F0850043457E /* GCDNetworking.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDNetworking.h; sourceTree = ""; }; E26ABC2B19EC9F9A00654D9F /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; E26DC16719E8494700C68DDC /* GCDNetworking */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = GCDNetworking; sourceTree = BUILT_PRODUCTS_DIR; }; E280BF0E19E9F3DF00D85595 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; E280BF1319E9FF5000D85595 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; }; E280BF1519E9FF5500D85595 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.1.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; }; E2812BB119F2E9780046DDC1 /* GCDTCPClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDTCPClient.h; sourceTree = ""; }; E2812BB219F2E9780046DDC1 /* GCDTCPClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDTCPClient.m; sourceTree = ""; }; E2812BB319F2E9780046DDC1 /* GCDTCPConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDTCPConnection.h; sourceTree = ""; }; E2812BB419F2E9780046DDC1 /* GCDTCPConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDTCPConnection.m; sourceTree = ""; }; E2812BB519F2E9780046DDC1 /* GCDTCPPeer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDTCPPeer.h; sourceTree = ""; }; E2812BB619F2E9780046DDC1 /* GCDTCPPeer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDTCPPeer.m; sourceTree = ""; }; E2812BB719F2E9780046DDC1 /* GCDTCPServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDTCPServer.h; sourceTree = ""; }; E2812BB819F2E9780046DDC1 /* GCDTCPServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDTCPServer.m; sourceTree = ""; }; E295AF8B1E6B4B1F00EAC2FF /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; E298C46C19ED859F00C76821 /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; E298C47919ED866100C76821 /* GCDNetworking_Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDNetworking_Tests.m; sourceTree = ""; }; E2B03E3A19F493A500D56CA6 /* GCDNetworkingLoggingBridgePrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDNetworkingLoggingBridgePrivate.h; sourceTree = ""; }; E2B3CEF019E917BB003ED065 /* GCDNetworking.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GCDNetworking.app; sourceTree = BUILT_PRODUCTS_DIR; }; E2B3CF1719E91825003ED065 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; E2B3CF1819E91825003ED065 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; E2B3CF1919E91825003ED065 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E2B3CF1A19E91825003ED065 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; E2B3CF2F19E919DD003ED065 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.1.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ E26DC16419E8494700C68DDC /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( E295AF8C1E6B4B1F00EAC2FF /* SystemConfiguration.framework in Frameworks */, E280BF1419E9FF5000D85595 /* CFNetwork.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; E298C46919ED859F00C76821 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( E295AF8D1E6B4B5C00EAC2FF /* SystemConfiguration.framework in Frameworks */, E298C48619ED892500C76821 /* CFNetwork.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; E2B3CEED19E917BB003ED065 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( E280BF1619E9FF5500D85595 /* CFNetwork.framework in Frameworks */, E2B3CF3019E919DD003ED065 /* UIKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ E26ABC2A19EC9F9A00654D9F /* CLT */ = { isa = PBXGroup; children = ( E26ABC2B19EC9F9A00654D9F /* main.m */, ); path = CLT; sourceTree = ""; }; E26DC15E19E8494700C68DDC = { isa = PBXGroup; children = ( E280BF0E19E9F3DF00D85595 /* README.md */, E2812BB019F2E9780046DDC1 /* GCDNetworking */, E26ABC2A19EC9F9A00654D9F /* CLT */, E2B3CF1619E91825003ED065 /* iOS */, E298C47719ED866100C76821 /* Tests */, E26DC19E19E883CC00C68DDC /* Mac Frameworks and Libraries */, E2B3CF2E19E919AC003ED065 /* iOS Frameworks and Libraries */, E26DC16819E8494700C68DDC /* Products */, ); indentWidth = 2; sourceTree = ""; tabWidth = 2; }; E26DC16819E8494700C68DDC /* Products */ = { isa = PBXGroup; children = ( E26DC16719E8494700C68DDC /* GCDNetworking */, E2B3CEF019E917BB003ED065 /* GCDNetworking.app */, E298C46C19ED859F00C76821 /* Tests.xctest */, ); name = Products; sourceTree = ""; }; E26DC19E19E883CC00C68DDC /* Mac Frameworks and Libraries */ = { isa = PBXGroup; children = ( E295AF8B1E6B4B1F00EAC2FF /* SystemConfiguration.framework */, E280BF1319E9FF5000D85595 /* CFNetwork.framework */, ); name = "Mac Frameworks and Libraries"; sourceTree = ""; }; E2812BB019F2E9780046DDC1 /* GCDNetworking */ = { isa = PBXGroup; children = ( E22E917F19F2F0850043457E /* GCDNetworking.h */, E22E917A19F2EBD40043457E /* GCDNetworkingPrivate.h */, E2B03E3A19F493A500D56CA6 /* GCDNetworkingLoggingBridgePrivate.h */, E2812BB119F2E9780046DDC1 /* GCDTCPClient.h */, E2812BB219F2E9780046DDC1 /* GCDTCPClient.m */, E2812BB319F2E9780046DDC1 /* GCDTCPConnection.h */, E2812BB419F2E9780046DDC1 /* GCDTCPConnection.m */, E2812BB519F2E9780046DDC1 /* GCDTCPPeer.h */, E2812BB619F2E9780046DDC1 /* GCDTCPPeer.m */, E2812BB719F2E9780046DDC1 /* GCDTCPServer.h */, E2812BB819F2E9780046DDC1 /* GCDTCPServer.m */, ); path = GCDNetworking; sourceTree = ""; }; E298C47719ED866100C76821 /* Tests */ = { isa = PBXGroup; children = ( E298C47919ED866100C76821 /* GCDNetworking_Tests.m */, ); path = Tests; sourceTree = ""; }; E2B3CF1619E91825003ED065 /* iOS */ = { isa = PBXGroup; children = ( E2B3CF1719E91825003ED065 /* AppDelegate.h */, E2B3CF1819E91825003ED065 /* AppDelegate.m */, E2B3CF1919E91825003ED065 /* Info.plist */, E2B3CF1A19E91825003ED065 /* main.m */, ); path = iOS; sourceTree = ""; }; E2B3CF2E19E919AC003ED065 /* iOS Frameworks and Libraries */ = { isa = PBXGroup; children = ( E2B3CF2F19E919DD003ED065 /* UIKit.framework */, E280BF1519E9FF5500D85595 /* CFNetwork.framework */, ); name = "iOS Frameworks and Libraries"; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ E26DC16619E8494700C68DDC /* GCDNetworking (CLT) */ = { isa = PBXNativeTarget; buildConfigurationList = E26DC16E19E8494700C68DDC /* Build configuration list for PBXNativeTarget "GCDNetworking (CLT)" */; buildPhases = ( E26DC16319E8494700C68DDC /* Sources */, E26DC16419E8494700C68DDC /* Frameworks */, ); buildRules = ( ); dependencies = ( ); name = "GCDNetworking (CLT)"; productName = Logger; productReference = E26DC16719E8494700C68DDC /* GCDNetworking */; productType = "com.apple.product-type.tool"; }; E298C46B19ED859F00C76821 /* GCDNetworking (Tests) */ = { isa = PBXNativeTarget; buildConfigurationList = E298C47619ED859F00C76821 /* Build configuration list for PBXNativeTarget "GCDNetworking (Tests)" */; buildPhases = ( E298C46819ED859F00C76821 /* Sources */, E298C46919ED859F00C76821 /* Frameworks */, ); buildRules = ( ); dependencies = ( ); name = "GCDNetworking (Tests)"; productName = "XLFacility Tests"; productReference = E298C46C19ED859F00C76821 /* Tests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; E2B3CEEF19E917BB003ED065 /* GCDNetworking (iOS) */ = { isa = PBXNativeTarget; buildConfigurationList = E2B3CF1019E917BC003ED065 /* Build configuration list for PBXNativeTarget "GCDNetworking (iOS)" */; buildPhases = ( E2B3CEEC19E917BB003ED065 /* Sources */, E2B3CEED19E917BB003ED065 /* Frameworks */, E2B3CEEE19E917BB003ED065 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = "GCDNetworking (iOS)"; productName = GCDLogger; productReference = E2B3CEF019E917BB003ED065 /* GCDNetworking.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ E26DC15F19E8494700C68DDC /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0830; ORGANIZATIONNAME = ""; TargetAttributes = { E26DC16619E8494700C68DDC = { CreatedOnToolsVersion = 6.1; }; E298C46B19ED859F00C76821 = { CreatedOnToolsVersion = 6.1; TestTargetID = E26ABC0919EC9E2D00654D9F; }; E2B3CEE619E91770003ED065 = { CreatedOnToolsVersion = 6.1; }; E2B3CEEF19E917BB003ED065 = { CreatedOnToolsVersion = 6.1; }; }; }; buildConfigurationList = E26DC16219E8494700C68DDC /* Build configuration list for PBXProject "GCDNetworking" */; compatibilityVersion = "Xcode 8.0"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = E26DC15E19E8494700C68DDC; productRefGroup = E26DC16819E8494700C68DDC /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( E2B3CEE619E91770003ED065 /* Build All */, E26DC16619E8494700C68DDC /* GCDNetworking (CLT) */, E2B3CEEF19E917BB003ED065 /* GCDNetworking (iOS) */, E298C46B19ED859F00C76821 /* GCDNetworking (Tests) */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ E2B3CEEE19E917BB003ED065 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ E26DC16319E8494700C68DDC /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( E2812BB919F2E9780046DDC1 /* GCDTCPClient.m in Sources */, E2812BC119F2E9780046DDC1 /* GCDTCPPeer.m in Sources */, E26ABC2C19EC9F9A00654D9F /* main.m in Sources */, E2812BC519F2E9780046DDC1 /* GCDTCPServer.m in Sources */, E2812BBD19F2E9780046DDC1 /* GCDTCPConnection.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; E298C46819ED859F00C76821 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( E2812BBC19F2E9780046DDC1 /* GCDTCPClient.m in Sources */, E2812BC419F2E9780046DDC1 /* GCDTCPPeer.m in Sources */, E298C47A19ED866100C76821 /* GCDNetworking_Tests.m in Sources */, E2812BC819F2E9780046DDC1 /* GCDTCPServer.m in Sources */, E2812BC019F2E9780046DDC1 /* GCDTCPConnection.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; E2B3CEEC19E917BB003ED065 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( E2812BBA19F2E9780046DDC1 /* GCDTCPClient.m in Sources */, E2812BC219F2E9780046DDC1 /* GCDTCPPeer.m in Sources */, E2B3CF1D19E91825003ED065 /* main.m in Sources */, E2B3CF1B19E91825003ED065 /* AppDelegate.m in Sources */, E2812BC619F2E9780046DDC1 /* GCDTCPServer.m in Sources */, E2812BBE19F2E9780046DDC1 /* GCDTCPConnection.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ E2168F9619EE04B200865350 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = E298C46B19ED859F00C76821 /* GCDNetworking (Tests) */; targetProxy = E2168F9519EE04B200865350 /* PBXContainerItemProxy */; }; E2B3CEEB19E91784003ED065 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = E26DC16619E8494700C68DDC /* GCDNetworking (CLT) */; targetProxy = E2B3CEEA19E91784003ED065 /* PBXContainerItemProxy */; }; E2B3CF2B19E9189C003ED065 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = E2B3CEEF19E917BB003ED065 /* GCDNetworking (iOS) */; targetProxy = E2B3CF2A19E9189C003ED065 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ E26DC16C19E8494700C68DDC /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ENABLE_OBJC_ARC = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1"; ONLY_ACTIVE_ARCH = YES; WARNING_CFLAGS = ( "-Wall", "-Weverything", "-Wshadow", "-Wshorten-64-to-32", "-Wno-direct-ivar-access", "-Wno-objc-missing-property-synthesis", "-Wno-implicit-retain-self", "-Wno-documentation-unknown-command", "-Wno-gnu-statement-expression", "-Wno-reserved-id-macro", "-Wno-cstring-format-directive", "-Wno-partial-availability", ); }; name = Debug; }; E26DC16D19E8494700C68DDC /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ENABLE_OBJC_ARC = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_TREAT_WARNINGS_AS_ERRORS = YES; WARNING_CFLAGS = "-Wall"; }; name = Release; }; E26DC16F19E8494700C68DDC /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { MACOSX_DEPLOYMENT_TARGET = 10.8; PRODUCT_NAME = GCDNetworking; SDKROOT = macosx; }; name = Debug; }; E26DC17019E8494700C68DDC /* Release */ = { isa = XCBuildConfiguration; buildSettings = { MACOSX_DEPLOYMENT_TARGET = 10.8; PRODUCT_NAME = GCDNetworking; SDKROOT = macosx; }; name = Release; }; E298C47419ED859F00C76821 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { FRAMEWORK_SEARCH_PATHS = ( "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", ); MACOSX_DEPLOYMENT_TARGET = 10.8; PRODUCT_NAME = Tests; SDKROOT = macosx; }; name = Debug; }; E298C47519ED859F00C76821 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { FRAMEWORK_SEARCH_PATHS = ( "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", ); MACOSX_DEPLOYMENT_TARGET = 10.8; PRODUCT_NAME = Tests; SDKROOT = macosx; }; name = Release; }; E2B3CEE819E91770003ED065 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; E2B3CEE919E91770003ED065 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; E2B3CF1119E917BC003ED065 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = "iPhone Developer"; DEVELOPMENT_TEAM = 88W3E55T4B; INFOPLIST_FILE = iOS/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; PRODUCT_NAME = GCDNetworking; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; E2B3CF1219E917BC003ED065 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = "iPhone Developer"; DEVELOPMENT_TEAM = 88W3E55T4B; INFOPLIST_FILE = iOS/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; PRODUCT_NAME = GCDNetworking; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ E26DC16219E8494700C68DDC /* Build configuration list for PBXProject "GCDNetworking" */ = { isa = XCConfigurationList; buildConfigurations = ( E26DC16C19E8494700C68DDC /* Debug */, E26DC16D19E8494700C68DDC /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; E26DC16E19E8494700C68DDC /* Build configuration list for PBXNativeTarget "GCDNetworking (CLT)" */ = { isa = XCConfigurationList; buildConfigurations = ( E26DC16F19E8494700C68DDC /* Debug */, E26DC17019E8494700C68DDC /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; E298C47619ED859F00C76821 /* Build configuration list for PBXNativeTarget "GCDNetworking (Tests)" */ = { isa = XCConfigurationList; buildConfigurations = ( E298C47419ED859F00C76821 /* Debug */, E298C47519ED859F00C76821 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; E2B3CEE719E91770003ED065 /* Build configuration list for PBXAggregateTarget "Build All" */ = { isa = XCConfigurationList; buildConfigurations = ( E2B3CEE819E91770003ED065 /* Debug */, E2B3CEE919E91770003ED065 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; E2B3CF1019E917BC003ED065 /* Build configuration list for PBXNativeTarget "GCDNetworking (iOS)" */ = { isa = XCConfigurationList; buildConfigurations = ( E2B3CF1119E917BC003ED065 /* Debug */, E2B3CF1219E917BC003ED065 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = E26DC15F19E8494700C68DDC /* Project object */; } ================================================ FILE: GCDNetworking/GCDNetworking.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme ================================================ ================================================ FILE: GCDNetworking/LICENSE ================================================ Copyright (c) 2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. ================================================ FILE: GCDNetworking/README.md ================================================ Overview ======== [![Build Status](https://travis-ci.org/swisspol/GCDNetworking.svg?branch=master)](https://travis-ci.org/swisspol/GCDNetworking) GCDNetworking is a networking framework based on GCD available under a friendly [New BSD License](LICENSE). Requirements: * OS X 10.8 or later (x86_64) * iOS 5.8 or later (armv7, armv7s or arm64) * ARC memory management only ================================================ FILE: GCDNetworking/Run-Tests.sh ================================================ #!/bin/bash -ex OSX_SDK="macosx" if [ -z "$TRAVIS" ]; then IOS_SDK="iphoneos" else IOS_SDK="iphonesimulator" fi OSX_TARGET="GCDNetworking (CLT)" IOS_TARGET="GCDNetworking (iOS)" CONFIGURATION="Release" BUILD_DIR="/tmp/GCDNetworking" PRODUCT="$BUILD_DIR/$CONFIGURATION/GCDNetworking" # Build for iOS for oldest deployment target rm -rf "$BUILD_DIR" xcodebuild -sdk "$IOS_SDK" -target "$IOS_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$BUILD_DIR" "IPHONEOS_DEPLOYMENT_TARGET=8.0" # Build for iOS for default deployment target rm -rf "$BUILD_DIR" xcodebuild -sdk "$IOS_SDK" -target "$IOS_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$BUILD_DIR" # Build for OS X for oldest deployment target rm -rf "$BUILD_DIR" xcodebuild -sdk "$OSX_SDK" -target "$OSX_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$BUILD_DIR" "MACOSX_DEPLOYMENT_TARGET=10.8" # Build for OS X for default deployment target rm -rf "$BUILD_DIR" xcodebuild -sdk "$OSX_SDK" -target "$OSX_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$BUILD_DIR" # Run tests xcodebuild test -scheme "Tests" # Done echo "\nAll tests completed successfully!" ================================================ FILE: GCDNetworking/Tests/GCDNetworking_Tests.m ================================================ /* Copyright (c) 2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" #pragma clang diagnostic ignored "-Wsign-compare" #import #import "GCDNetworking.h" #define kTestPort 3333 typedef void (^TCPServerConnectionBlock)(GCDTCPPeerConnection* connection); @interface TCPServer : GCDTCPServer @end @implementation TCPServer { @private TCPServerConnectionBlock _block; } - (id)initWithPort:(NSUInteger)port connectionBlock:(TCPServerConnectionBlock)block { if ((self = [super initWithConnectionClass:[GCDTCPServerConnection class] port:port])) { _block = block; } return self; } - (void)willOpenConnection:(GCDTCPPeerConnection*)connection { [super willOpenConnection:connection]; _block(connection); } @end @interface GCDNetworking_Tests : XCTestCase @end @implementation GCDNetworking_Tests - (void)testReading { __block GCDTCPPeerConnection* inConnection = nil; TCPServer* server = [[TCPServer alloc] initWithPort:kTestPort connectionBlock:^(GCDTCPPeerConnection* connection) { inConnection = connection; }]; XCTAssertTrue([server start]); GCDTCPClient* client = [[GCDTCPClient alloc] initWithConnectionClass:[GCDTCPClientConnection class] host:@"localhost" port:kTestPort]; XCTAssertTrue([client start]); sleep(1); XCTAssertNotNil(inConnection); GCDTCPClientConnection* outConnection = client.connection; XCTAssertNotNil(outConnection); NSData* data1 = [inConnection readDataWithTimeout:3.0]; XCTAssertNil(data1); XCTestExpectation* expectation = [self expectationWithDescription:@""]; [outConnection writeDataAsynchronously:[@"Hello World!\n" dataUsingEncoding:NSUTF8StringEncoding] completion:^(BOOL success) { XCTAssertTrue(success); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:10.0 handler:NULL]; NSData* data2 = [inConnection readDataWithTimeout:3.0]; XCTAssertNotNil(data2); NSString* string = [[NSString alloc] initWithData:data2 encoding:NSUTF8StringEncoding]; XCTAssertEqualObjects(string, @"Hello World!\n"); [outConnection close]; [inConnection close]; [client stop]; [server stop]; } - (void)testWriting { __block GCDTCPPeerConnection* inConnection = nil; TCPServer* server = [[TCPServer alloc] initWithPort:kTestPort connectionBlock:^(GCDTCPPeerConnection* connection) { inConnection = connection; }]; XCTAssertTrue([server start]); GCDTCPClient* client = [[GCDTCPClient alloc] initWithConnectionClass:[GCDTCPClientConnection class] host:@"localhost" port:kTestPort]; XCTAssertTrue([client start]); sleep(1); XCTAssertNotNil(inConnection); GCDTCPClientConnection* outConnection = client.connection; XCTAssertNotNil(outConnection); XCTestExpectation* expectation = [self expectationWithDescription:@""]; [inConnection readDataAsynchronously:^(NSData* data) { XCTAssertNotNil(data); NSString* string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; XCTAssertEqualObjects(string, @"Hello World!\n"); [expectation fulfill]; }]; BOOL success = [outConnection writeData:[@"Hello World!\n" dataUsingEncoding:NSUTF8StringEncoding] withTimeout:3.0]; XCTAssertTrue(success); [self waitForExpectationsWithTimeout:10.0 handler:NULL]; [outConnection close]; [inConnection close]; [client stop]; [server stop]; } @end ================================================ FILE: GCDNetworking/iOS/AppDelegate.h ================================================ /* Copyright (c) 2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #import @interface AppDelegate : UIResponder @property(retain, nonatomic) UIWindow* window; @end ================================================ FILE: GCDNetworking/iOS/AppDelegate.m ================================================ /* Copyright (c) 2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #import "AppDelegate.h" #import "GCDNetworking.h" @interface Connection : GCDTCPServerConnection @end @implementation Connection - (void)didOpen { [super didOpen]; NSString* welcome = @"Hello World!\n"; [self writeDataAsynchronously:[welcome dataUsingEncoding:NSUTF8StringEncoding] completion:^(BOOL success) { [self close]; }]; } @end @implementation AppDelegate - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { _window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; _window.backgroundColor = [UIColor whiteColor]; _window.rootViewController = [[UIViewController alloc] init]; _window.rootViewController.view = [[UIView alloc] init]; [_window makeKeyAndVisible]; GCDTCPServer* server = [[GCDTCPServer alloc] initWithConnectionClass:[Connection class] port:2323]; if (![server start]) { abort(); } NSLog(@"TCP server running on %@", GCDTCPServerGetPrimaryIPAddress(false)); return YES; } @end ================================================ FILE: GCDNetworking/iOS/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleDisplayName ${PRODUCT_NAME} CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier net.pol-online.${PRODUCT_NAME:rfc1034identifier} CFBundleInfoDictionaryVersion 6.0 CFBundleName ${PRODUCT_NAME} CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleVersion 1.0 LSRequiresIPhoneOS UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: GCDNetworking/iOS/main.m ================================================ /* Copyright (c) 2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #import "AppDelegate.h" int main(int argc, char* argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } ================================================ FILE: GCDTelnetServer/GCDTelnetConnection.h ================================================ /* Copyright (c) 2012-2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #import "GCDNetworking.h" /** * The GCDTelnetConnection class is the class used to implement connections * for GCDTelnetServer. */ @interface GCDTelnetConnection : GCDTCPServerConnection /* * Returns the type of the remote terminal. * * @warning This will only be non-nil if the connected peer responded to the * corresponding Telnet command. */ @property(nonatomic, readonly) NSString* terminalType; /* * Returns YES if the remote terminal is a color one. */ @property(nonatomic, readonly, getter=isColorTerminal) BOOL colorTerminal; /* * Sets the prompt to show at the beginning of a new line. * * Set this value to nil to remove the prompt entirely. * * The default value is "> ". * * @warning Setting this value to a non-ASCII string or changing it outside * of the scope of the GCDTelnetServer start handler is not supported. */ @property(nonatomic, copy) NSString* prompt; /* * Sets the placeholder string inserted when the tab key is pressed. * * The default value is "\t". * * @warning Setting this value to a non-ASCII string or changing it outside * of the scope of the GCDTelnetServer start handler is not supported. */ @property(nonatomic, copy) NSString* tabPlaceholder; /* * Sets the maximum number of lines preserved by the history. * * Set this value to 0 to disable the history entirely. * * The default value is NSIntegerMax i.e. unlimited. * * @warning Changing this value outside of the scope of the GCDTelnetServer * start handler is not supported. */ @property(nonatomic) NSUInteger maxHistorySize; @end @interface GCDTelnetConnection (Subclassing) /* * Direct access to the line buffer used by the connection. * * @warning This string must only contain ASCII characters. */ @property(nonatomic, readonly) NSMutableString* lineBuffer; /* * Called whenever a new connection has started with a remote terminal. * * The default implementation calls the GCDTelnetServer start handler. */ - (NSString*)start; /* * Called when the arrow up key is pressed. * * The default implementation navigates the history towards older entries. */ - (NSData*)processCursorUp; /* * Called when the arrow up key is pressed. * * The default implementation navigates the history towards newer entries. */ - (NSData*)processCursorDown; /* * Called when the arrow right key is pressed. * * The default implementation just beeps. */ - (NSData*)processCursorForward; /* * Called when the arrow left key is pressed. * * The default implementation just beeps. */ - (NSData*)processCursorBack; /* * Called when an unimplemented ANSI escape sequence has been received. * * The default implementation just beeps. */ - (NSData*)processOtherANSIEscapeSequence:(NSData*)data; /* * Called when the tab key is pressed. * * The default implementation inserts the tab placeholder string. */ - (NSData*)processTab; /* * Called when the delete key is pressed. * * The default implementation deletes the last character. */ - (NSData*)processDelete; /* * Called when the return key is pressed. * * The default implementation calls -processLine: and updates the history. */ - (NSData*)processCarriageReturn; /* * Called when any other ASCII character has been received. * * The default implementation inserts the character. */ - (NSData*)processOtherASCIICharacter:(unsigned char)character; /* * Called when a non-ASCII character has been received. * * The default implementation does nothing. */ - (NSData*)processNonASCIICharacter:(unsigned char)character; /* * Called whenever input data has been received from the remote terminal. * * The default implementation parses the data and calls one of the other * methods. */ - (NSData*)processRawInput:(NSData*)input; /* * Called whenever a line has been fully received from the remote terminal. * * The default implementation calls the GCDTelnetServer line handler. */ - (NSString*)processLine:(NSString*)line; @end @interface GCDTelnetConnection (Extensions) /** * Parses a line like a command line interface extracting the command and * arguments. * * This methods supports quoted arguments using single or double quotes. */ - (NSArray*)parseLineAsCommandAndArguments:(NSString*)line; /** * Returns a sanitized version of a string suitable for sending to the remote * terminal. * * The current implementation replaces all newline characters by carriage * returns. */ - (NSString*)sanitizeStringForTerminal:(NSString*)string; /* * Convenience methods that writes a string to the connection using lossy * ASCII encoding. */ - (BOOL)writeASCIIString:(NSString*)string withTimeout:(NSTimeInterval)timeout; /* * Convenience methods that writes a formatted string to the connection using * lossy ASCII encoding. */ - (void)writeASCIIStringAsynchronously:(NSString*)string completion:(void (^)(BOOL success))completion; @end ================================================ FILE: GCDTelnetServer/GCDTelnetConnection.m ================================================ /* Copyright (c) 2012-2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #import "GCDTelnetPrivate.h" #define kTelnetCommandStringsOffset kTelnetCommand_SE #define kCSIPrefix "\x1b[" // https://en.wikipedia.org/wiki/ANSI_escape_code #define kCarriageReturnString @"\r\n" #define kSynchronousCommunicationTimeout 3.0 static const char* _telnetCommandStrings[] = { "SE", "NOP", "DM", "BRK", "IP", "AO", "AYT", "EC", "EL", "GA", "SB", "WILL", "WONT", "DO", "DONT", "IAC"}; static const char* _telnetOptionStrings[] = { NULL, "Echo", NULL, "Supress Go Ahead", NULL, "Status", "Timing Mark", NULL, NULL, NULL, // 0-9 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 10-29 NULL, NULL, NULL, NULL, "Terminal Type", NULL, NULL, NULL, NULL, NULL, // 20-29 NULL, "Window Size", "Terminal Speed", "Remote Flow Control", "Linemode", NULL, "Environment Variables", NULL, NULL, NULL // 30-39 }; static NSRegularExpression* _commandLineParser = nil; @interface GCDTelnetConnection () { @private NSMutableString* _lineBuffer; NSMutableArray* _historyLines; NSUInteger _historyIndex; NSString* _savedLine; } @end @implementation GCDTelnetConnection + (void)initialize { if (self == [GCDTelnetConnection class]) { _commandLineParser = [[NSRegularExpression alloc] initWithPattern:@"(\"[^\"]+\"|'[^']+'|[^\\s\"]+)" options:0 error:NULL]; _LOG_DEBUG_CHECK(_commandLineParser); } } - (instancetype)initWithSocket:(int)socket { if ((self = [super initWithSocket:socket])) { _lineBuffer = [[NSMutableString alloc] init]; _prompt = @"> "; _tabPlaceholder = @"\t"; _maxHistorySize = NSIntegerMax; _historyLines = [[NSMutableArray alloc] init]; _historyIndex = NSNotFound; } return self; } static NSString* _StringFromIACBuffer(const unsigned char* buffer, NSUInteger length) { _LOG_DEBUG_CHECK(buffer[0] == kTelnetCommand_IAC); _LOG_DEBUG_CHECK(length >= 3); NSMutableString* string = [[NSMutableString alloc] init]; NSUInteger i = 0; while (i < length) { [string appendFormat:@"%s, %s", _telnetCommandStrings[buffer[i] - kTelnetCommandStringsOffset], _telnetCommandStrings[buffer[i + 1] - kTelnetCommandStringsOffset]]; i += 2; if ((buffer[i - 1] >= kTelnetCommand_WILL) && (buffer[i - 1] <= kTelnetCommand_DONT)) { [string appendFormat:@", %s", _telnetOptionStrings[buffer[i]]]; ++i; } else if (buffer[i - 1] == kTelnetCommand_SB) { [string appendFormat:@", %s,", _telnetOptionStrings[buffer[i]]]; ++i; while ((buffer[i] != kTelnetCommand_IAC) || (buffer[i + 1] != kTelnetCommand_SE)) { [string appendFormat:@" (%i)'%c'", buffer[i], buffer[i]]; ++i; } [string appendFormat:@", %s, %s", _telnetCommandStrings[buffer[i] - kTelnetCommandStringsOffset], _telnetCommandStrings[buffer[i + 1] - kTelnetCommandStringsOffset]]; i += 2; } else { [string appendFormat:@", %i", buffer[i]]; ++i; } } return string; } - (NSData*)_sendIACBuffer:(const unsigned char*)buffer length:(NSUInteger)length { _LOG_DEBUG_CHECK(buffer[0] == kTelnetCommand_IAC); if (![self writeBuffer:buffer length:length withTimeout:kSynchronousCommunicationTimeout]) { _LOG_ERROR(@"Failed sending Telnet command: %@", _StringFromIACBuffer(buffer, length)); return nil; } _LOG_DEBUG(@"Telnet IAC (->) %@", _StringFromIACBuffer(buffer, length)); NSData* data = [self readDataWithTimeout:kSynchronousCommunicationTimeout]; if (!data.length) { return nil; } const unsigned char* bytes = data.bytes; if (bytes[0] != kTelnetCommand_IAC) { _LOG_ERROR(@"Invalid Telnet command received"); return nil; } _LOG_DEBUG(@"Telnet IAC (<-) %@", _StringFromIACBuffer(data.bytes, data.length)); return data; } - (BOOL)_setIACOption:(unsigned char)option { unsigned char buffer[] = {kTelnetCommand_IAC, kTelnetCommand_WILL, option}; NSData* data = [self _sendIACBuffer:buffer length:sizeof(buffer)]; if (data.length != 3) { return NO; } const unsigned char* bytes = data.bytes; if ((bytes[1] != kTelnetCommand_DO) || (bytes[2] != option)) { _LOG_ERROR(@"Failed setting Telnet option: %@", _StringFromIACBuffer(data.bytes, data.length)); return NO; } return YES; } - (NSString*)_retrieveTerminalType { unsigned char buffer1[] = {kTelnetCommand_IAC, kTelnetCommand_DO, kTelnetOption_TerminalType}; NSData* data1 = [self _sendIACBuffer:buffer1 length:sizeof(buffer1)]; if (data1.length != 3) { return nil; } const unsigned char* bytes1 = data1.bytes; if ((bytes1[1] != kTelnetCommand_WILL) || (bytes1[2] != kTelnetOption_TerminalType)) { return nil; } unsigned char buffer2[] = {kTelnetCommand_IAC, kTelnetCommand_SB, kTelnetOption_TerminalType, 1, kTelnetCommand_IAC, kTelnetCommand_SE}; NSData* data2 = [self _sendIACBuffer:buffer2 length:sizeof(buffer2)]; if (data2.length < 6) { return nil; } const unsigned char* bytes2 = data2.bytes; NSUInteger length2 = data2.length; if ((bytes2[1] != kTelnetCommand_SB) || (bytes2[2] != kTelnetOption_TerminalType) || (bytes2[3] != 0)) { return nil; } if ((bytes2[length2 - 2] != kTelnetCommand_IAC) || (bytes2[length2 - 1] != kTelnetCommand_SE)) { return nil; } return [[NSString alloc] initWithBytes:&bytes2[4] length:(length2 - 6) encoding:NSASCIIStringEncoding]; } - (void)_readInput { [self readDataAsynchronously:^(NSData* data) { if (data.length) { data = [self processRawInput:data]; if (data) { [self writeDataAsynchronously:data completion:^(BOOL success) { if (success) { [self _readInput]; } else { [self close]; } }]; } else { [self _readInput]; } } else { [self close]; } }]; } - (void)didOpen { [self _setIACOption:kTelnetOption_SuppressGoAhead]; [self _setIACOption:kTelnetOption_Echo]; _terminalType = [self _retrieveTerminalType]; if (_terminalType.length) { NSRange range = [_terminalType rangeOfString:@"color" options:NSCaseInsensitiveSearch]; _colorTerminal = (range.location != NSNotFound); } else { _LOG_ERROR(@"Failed retrieving Telnet terminal type from %@", self.remoteIPAddress); } NSMutableString* string = [[NSMutableString alloc] init]; NSString* start = [self start]; if (start) { [string appendString:start]; } if (_prompt) { [string appendString:_prompt]; } if (string.length) { [self writeASCIIStringAsynchronously:string completion:^(BOOL success) { if (success) { [self _readInput]; } else { [self close]; } }]; } else { [self _readInput]; } } @end @implementation GCDTelnetConnection (Subclassing) - (NSMutableString*)lineBuffer { return _lineBuffer; } - (NSString*)start { GCDTelnetStartHandler handler = [(GCDTelnetServer*)self.peer startHandler]; if (handler) { return [self sanitizeStringForTerminal:handler(self)]; } return nil; } - (NSData*)_beepData { unsigned char buffer[] = {kControlCode_BEL}; return [NSData dataWithBytes:buffer length:sizeof(buffer)]; } - (NSMutableData*)_emptyLineData { unsigned char buffer[] = { kCSIPrefix[0], kCSIPrefix[1], '2', 'K', // Clear entire line kCSIPrefix[0], kCSIPrefix[1], '1', 'G' // Move cursor to column 1 }; NSMutableData* data = [NSMutableData dataWithBytes:buffer length:sizeof(buffer)]; if (_prompt) { [data appendData:(id)[_prompt dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]]; } return data; } - (NSData*)_stringLineData:(NSString*)string { [_lineBuffer setString:string]; NSMutableData* data = [self _emptyLineData]; [data appendData:(id)[string dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]]; return data; } - (NSData*)processCursorUp { if ((_historyIndex == NSNotFound) && _historyLines.count) { _savedLine = [_lineBuffer copy]; _historyIndex = _historyLines.count - 1; } else if ((_historyIndex != NSNotFound) && (_historyIndex > 0)) { _historyIndex -= 1; } else { return [self _beepData]; } return [self _stringLineData:_historyLines[_historyIndex]]; } - (NSData*)processCursorDown { if (_historyIndex == NSNotFound) { return [self _beepData]; } NSString* string; if (_historyIndex < _historyLines.count - 1) { _historyIndex += 1; string = _historyLines[_historyIndex]; } else { string = _savedLine; _historyIndex = NSNotFound; _savedLine = nil; } return [self _stringLineData:string]; } - (NSData*)processCursorForward { return [self _beepData]; } - (NSData*)processCursorBack { return [self _beepData]; } - (NSData*)processOtherANSIEscapeSequence:(NSData*)data { return [self _beepData]; } - (NSData*)processTab { [_lineBuffer appendString:_tabPlaceholder]; return [_tabPlaceholder dataUsingEncoding:NSASCIIStringEncoding]; } - (NSData*)processDelete { if (_lineBuffer.length) { [_lineBuffer deleteCharactersInRange:NSMakeRange(_lineBuffer.length - 1, 1)]; unsigned char buffer[] = {0x08, 0x20, 0x08}; // http://stackoverflow.com/questions/1689554/telnet-server-backspace-delete-not-working return [NSData dataWithBytes:buffer length:sizeof(buffer)]; } return [self _beepData]; } - (NSData*)processCarriageReturn { NSData* data; NSString* line = [_lineBuffer copy]; NSString* result = [self processLine:line]; if (result) { if (_maxHistorySize && line.length) { if (!_historyLines.count || ![line isEqualToString:(id)[_historyLines lastObject]]) { [_historyLines addObject:line]; if (_historyLines.count > _maxHistorySize) { [_historyLines removeObjectsInRange:NSMakeRange(0, _historyLines.count - _maxHistorySize)]; } } } _historyIndex = NSNotFound; _savedLine = nil; NSMutableString* string = [[NSMutableString alloc] init]; [string appendString:kCarriageReturnString]; [string appendString:result]; if (_prompt) { [string appendString:_prompt]; } data = [string dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]; } else { data = [self _emptyLineData]; } [_lineBuffer setString:@""]; return data; } - (NSData*)processOtherASCIICharacter:(unsigned char)character { [_lineBuffer appendFormat:@"%c", character]; return [NSData dataWithBytes:&character length:1]; } - (NSData*)processNonASCIICharacter:(unsigned char)character { return nil; } - (NSData*)processRawInput:(NSData*)input { const unsigned char* bytes = input.bytes; NSUInteger length = input.length; if ((length > 2) && (bytes[0] == kCSIPrefix[0]) && (bytes[1] == kCSIPrefix[1])) { if (length == 3) { switch (bytes[2]) { case 'A': return [self processCursorUp]; case 'B': return [self processCursorDown]; case 'C': return [self processCursorForward]; case 'D': return [self processCursorBack]; } } return [self processOtherANSIEscapeSequence:input]; } NSMutableData* output = [[NSMutableData alloc] init]; while (length) { NSData* data = nil; if ((length >= 2) && (bytes[0] == kControlCode_CR) && (bytes[1] == kControlCode_NUL)) { data = [self processCarriageReturn]; bytes += 2; length -= 2; } else { switch (bytes[0]) { case 0x09: data = [self processTab]; break; case 0x7F: data = [self processDelete]; break; default: if (bytes[0] <= 127) { data = [self processOtherASCIICharacter:bytes[0]]; } else { data = [self processNonASCIICharacter:bytes[0]]; } break; } bytes += 1; length -= 1; } if (data.length) { [output appendData:data]; } } return output; } - (NSString*)processLine:(NSString*)line { GCDTelnetLineHandler handler = [(GCDTelnetServer*)self.peer lineHandler]; if (handler) { return [self sanitizeStringForTerminal:handler(self, line)]; } return nil; } @end @implementation GCDTelnetConnection (Extensions) - (NSArray*)parseLineAsCommandAndArguments:(NSString*)line { NSMutableArray* array = [[NSMutableArray alloc] init]; [_commandLineParser enumerateMatchesInString:line options:0 range:NSMakeRange(0, line.length) usingBlock:^(NSTextCheckingResult* result, NSMatchingFlags flags, BOOL* stop) { NSString* string = [line substringWithRange:result.range]; if (([string hasPrefix:@"\""] && [string hasSuffix:@"\""]) || ([string hasPrefix:@"'"] && [string hasSuffix:@"'"])) { // TODO: Strip quotes directly in Regex [array addObject:[string substringWithRange:NSMakeRange(1, string.length - 2)]]; } else { [array addObject:string]; } }]; return array; } - (NSString*)sanitizeStringForTerminal:(NSString*)string { return [string stringByReplacingOccurrencesOfString:@"\n" withString:kCarriageReturnString]; } - (BOOL)writeASCIIString:(NSString*)string withTimeout:(NSTimeInterval)timeout { return [self writeData:[string dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES] withTimeout:timeout]; } - (void)writeASCIIStringAsynchronously:(NSString*)string completion:(void (^)(BOOL success))completion { [self writeDataAsynchronously:[string dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES] completion:completion]; } @end ================================================ FILE: GCDTelnetServer/GCDTelnetLogging.h ================================================ /* Copyright (c) 2012-2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #import /** * Automatically detect if XLFacility is available and if so use it as a * logging facility. */ #if defined(__has_include) && __has_include("XLFacilityMacros.h") #define __GCDNETWORKING_LOGGING_FACILITY_XLFACILITY__ #undef XLOG_TAG #define XLOG_TAG @"gcdnetworking.internal" #import "XLFacilityMacros.h" #define GN_LOG_DEBUG(...) XLOG_DEBUG(__VA_ARGS__) #define GN_LOG_VERBOSE(...) XLOG_VERBOSE(__VA_ARGS__) #define GN_LOG_INFO(...) XLOG_INFO(__VA_ARGS__) #define GN_LOG_WARNING(...) XLOG_WARNING(__VA_ARGS__) #define GN_LOG_ERROR(...) XLOG_ERROR(__VA_ARGS__) #define GN_LOG_EXCEPTION(__EXCEPTION__) XLOG_EXCEPTION(__EXCEPTION__) #define GN_DCHECK(__CONDITION__) XLOG_DEBUG_CHECK(__CONDITION__) #define GN_DNOT_REACHED() XLOG_DEBUG_UNREACHABLE() /** * Automatically detect if CocoaLumberJack is available and if so use * it as a logging facility. */ #elif defined(__has_include) && __has_include("DDLogMacros.h") #import "DDLogMacros.h" #define __GCDNETWORKING_LOGGING_FACILITY_COCOALUMBERJACK__ #undef LOG_LEVEL_DEF #define LOG_LEVEL_DEF GCDNetworkingLogLevel extern int GCDNetworkingLogLevel; #define GN_LOG_DEBUG(...) DDLogDebug(__VA_ARGS__) #define GN_LOG_VERBOSE(...) DDLogVerbose(__VA_ARGS__) #define GN_LOG_INFO(...) DDLogInfo(__VA_ARGS__) #define GN_LOG_WARNING(...) DDLogWarn(__VA_ARGS__) #define GN_LOG_ERROR(...) DDLogError(__VA_ARGS__) #define GN_LOG_EXCEPTION(__EXCEPTION__) DDLogError(@"%@", __EXCEPTION__) /** * Check if a custom logging facility should be used instead. */ #elif defined(__GCDNETWORKING_LOGGING_HEADER__) #define __GCDNETWORKING_LOGGING_FACILITY_CUSTOM__ #import __GCDNETWORKING_LOGGING_HEADER__ /** * If all of the above fail, then use GCDNetworking built-in * logging facility. */ #else #define __GCDNETWORKING_LOGGING_FACILITY_BUILTIN__ typedef NS_ENUM(int, GCDNetworkingLoggingLevel) { kGCDNetworkingLoggingLevel_Debug = 0, kGCDNetworkingLoggingLevel_Verbose, kGCDNetworkingLoggingLevel_Info, kGCDNetworkingLoggingLevel_Warning, kGCDNetworkingLoggingLevel_Error, kGCDNetworkingLoggingLevel_Exception, }; extern GCDNetworkingLoggingLevel GCDNetworkingLogLevel; extern void GCDNetworkingLogMessage(GCDNetworkingLoggingLevel level, NSString* format, ...) NS_FORMAT_FUNCTION(2, 3); #if DEBUG #define GN_LOG_DEBUG(...) \ do { \ if (GCDNetworkingLogLevel <= kGCDNetworkingLoggingLevel_Debug) GCDNetworkingLogMessage(kGCDNetworkingLoggingLevel_Debug, __VA_ARGS__); \ } while (0) #else #define GN_LOG_DEBUG(...) #endif #define GN_LOG_VERBOSE(...) \ do { \ if (GCDNetworkingLogLevel <= kGCDNetworkingLoggingLevel_Verbose) GCDNetworkingLogMessage(kGCDNetworkingLoggingLevel_Verbose, __VA_ARGS__); \ } while (0) #define GN_LOG_INFO(...) \ do { \ if (GCDNetworkingLogLevel <= kGCDNetworkingLoggingLevel_Info) GCDNetworkingLogMessage(kGCDNetworkingLoggingLevel_Info, __VA_ARGS__); \ } while (0) #define GN_LOG_WARNING(...) \ do { \ if (GCDNetworkingLogLevel <= kGCDNetworkingLoggingLevel_Warning) GCDNetworkingLogMessage(kGCDNetworkingLoggingLevel_Warning, __VA_ARGS__); \ } while (0) #define GN_LOG_ERROR(...) \ do { \ if (GCDNetworkingLogLevel <= kGCDNetworkingLoggingLevel_Error) GCDNetworkingLogMessage(kGCDNetworkingLoggingLevel_Error, __VA_ARGS__); \ } while (0) #define GN_LOG_EXCEPTION(__EXCEPTION__) \ do { \ if (GCDNetworkingLogLevel <= kGCDNetworkingLoggingLevel_Exception) GCDNetworkingLogMessage(kGCDNetworkingLoggingLevel_Exception, @"%@", __EXCEPTION__); \ } while (0) #endif /** * Consistency check macros used when building Debug only. */ #if !defined(GN_DCHECK) || !defined(GN_DNOT_REACHED) #if DEBUG #define GN_DCHECK(__CONDITION__) \ do { \ if (!(__CONDITION__)) { \ abort(); \ } \ } while (0) #define GN_DNOT_REACHED() abort() #else #define GN_DCHECK(__CONDITION__) #define GN_DNOT_REACHED() #endif #endif ================================================ FILE: GCDTelnetServer/GCDTelnetLoggingBridgePrivate.h ================================================ /* Copyright (c) 2012-2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #import /** * Automatically detect if XLFacility is available. */ #if defined(__has_include) && __has_include("XLFacilityMacros.h") #define __LOGGING_BRIDGE_XLFACILITY__ #import "XLFacilityMacros.h" #define _LOG_DEBUG(...) XLOG_DEBUG(__VA_ARGS__) #define _LOG_VERBOSE(...) XLOG_VERBOSE(__VA_ARGS__) #define _LOG_INFO(...) XLOG_INFO(__VA_ARGS__) #define _LOG_WARNING(...) XLOG_WARNING(__VA_ARGS__) #define _LOG_ERROR(...) XLOG_ERROR(__VA_ARGS__) #define _LOG_EXCEPTION(__EXCEPTION__) XLOG_EXCEPTION(__EXCEPTION__) #define _LOG_DEBUG_CHECK(__CONDITION__) XLOG_DEBUG_CHECK(__CONDITION__) #define _LOG_DEBUG_UNREACHABLE() XLOG_DEBUG_UNREACHABLE() /** * Automatically detect if CocoaLumberJack is available. */ #elif defined(__has_include) && __has_include("DDLogMacros.h") #import "DDLogMacros.h" #define __LOGGING_BRIDGE_COCOALUMBERJACK__ #undef LOG_LEVEL_DEF #define LOG_LEVEL_DEF _LoggingMinLevel extern int _LoggingMinLevel; #define _LOG_DEBUG(...) DDLogDebug(__VA_ARGS__) #define _LOG_VERBOSE(...) DDLogVerbose(__VA_ARGS__) #define _LOG_INFO(...) DDLogInfo(__VA_ARGS__) #define _LOG_WARNING(...) DDLogWarn(__VA_ARGS__) #define _LOG_ERROR(...) DDLogError(__VA_ARGS__) #define _LOG_EXCEPTION(__EXCEPTION__) DDLogError(@"%@", __EXCEPTION__) /** * Check if a custom logging header should be used instead. */ #elif defined(__LOGGING_CUSTOM_HEADER__) #define __LOGGING_BRIDGE_CUSTOM__ #import __LOGGING_CUSTOM_HEADER__ /** * If all of the above fail, fall back to NSLog(). */ #else #define __LOGGING_BRIDGE_BUILTIN__ #if DEBUG #define _LOG_DEBUG(...) NSLog(__VA_ARGS__) #define _LOG_VERBOSE(...) NSLog(__VA_ARGS__) #define _LOG_INFO(...) NSLog(__VA_ARGS__) #else #define _LOG_DEBUG(...) #define _LOG_VERBOSE(...) #define _LOG_INFO(...) #endif #define _LOG_WARNING(...) NSLog(__VA_ARGS__) #define _LOG_ERROR(...) NSLog(__VA_ARGS__) #define _LOG_EXCEPTION(__EXCEPTION__) NSLog(@"%@", __EXCEPTION__) #endif /** * Consistency check macros. */ #if !defined(_LOG_CHECK) #define _LOG_CHECK(__CONDITION__) \ do { \ if (!(__CONDITION__)) { \ abort(); \ } \ } while (0) #endif #if !defined(_LOG_DEBUG_CHECK) #if DEBUG #define _LOG_DEBUG_CHECK(__CONDITION__) _LOG_CHECK(__CONDITION__) #else #define _LOG_DEBUG_CHECK(__CONDITION__) #endif #endif #if !defined(_LOG_UNREACHABLE) #define _LOG_UNREACHABLE() abort() #endif #if !defined(_LOG_DEBUG_UNREACHABLE) #if DEBUG #define _LOG_DEBUG_UNREACHABLE() _LOG_UNREACHABLE() #else #define _LOG_DEBUG_UNREACHABLE() #endif #endif ================================================ FILE: GCDTelnetServer/GCDTelnetPrivate.h ================================================ /* Copyright (c) 2012-2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ /** * All GCDTelnetServer headers. */ #import "GCDTelnetServer.h" #import "GCDTelnetLoggingBridgePrivate.h" /** * GCDTelnetServer internal constants and APIs. */ // Network Virtual Terminal control codes typedef NS_ENUM(unsigned char, ControlCode) { // Required kControlCode_NUL = 0, // NULL - No operation kControlCode_LF = 10, // Line Feed - Moves the printer to the next print line, keeping the same horizontal position. kControlCode_CR = 13, // Carriage Return - Moves the printer to the left margin of the current line. // Optional kControlCode_BEL = 7, // BELL - Produces an audible or visible signal (which does NOT move the print head). kControlCode_BS = 8, // Back Space - Moves the print head one character position towards the left margin. (On a printing device, this mechanism was commonly used to form composite characters by printing two basic characters on top of each other.) kControlCode_HT = 9, // Horizontal Tab - Moves the printer to the next horizontal tab stop. It remains unspecified how either party determines or establishes where such tab stops are located. kControlCode_VT = 11, // Vertical Tab - Moves the printer to the next vertical tab stop. It remains unspecified how either party determines or establishes where such tab stops are located. kControlCode_FF = 12 // Form Feed - Moves the printer to the top of the next page, keeping the same horizontal position. (On visual displays, this commonly clears the screen and moves the cursor to the top left corner.) }; // Telnet commands typedef NS_ENUM(unsigned char, TelnetCommand) { kTelnetCommand_SE = 240, // End of subnegotiation parameters kTelnetCommand_NOP = 241, // No operation kTelnetCommand_DM = 242, // Data mark - Indicates the position of a Synch event within the data stream. This should always be accompanied by a TCP urgent notification. kTelnetCommand_BRK = 243, // Break - Indicates that the "break" or "attention" key was hi. kTelnetCommand_IP = 244, // Suspend - Interrupt or abort the process to which the NVT is connected. kTelnetCommand_AO = 245, // Abort output - Allows the current process to run to completion but does not send its output to the user. kTelnetCommand_AYT = 246, // Are you there - Send back to the NVT some visible evidence that the AYT was received. kTelnetCommand_EC = 247, // Erase character - The receiver should delete the last preceding undeleted character from the data stream. kTelnetCommand_EL = 248, // Erase line - Delete characters from the data stream back to but not including the previous CRLF. kTelnetCommand_GA = 249, // Go ahead - Under certain circumstances used to tell the other end that it can transmit. kTelnetCommand_SB = 250, // Subnegotiation - Subnegotiation of the indicated option follows. kTelnetCommand_WILL = 251, // will - Indicates the desire to begin performing, or confirmation that you are now performing, the indicated option. kTelnetCommand_WONT = 252, // wont - Indicates the refusal to perform, or continue performing, the indicated option. kTelnetCommand_DO = 253, // do - Indicates the request that the other party perform, or confirmation that you are expecting the other party to perform, the indicated option. kTelnetCommand_DONT = 254, // dont - Indicates the demand that the other party stop performing, or confirmation that you are no longer expecting the other party to perform, the indicated option. kTelnetCommand_IAC = 255 // Interpret as command - Interpret as a command }; typedef NS_ENUM(unsigned char, TelnetOption) { kTelnetOption_Echo = 1, // RFC 857 kTelnetOption_SuppressGoAhead = 3, // RFC 858 kTelnetOption_Status = 5, // RFC 859 kTelnetOption_TimingMark = 6, // RFC 860 kTelnetOption_TerminalType = 24, // RFC 1091 kTelnetOption_WindowSize = 31, // RFC 1073 kTelnetOption_TerminalSpeed = 32, // RFC 1079 kTelnetOption_RemoteFlowControl = 33, // RFC 1372 kTelnetOption_Linemode = 34, // RFC 1184 kTelnetOption_EnvironmentVariables = 36 // RFC 1408 }; @interface GCDTelnetServer () @property(nonatomic, readonly) GCDTelnetStartHandler startHandler; @property(nonatomic, readonly) GCDTelnetLineHandler lineHandler; @end ================================================ FILE: GCDTelnetServer/GCDTelnetServer.h ================================================ /* Copyright (c) 2012-2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #import "GCDTelnetConnection.h" /** * The GCDTelnetStartHandler is called by the Telnet server whenever a new * connection is open with a remote terminal. * * The handler can return a string (or nil) to be sent to the terminal. * Note that the string will be converted to ASCII characters. * * @warning This block will be executed on arbitrary threads. */ typedef NSString* (^GCDTelnetStartHandler)(GCDTelnetConnection* connection); /** * The GCDTelnetLineHandler is called whenever a new line has been received * from the connected terminal. * * The handler can return a string (or nil) to be sent back to the terminal. * Note that the string will be converted to ASCII characters. * * @warning This block will be executed on arbitrary threads. */ typedef NSString* (^GCDTelnetLineHandler)(GCDTelnetConnection* connection, NSString* line); /** * The GCDTelnetCommandHandler is a special line handler that pre-parses the * line like a command line interface extracting the command and arguments. * * @warning This block will be executed on arbitrary threads. */ typedef NSString* (^GCDTelnetCommandHandler)(GCDTelnetConnection* connection, NSString* command, NSArray* arguments); /** * The GCDTelnetServer class implements a Telnet server. */ @interface GCDTelnetServer : GCDTCPServer /** * Initializes a Telnet server on a given port and using the default * GCDTelnetConnection class. */ - (instancetype)initWithPort:(NSUInteger)port startHandler:(GCDTelnetStartHandler)startHandler lineHandler:(GCDTelnetLineHandler)lineHandler; /** * Initializes a Telnet server on a given port and using the default * GCDTelnetConnection class but with a command handler instead of a line hander. */ - (instancetype)initWithPort:(NSUInteger)port startHandler:(GCDTelnetStartHandler)startHandler commandHandler:(GCDTelnetCommandHandler)commandHandler; /** * This method is the designated initializer for the class. * * Connection class must be [GCDTelnetConnection class] or a subclass of it. */ - (instancetype)initWithConnectionClass:(Class)connectionClass port:(NSUInteger)port startHandler:(GCDTelnetStartHandler)startHandler lineHandler:(GCDTelnetLineHandler)lineHandler; @end ================================================ FILE: GCDTelnetServer/GCDTelnetServer.m ================================================ /* Copyright (c) 2012-2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #import "GCDTelnetPrivate.h" /* Information Sources - http://support.microsoft.com/kb/231866 - http://pcmicro.com/netfoss/telnet.html - http://mud-dev.wikidot.com/telnet:negotiation - http://tintin.sourceforge.net/mtts/ */ /* Telnet Option Negotiation Sender Sent Receiver Responds Implication WILL DO The sender would like to use a certain facility if the receiver can handle it. Option is now in effect. WILL DONT Receiver says it cannot support the option. Option is not in effect. DO WILL The sender says it can handle traffic from the sender if the sender wishes to use a certain option. Option is now in effect. DO WONT Receiver says it cannot support the option. Option is not in effect. WONT DONT Option disabled. DONT is only valid response. DONT WONT Option disabled. WONT is only valid response. */ /* Telnet Built-in CLT from OS X 10.10 -> IAC, WILL, Echo, <- IAC, DO, Echo, -> IAC, WILL, Supress Go Ahead, <- IAC, DO, Supress Go Ahead, -> IAC, WILL, Status, <- IAC, DO, Status, (no response for Timing Mark) -> IAC, WILL, Terminal Type, <- IAC, DONT, Terminal Type, -> IAC, WILL, Window Size, <- IAC, DONT, Window Size, -> IAC, WILL, Terminal Speed, <- IAC, DONT, Terminal Speed, -> IAC, WILL, Remote Flow Control, <- IAC, DONT, Remote Flow Control, -> IAC, WILL, Linemode, <- IAC, DONT, Linemode, -> IAC, WILL, Environment Variables, <- IAC, DONT, Environment Variables, */ /* Telnet Built-in CLT from OS X 10.10 -> IAC, DO, Echo, <- IAC, WONT, Echo, -> IAC, DO, Supress Go Ahead, <- IAC, WILL, Supress Go Ahead, -> IAC, DO, Status, <- IAC, WONT, Status, -> IAC, DO, Timing Mark, <- IAC, WILL, Timing Mark, -> IAC, DO, Terminal Type, <- IAC, WILL, Terminal Type, -> IAC, DO, Window Size, <- IAC, WILL, Window Size, IAC, SB, Window Size, 0 208 0 42, IAC, SE, -> IAC, DO, Terminal Speed, <- IAC, WILL, Terminal Speed, -> IAC, DO, Remote Flow Control, <- IAC, WILL, Remote Flow Control, -> IAC, DO, Linemode, <- IAC, DO, Supress Go Ahead, IAC, WILL, Linemode, IAC, SB, Linemode, 3 1 3 0 3 98 3 4 2 15 5 2 20 7 98 28 8 2 4 9 66 26 10 2 127 11 2 21 12 2 23 13 2 18 14 2 22 15 2 17 16 2 19 17 0 255 255 18 0 255 255, IAC, SE, -> IAC, DO, Environment Variables, <- IAC, WONT, Environment Variables, */ @implementation GCDTelnetServer - (instancetype)initWithConnectionClass:(Class)connectionClass port:(NSUInteger)port { return [self initWithConnectionClass:connectionClass port:port startHandler:NULL lineHandler:NULL]; } - (instancetype)initWithPort:(NSUInteger)port startHandler:(GCDTelnetStartHandler)startHandler lineHandler:(GCDTelnetLineHandler)lineHandler { return [self initWithConnectionClass:[GCDTelnetConnection class] port:port startHandler:startHandler lineHandler:lineHandler]; } - (instancetype)initWithPort:(NSUInteger)port startHandler:(GCDTelnetStartHandler)startHandler commandHandler:(GCDTelnetCommandHandler)commandHandler { return [self initWithPort:port startHandler:startHandler lineHandler:^NSString*(GCDTelnetConnection* connection, NSString* line) { NSArray* array = [connection parseLineAsCommandAndArguments:line]; if (array.count) { NSString* command = array[0]; NSArray* arguments = [array subarrayWithRange:NSMakeRange(1, array.count - 1)]; return commandHandler(connection, command, arguments); } return nil; }]; } - (instancetype)initWithConnectionClass:(Class)connectionClass port:(NSUInteger)port startHandler:(GCDTelnetStartHandler)startHandler lineHandler:(GCDTelnetLineHandler)lineHandler { _LOG_DEBUG_CHECK([connectionClass isSubclassOfClass:[GCDTelnetConnection class]]); if ((self = [super initWithConnectionClass:connectionClass port:port])) { _startHandler = startHandler; _lineHandler = lineHandler; } return self; } @end ================================================ FILE: GCDTelnetServer/NSMutableString+ANSI.h ================================================ /* Copyright (c) 2012-2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #import /** * Constants representing ANSI colors. * * See https://en.wikipedia.org/wiki/ANSI_escape_code for details. */ typedef NS_ENUM(unsigned char, ANSIColor) { kANSIColor_Black = 0, kANSIColor_Red, kANSIColor_Green, kANSIColor_Yellow, kANSIColor_Blue, kANSIColor_Magenta, kANSIColor_Cyan, kANSIColor_White }; /** * Extensions to NSMutableString to create ANSI colored strings. */ @interface NSMutableString (ANSI) /** * Appends a string using the provided ANSI color and optionally in bold. */ - (void)appendANSIString:(NSString*)string withColor:(ANSIColor)color bold:(BOOL)bold; /** * Appends a formatted string using the provided ANSI color and optionally in bold. */ - (void)appendANSIStringWithColor:(ANSIColor)color bold:(BOOL)bold format:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4); @end ================================================ FILE: GCDTelnetServer/NSMutableString+ANSI.m ================================================ /* Copyright (c) 2012-2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #import "NSMutableString+ANSI.h" #define kCSIPrefix "\x1b[" @implementation NSMutableString (ANSI) - (void)appendANSIString:(NSString*)string withColor:(ANSIColor)color bold:(BOOL)bold { [self appendFormat:@"%s%i%sm%@%s0m", kCSIPrefix, 30 + color, bold ? ";1" : "", string, kCSIPrefix]; } - (void)appendANSIStringWithColor:(ANSIColor)color bold:(BOOL)bold format:(NSString*)format, ... { va_list arguments; va_start(arguments, format); NSString* string = [[NSMutableString alloc] initWithFormat:format arguments:arguments]; va_end(arguments); [self appendANSIString:string withColor:color bold:bold]; } @end ================================================ FILE: GCDTelnetServer.podspec ================================================ # http://guides.cocoapods.org/syntax/podspec.html # http://guides.cocoapods.org/making/getting-setup-with-trunk.html # $ sudo gem update cocoapods # (optional) $ pod trunk register {email} {name} --description={computer} # $ pod trunk --verbose push # DELETE THIS SECTION BEFORE PROCEEDING! Pod::Spec.new do |s| s.name = 'GCDTelnetServer' s.version = '1.1.6' s.author = { 'Pierre-Olivier Latour' => 'info@pol-online.net' } s.license = { :type => 'BSD', :file => 'LICENSE' } s.homepage = 'https://github.com/swisspol/GCDTelnetServer' s.summary = 'Drop-in embedded Telnet server for iOS and OS X apps' s.source = { :git => 'https://github.com/swisspol/GCDTelnetServer.git', :tag => s.version.to_s } s.ios.deployment_target = '8.0' s.osx.deployment_target = '10.8' s.requires_arc = true s.subspec 'GCDNetworking' do |cs| cs.source_files = 'GCDNetworking/GCDNetworking/*.{h,m}' cs.private_header_files = "GCDNetworking/GCDNetworking/*Private.h" cs.requires_arc = true cs.osx.frameworks = 'SystemConfiguration' cs.ios.frameworks = 'CFNetwork' end s.subspec 'Core' do |cs| cs.dependency 'GCDTelnetServer/GCDNetworking' s.source_files = 'GCDTelnetServer/*.{h,m}' s.private_header_files = "GCDTelnetServer/*Private.h" cs.requires_arc = true end end ================================================ FILE: GCDTelnetServer.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 48; objects = { /* Begin PBXAggregateTarget section */ E2B3CEE619E91770003ED065 /* Build All */ = { isa = PBXAggregateTarget; buildConfigurationList = E2B3CEE719E91770003ED065 /* Build configuration list for PBXAggregateTarget "Build All" */; buildPhases = ( ); dependencies = ( E2B3CEEB19E91784003ED065 /* PBXTargetDependency */, E2B3CF2B19E9189C003ED065 /* PBXTargetDependency */, E2168F9619EE04B200865350 /* PBXTargetDependency */, ); name = "Build All"; productName = "Build All"; }; /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ E26ABC2C19EC9F9A00654D9F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E26ABC2B19EC9F9A00654D9F /* main.m */; }; E280BF1419E9FF5000D85595 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E280BF1319E9FF5000D85595 /* CFNetwork.framework */; }; E280BF1619E9FF5500D85595 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E280BF1519E9FF5500D85595 /* CFNetwork.framework */; }; E295AF901E6B4C5E00EAC2FF /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E295AF8F1E6B4C5E00EAC2FF /* SystemConfiguration.framework */; }; E295AF911E6B4C6800EAC2FF /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E295AF8F1E6B4C5E00EAC2FF /* SystemConfiguration.framework */; }; E298C47A19ED866100C76821 /* GCDTelnetServer_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = E298C47919ED866100C76821 /* GCDTelnetServer_Tests.m */; }; E298C48619ED892500C76821 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E280BF1319E9FF5000D85595 /* CFNetwork.framework */; }; E2B03DCD19F4659F00D56CA6 /* GCDTelnetServer.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B03DCC19F4659F00D56CA6 /* GCDTelnetServer.m */; }; E2B03DCE19F4659F00D56CA6 /* GCDTelnetServer.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B03DCC19F4659F00D56CA6 /* GCDTelnetServer.m */; }; E2B03DCF19F4659F00D56CA6 /* GCDTelnetServer.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B03DCC19F4659F00D56CA6 /* GCDTelnetServer.m */; }; E2B03E0919F46A7300D56CA6 /* NSMutableString+ANSI.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B03E0819F46A7300D56CA6 /* NSMutableString+ANSI.m */; }; E2B03E0A19F46A7300D56CA6 /* NSMutableString+ANSI.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B03E0819F46A7300D56CA6 /* NSMutableString+ANSI.m */; }; E2B03E0B19F46A7300D56CA6 /* NSMutableString+ANSI.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B03E0819F46A7300D56CA6 /* NSMutableString+ANSI.m */; }; E2B03E0E19F46B1C00D56CA6 /* GCDTelnetConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B03E0D19F46B1C00D56CA6 /* GCDTelnetConnection.m */; }; E2B03E0F19F46B1C00D56CA6 /* GCDTelnetConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B03E0D19F46B1C00D56CA6 /* GCDTelnetConnection.m */; }; E2B03E1019F46B1C00D56CA6 /* GCDTelnetConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B03E0D19F46B1C00D56CA6 /* GCDTelnetConnection.m */; }; E2B03E8719F49EAD00D56CA6 /* GCDTCPClient.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B03E8019F49EAD00D56CA6 /* GCDTCPClient.m */; }; E2B03E8819F49EAD00D56CA6 /* GCDTCPClient.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B03E8019F49EAD00D56CA6 /* GCDTCPClient.m */; }; E2B03E8919F49EAD00D56CA6 /* GCDTCPClient.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B03E8019F49EAD00D56CA6 /* GCDTCPClient.m */; }; E2B03E8A19F49EAD00D56CA6 /* GCDTCPConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B03E8219F49EAD00D56CA6 /* GCDTCPConnection.m */; }; E2B03E8B19F49EAD00D56CA6 /* GCDTCPConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B03E8219F49EAD00D56CA6 /* GCDTCPConnection.m */; }; E2B03E8C19F49EAD00D56CA6 /* GCDTCPConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B03E8219F49EAD00D56CA6 /* GCDTCPConnection.m */; }; E2B03E8D19F49EAD00D56CA6 /* GCDTCPPeer.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B03E8419F49EAD00D56CA6 /* GCDTCPPeer.m */; }; E2B03E8E19F49EAD00D56CA6 /* GCDTCPPeer.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B03E8419F49EAD00D56CA6 /* GCDTCPPeer.m */; }; E2B03E8F19F49EAD00D56CA6 /* GCDTCPPeer.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B03E8419F49EAD00D56CA6 /* GCDTCPPeer.m */; }; E2B03E9019F49EAD00D56CA6 /* GCDTCPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B03E8619F49EAD00D56CA6 /* GCDTCPServer.m */; }; E2B03E9119F49EAD00D56CA6 /* GCDTCPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B03E8619F49EAD00D56CA6 /* GCDTCPServer.m */; }; E2B03E9219F49EAD00D56CA6 /* GCDTCPServer.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B03E8619F49EAD00D56CA6 /* GCDTCPServer.m */; }; E2B3CF1B19E91825003ED065 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B3CF1819E91825003ED065 /* AppDelegate.m */; }; E2B3CF1D19E91825003ED065 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B3CF1A19E91825003ED065 /* main.m */; }; E2B3CF3019E919DD003ED065 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2B3CF2F19E919DD003ED065 /* UIKit.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ E2168F9519EE04B200865350 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = E26DC15F19E8494700C68DDC /* Project object */; proxyType = 1; remoteGlobalIDString = E298C46B19ED859F00C76821; remoteInfo = "XLFacility (Tests)"; }; E2B3CEEA19E91784003ED065 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = E26DC15F19E8494700C68DDC /* Project object */; proxyType = 1; remoteGlobalIDString = E26DC16619E8494700C68DDC; remoteInfo = GCDLogger; }; E2B3CF2A19E9189C003ED065 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = E26DC15F19E8494700C68DDC /* Project object */; proxyType = 1; remoteGlobalIDString = E2B3CEEF19E917BB003ED065; remoteInfo = "GCDLogger (iOS)"; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ E26ABC2B19EC9F9A00654D9F /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; E26DC16719E8494700C68DDC /* GCDTelnetServer */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = GCDTelnetServer; sourceTree = BUILT_PRODUCTS_DIR; }; E280BF0E19E9F3DF00D85595 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; E280BF1319E9FF5000D85595 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; }; E280BF1519E9FF5500D85595 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.1.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; }; E295AF8F1E6B4C5E00EAC2FF /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; E298C46C19ED859F00C76821 /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; E298C47919ED866100C76821 /* GCDTelnetServer_Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDTelnetServer_Tests.m; sourceTree = ""; }; E2B03DCB19F4659F00D56CA6 /* GCDTelnetServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDTelnetServer.h; sourceTree = ""; }; E2B03DCC19F4659F00D56CA6 /* GCDTelnetServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDTelnetServer.m; sourceTree = ""; }; E2B03DD019F465E100D56CA6 /* GCDTelnetServer.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = GCDTelnetServer.podspec; sourceTree = ""; }; E2B03E0719F46A7300D56CA6 /* NSMutableString+ANSI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableString+ANSI.h"; sourceTree = ""; }; E2B03E0819F46A7300D56CA6 /* NSMutableString+ANSI.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableString+ANSI.m"; sourceTree = ""; }; E2B03E0C19F46B1C00D56CA6 /* GCDTelnetConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDTelnetConnection.h; sourceTree = ""; }; E2B03E0D19F46B1C00D56CA6 /* GCDTelnetConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = GCDTelnetConnection.m; sourceTree = ""; tabWidth = 2; }; E2B03E1119F46BBB00D56CA6 /* GCDTelnetPrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDTelnetPrivate.h; sourceTree = ""; }; E2B03E3919F4939000D56CA6 /* GCDTelnetLoggingBridgePrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDTelnetLoggingBridgePrivate.h; sourceTree = ""; }; E2B03E7C19F49EAD00D56CA6 /* GCDNetworkingLoggingBridgePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDNetworkingLoggingBridgePrivate.h; sourceTree = ""; }; E2B03E7D19F49EAD00D56CA6 /* GCDNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDNetworking.h; sourceTree = ""; }; E2B03E7E19F49EAD00D56CA6 /* GCDNetworkingPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDNetworkingPrivate.h; sourceTree = ""; }; E2B03E7F19F49EAD00D56CA6 /* GCDTCPClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDTCPClient.h; sourceTree = ""; }; E2B03E8019F49EAD00D56CA6 /* GCDTCPClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDTCPClient.m; sourceTree = ""; }; E2B03E8119F49EAD00D56CA6 /* GCDTCPConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDTCPConnection.h; sourceTree = ""; }; E2B03E8219F49EAD00D56CA6 /* GCDTCPConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDTCPConnection.m; sourceTree = ""; }; E2B03E8319F49EAD00D56CA6 /* GCDTCPPeer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDTCPPeer.h; sourceTree = ""; }; E2B03E8419F49EAD00D56CA6 /* GCDTCPPeer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDTCPPeer.m; sourceTree = ""; }; E2B03E8519F49EAD00D56CA6 /* GCDTCPServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDTCPServer.h; sourceTree = ""; }; E2B03E8619F49EAD00D56CA6 /* GCDTCPServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDTCPServer.m; sourceTree = ""; }; E2B3CEF019E917BB003ED065 /* GCDTelnetServer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GCDTelnetServer.app; sourceTree = BUILT_PRODUCTS_DIR; }; E2B3CF1719E91825003ED065 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; E2B3CF1819E91825003ED065 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; E2B3CF1919E91825003ED065 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E2B3CF1A19E91825003ED065 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; E2B3CF2F19E919DD003ED065 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.1.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ E26DC16419E8494700C68DDC /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( E295AF901E6B4C5E00EAC2FF /* SystemConfiguration.framework in Frameworks */, E280BF1419E9FF5000D85595 /* CFNetwork.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; E298C46919ED859F00C76821 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( E295AF911E6B4C6800EAC2FF /* SystemConfiguration.framework in Frameworks */, E298C48619ED892500C76821 /* CFNetwork.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; E2B3CEED19E917BB003ED065 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( E280BF1619E9FF5500D85595 /* CFNetwork.framework in Frameworks */, E2B3CF3019E919DD003ED065 /* UIKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ E26ABC2A19EC9F9A00654D9F /* CLT */ = { isa = PBXGroup; children = ( E26ABC2B19EC9F9A00654D9F /* main.m */, ); path = CLT; sourceTree = ""; }; E26DC15E19E8494700C68DDC = { isa = PBXGroup; children = ( E280BF0E19E9F3DF00D85595 /* README.md */, E2B03DD019F465E100D56CA6 /* GCDTelnetServer.podspec */, E2B03DCA19F4659F00D56CA6 /* GCDTelnetServer */, E26ABC2A19EC9F9A00654D9F /* CLT */, E2B3CF1619E91825003ED065 /* iOS */, E298C47719ED866100C76821 /* Tests */, E2B03E7B19F49EAD00D56CA6 /* GCDNetworking */, E26DC19E19E883CC00C68DDC /* Mac Frameworks and Libraries */, E2B3CF2E19E919AC003ED065 /* iOS Frameworks and Libraries */, E26DC16819E8494700C68DDC /* Products */, ); indentWidth = 2; sourceTree = ""; tabWidth = 2; }; E26DC16819E8494700C68DDC /* Products */ = { isa = PBXGroup; children = ( E26DC16719E8494700C68DDC /* GCDTelnetServer */, E2B3CEF019E917BB003ED065 /* GCDTelnetServer.app */, E298C46C19ED859F00C76821 /* Tests.xctest */, ); name = Products; sourceTree = ""; }; E26DC19E19E883CC00C68DDC /* Mac Frameworks and Libraries */ = { isa = PBXGroup; children = ( E295AF8F1E6B4C5E00EAC2FF /* SystemConfiguration.framework */, E280BF1319E9FF5000D85595 /* CFNetwork.framework */, ); name = "Mac Frameworks and Libraries"; sourceTree = ""; }; E298C47719ED866100C76821 /* Tests */ = { isa = PBXGroup; children = ( E298C47919ED866100C76821 /* GCDTelnetServer_Tests.m */, ); path = Tests; sourceTree = ""; }; E2B03DCA19F4659F00D56CA6 /* GCDTelnetServer */ = { isa = PBXGroup; children = ( E2B03E0C19F46B1C00D56CA6 /* GCDTelnetConnection.h */, E2B03E0D19F46B1C00D56CA6 /* GCDTelnetConnection.m */, E2B03E1119F46BBB00D56CA6 /* GCDTelnetPrivate.h */, E2B03E3919F4939000D56CA6 /* GCDTelnetLoggingBridgePrivate.h */, E2B03DCB19F4659F00D56CA6 /* GCDTelnetServer.h */, E2B03DCC19F4659F00D56CA6 /* GCDTelnetServer.m */, E2B03E0719F46A7300D56CA6 /* NSMutableString+ANSI.h */, E2B03E0819F46A7300D56CA6 /* NSMutableString+ANSI.m */, ); path = GCDTelnetServer; sourceTree = ""; }; E2B03E7B19F49EAD00D56CA6 /* GCDNetworking */ = { isa = PBXGroup; children = ( E2B03E7D19F49EAD00D56CA6 /* GCDNetworking.h */, E2B03E7E19F49EAD00D56CA6 /* GCDNetworkingPrivate.h */, E2B03E7C19F49EAD00D56CA6 /* GCDNetworkingLoggingBridgePrivate.h */, E2B03E7F19F49EAD00D56CA6 /* GCDTCPClient.h */, E2B03E8019F49EAD00D56CA6 /* GCDTCPClient.m */, E2B03E8119F49EAD00D56CA6 /* GCDTCPConnection.h */, E2B03E8219F49EAD00D56CA6 /* GCDTCPConnection.m */, E2B03E8319F49EAD00D56CA6 /* GCDTCPPeer.h */, E2B03E8419F49EAD00D56CA6 /* GCDTCPPeer.m */, E2B03E8519F49EAD00D56CA6 /* GCDTCPServer.h */, E2B03E8619F49EAD00D56CA6 /* GCDTCPServer.m */, ); name = GCDNetworking; path = GCDNetworking/GCDNetworking; sourceTree = ""; }; E2B3CF1619E91825003ED065 /* iOS */ = { isa = PBXGroup; children = ( E2B3CF1719E91825003ED065 /* AppDelegate.h */, E2B3CF1819E91825003ED065 /* AppDelegate.m */, E2B3CF1919E91825003ED065 /* Info.plist */, E2B3CF1A19E91825003ED065 /* main.m */, ); path = iOS; sourceTree = ""; }; E2B3CF2E19E919AC003ED065 /* iOS Frameworks and Libraries */ = { isa = PBXGroup; children = ( E2B3CF2F19E919DD003ED065 /* UIKit.framework */, E280BF1519E9FF5500D85595 /* CFNetwork.framework */, ); name = "iOS Frameworks and Libraries"; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ E26DC16619E8494700C68DDC /* GCDTelnetServer (CLT) */ = { isa = PBXNativeTarget; buildConfigurationList = E26DC16E19E8494700C68DDC /* Build configuration list for PBXNativeTarget "GCDTelnetServer (CLT)" */; buildPhases = ( E26DC16319E8494700C68DDC /* Sources */, E26DC16419E8494700C68DDC /* Frameworks */, ); buildRules = ( ); dependencies = ( ); name = "GCDTelnetServer (CLT)"; productName = Logger; productReference = E26DC16719E8494700C68DDC /* GCDTelnetServer */; productType = "com.apple.product-type.tool"; }; E298C46B19ED859F00C76821 /* GCDTelnetServer (Tests) */ = { isa = PBXNativeTarget; buildConfigurationList = E298C47619ED859F00C76821 /* Build configuration list for PBXNativeTarget "GCDTelnetServer (Tests)" */; buildPhases = ( E298C46819ED859F00C76821 /* Sources */, E298C46919ED859F00C76821 /* Frameworks */, ); buildRules = ( ); dependencies = ( ); name = "GCDTelnetServer (Tests)"; productName = "XLFacility Tests"; productReference = E298C46C19ED859F00C76821 /* Tests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; E2B3CEEF19E917BB003ED065 /* GCDTelnetServer (iOS) */ = { isa = PBXNativeTarget; buildConfigurationList = E2B3CF1019E917BC003ED065 /* Build configuration list for PBXNativeTarget "GCDTelnetServer (iOS)" */; buildPhases = ( E2B3CEEC19E917BB003ED065 /* Sources */, E2B3CEED19E917BB003ED065 /* Frameworks */, E2B3CEEE19E917BB003ED065 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = "GCDTelnetServer (iOS)"; productName = GCDLogger; productReference = E2B3CEF019E917BB003ED065 /* GCDTelnetServer.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ E26DC15F19E8494700C68DDC /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0830; ORGANIZATIONNAME = ""; TargetAttributes = { E26DC16619E8494700C68DDC = { CreatedOnToolsVersion = 6.1; }; E298C46B19ED859F00C76821 = { CreatedOnToolsVersion = 6.1; TestTargetID = E26ABC0919EC9E2D00654D9F; }; E2B3CEE619E91770003ED065 = { CreatedOnToolsVersion = 6.1; }; E2B3CEEF19E917BB003ED065 = { CreatedOnToolsVersion = 6.1; }; }; }; buildConfigurationList = E26DC16219E8494700C68DDC /* Build configuration list for PBXProject "GCDTelnetServer" */; compatibilityVersion = "Xcode 8.0"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = E26DC15E19E8494700C68DDC; productRefGroup = E26DC16819E8494700C68DDC /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( E2B3CEE619E91770003ED065 /* Build All */, E26DC16619E8494700C68DDC /* GCDTelnetServer (CLT) */, E2B3CEEF19E917BB003ED065 /* GCDTelnetServer (iOS) */, E298C46B19ED859F00C76821 /* GCDTelnetServer (Tests) */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ E2B3CEEE19E917BB003ED065 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ E26DC16319E8494700C68DDC /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( E2B03E8D19F49EAD00D56CA6 /* GCDTCPPeer.m in Sources */, E2B03E0919F46A7300D56CA6 /* NSMutableString+ANSI.m in Sources */, E26ABC2C19EC9F9A00654D9F /* main.m in Sources */, E2B03E8A19F49EAD00D56CA6 /* GCDTCPConnection.m in Sources */, E2B03E8719F49EAD00D56CA6 /* GCDTCPClient.m in Sources */, E2B03E9019F49EAD00D56CA6 /* GCDTCPServer.m in Sources */, E2B03E0E19F46B1C00D56CA6 /* GCDTelnetConnection.m in Sources */, E2B03DCD19F4659F00D56CA6 /* GCDTelnetServer.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; E298C46819ED859F00C76821 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( E2B03E8F19F49EAD00D56CA6 /* GCDTCPPeer.m in Sources */, E2B03E0B19F46A7300D56CA6 /* NSMutableString+ANSI.m in Sources */, E298C47A19ED866100C76821 /* GCDTelnetServer_Tests.m in Sources */, E2B03E8C19F49EAD00D56CA6 /* GCDTCPConnection.m in Sources */, E2B03E8919F49EAD00D56CA6 /* GCDTCPClient.m in Sources */, E2B03E9219F49EAD00D56CA6 /* GCDTCPServer.m in Sources */, E2B03E1019F46B1C00D56CA6 /* GCDTelnetConnection.m in Sources */, E2B03DCF19F4659F00D56CA6 /* GCDTelnetServer.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; E2B3CEEC19E917BB003ED065 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( E2B03E8E19F49EAD00D56CA6 /* GCDTCPPeer.m in Sources */, E2B03E9119F49EAD00D56CA6 /* GCDTCPServer.m in Sources */, E2B03E0F19F46B1C00D56CA6 /* GCDTelnetConnection.m in Sources */, E2B3CF1D19E91825003ED065 /* main.m in Sources */, E2B3CF1B19E91825003ED065 /* AppDelegate.m in Sources */, E2B03E8B19F49EAD00D56CA6 /* GCDTCPConnection.m in Sources */, E2B03E0A19F46A7300D56CA6 /* NSMutableString+ANSI.m in Sources */, E2B03DCE19F4659F00D56CA6 /* GCDTelnetServer.m in Sources */, E2B03E8819F49EAD00D56CA6 /* GCDTCPClient.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ E2168F9619EE04B200865350 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = E298C46B19ED859F00C76821 /* GCDTelnetServer (Tests) */; targetProxy = E2168F9519EE04B200865350 /* PBXContainerItemProxy */; }; E2B3CEEB19E91784003ED065 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = E26DC16619E8494700C68DDC /* GCDTelnetServer (CLT) */; targetProxy = E2B3CEEA19E91784003ED065 /* PBXContainerItemProxy */; }; E2B3CF2B19E9189C003ED065 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = E2B3CEEF19E917BB003ED065 /* GCDTelnetServer (iOS) */; targetProxy = E2B3CF2A19E9189C003ED065 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ E26DC16C19E8494700C68DDC /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ENABLE_OBJC_ARC = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1"; ONLY_ACTIVE_ARCH = YES; WARNING_CFLAGS = ( "-Wall", "-Weverything", "-Wshadow", "-Wshorten-64-to-32", "-Wno-direct-ivar-access", "-Wno-objc-missing-property-synthesis", "-Wno-implicit-retain-self", "-Wno-documentation-unknown-command", "-Wno-gnu-statement-expression", "-Wno-assign-enum", "-Wno-reserved-id-macro", "-Wno-cstring-format-directive", "-Wno-partial-availability", ); }; name = Debug; }; E26DC16D19E8494700C68DDC /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ENABLE_OBJC_ARC = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_TREAT_WARNINGS_AS_ERRORS = YES; WARNING_CFLAGS = "-Wall"; }; name = Release; }; E26DC16F19E8494700C68DDC /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { MACOSX_DEPLOYMENT_TARGET = 10.8; PRODUCT_NAME = GCDTelnetServer; SDKROOT = macosx; }; name = Debug; }; E26DC17019E8494700C68DDC /* Release */ = { isa = XCBuildConfiguration; buildSettings = { MACOSX_DEPLOYMENT_TARGET = 10.8; PRODUCT_NAME = GCDTelnetServer; SDKROOT = macosx; }; name = Release; }; E298C47419ED859F00C76821 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { FRAMEWORK_SEARCH_PATHS = ( "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", ); MACOSX_DEPLOYMENT_TARGET = 10.8; PRODUCT_NAME = Tests; SDKROOT = macosx; }; name = Debug; }; E298C47519ED859F00C76821 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { FRAMEWORK_SEARCH_PATHS = ( "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", ); MACOSX_DEPLOYMENT_TARGET = 10.8; PRODUCT_NAME = Tests; SDKROOT = macosx; }; name = Release; }; E2B3CEE819E91770003ED065 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; E2B3CEE919E91770003ED065 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; E2B3CF1119E917BC003ED065 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = "iPhone Developer"; DEVELOPMENT_TEAM = 88W3E55T4B; INFOPLIST_FILE = iOS/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; PRODUCT_NAME = GCDTelnetServer; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; E2B3CF1219E917BC003ED065 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = "iPhone Developer"; DEVELOPMENT_TEAM = 88W3E55T4B; INFOPLIST_FILE = iOS/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; PRODUCT_NAME = GCDTelnetServer; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ E26DC16219E8494700C68DDC /* Build configuration list for PBXProject "GCDTelnetServer" */ = { isa = XCConfigurationList; buildConfigurations = ( E26DC16C19E8494700C68DDC /* Debug */, E26DC16D19E8494700C68DDC /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; E26DC16E19E8494700C68DDC /* Build configuration list for PBXNativeTarget "GCDTelnetServer (CLT)" */ = { isa = XCConfigurationList; buildConfigurations = ( E26DC16F19E8494700C68DDC /* Debug */, E26DC17019E8494700C68DDC /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; E298C47619ED859F00C76821 /* Build configuration list for PBXNativeTarget "GCDTelnetServer (Tests)" */ = { isa = XCConfigurationList; buildConfigurations = ( E298C47419ED859F00C76821 /* Debug */, E298C47519ED859F00C76821 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; E2B3CEE719E91770003ED065 /* Build configuration list for PBXAggregateTarget "Build All" */ = { isa = XCConfigurationList; buildConfigurations = ( E2B3CEE819E91770003ED065 /* Debug */, E2B3CEE919E91770003ED065 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; E2B3CF1019E917BC003ED065 /* Build configuration list for PBXNativeTarget "GCDTelnetServer (iOS)" */ = { isa = XCConfigurationList; buildConfigurations = ( E2B3CF1119E917BC003ED065 /* Debug */, E2B3CF1219E917BC003ED065 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = E26DC15F19E8494700C68DDC /* Project object */; } ================================================ FILE: GCDTelnetServer.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme ================================================ ================================================ FILE: LICENSE ================================================ Copyright (c) 2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. ================================================ FILE: README.md ================================================ Overview ======== [![Version](http://cocoapod-badges.herokuapp.com/v/GCDTelnetServer/badge.png)](http://cocoadocs.org/docsets/GCDTelnetServer) [![Platform](http://cocoapod-badges.herokuapp.com/p/GCDTelnetServer/badge.png)](https://github.com/swisspol/GCDTelnetServer) [![License](http://img.shields.io/cocoapods/l/GCDTelnetServer.svg)](LICENSE) GCDTelnetServer is a drop-in embedded Telnet server for iOS and OS X apps. Features: * Elegant and simple API * Fully asynchronous (doesn't need the main thread) * Entirely built using [Grand Central Dispatch](http://en.wikipedia.org/wiki/Grand_Central_Dispatch) for best performance and concurrency * Support for ANSI colors with an extension on `NSMutableString` * Can parse line inputs as command and arguments command line interface * Full support for IPv4 and IPv6 * Automatically handles background and suspended modes on iOS * No dependencies on third-party source code * Available under a friendly [New BSD License](LICENSE) Requirements: * OS X 10.8 or later (x86_64) * iOS 8.0 or later (armv7, armv7s or arm64) * ARC memory management only Getting Started =============== Download or check out the [latest release](https://github.com/swisspol/GCDTelnetServer/releases) of GCDTelnetServer then add both the "GCDTelnetServer" and "GCDNetworking/GCDNetworking" subfolders to your Xcode project. Alternatively, you can install GCDTelnetServer using [CocoaPods](http://cocoapods.org/) by simply adding this line to your Xcode project's Podfile: ``` pod "GCDTelnetServer", "~> 1.0" ``` Using GCDTelnetServer in Your App ================================= ```objectivec #import "GCDTelnetServer.h" GCDTCPServer* server = [[GCDTelnetServer alloc] initWithPort:2323 startHandler:^NSString*(GCDTelnetConnection* connection) { // Return welcome message return [NSString stringWithFormat:@"You are connected using \"%@\"\n", connection.terminalType]; } lineHandler:^NSString*(GCDTelnetConnection* connection, NSString* line) { // Simply echo back the received line but you could do anything here return [line stringByAppendingString:@"\n"]; }]; [server start]; ``` Then launch Terminal on your Mac, and simply enter `telnet YOUR_COMPUTER_OR_IPHONE_IP_ADDRESS 2323` and voilà, you can communicate "live" with your app. **GCDTelnetServer has an extensive customization API, be sure to peruse [GCDTelnetConnection.h](GCDTelnetServer/GCDTelnetConnection.h).** Executing Remote Commands ========================= The most interesting use of GCDTelnetServer is to execute commands inside your app while it's running on the device e.g. to query internal state, trigger actions while the app is in the background, etc... This sample code demonstrate how to implement a welcome message with more information (and ANSI colors!), and also support 3 commands (`quit`, `crash` and `setwcolor` which takes some arguments): ```objectivec #import "GCDTelnetServer.h" #import "NSMutableString+ANSI.h" - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { GCDTCPServer* server = [[GCDTelnetServer alloc] initWithPort:2323 startHandler:^NSString*(GCDTelnetConnection* connection) { UIDevice* device = [UIDevice currentDevice]; NSMutableString* welcome = [[NSMutableString alloc] init]; [welcome appendANSIStringWithColor:kANSIColor_Green bold:NO format:@"You are connected from %@ using \"%@\"\n", connection.remoteIPAddress, connection.terminalType]; [welcome appendANSIStringWithColor:kANSIColor_Green bold:NO format:@"Current device is %@ running %@ %@\n", device.model, device.systemName, device.systemVersion]; return welcome; } commandHandler:^NSString*(GCDTelnetConnection* connection, NSString* command, NSArray* arguments) { if ([command isEqualToString:@"quit"]) { [connection close]; return nil; } else if ([command isEqualToString:@"crash"]) { abort(); } else if ([command isEqualToString:@"setwcolor"]) { if (arguments.count == 3) { dispatch_async(dispatch_get_main_queue(), ^{ _window.backgroundColor = [UIColor colorWithRed:[arguments[0] doubleValue] green:[arguments[1] doubleValue] blue:[arguments[2] doubleValue] alpha:1.0]; }); return @"OK\n"; } return @"Usage: setwcolor red green blue\n"; } NSMutableString* error = [[NSMutableString alloc] init]; [error appendANSIStringWithColor:kANSIColor_Red bold:YES format:@"UNKNOWN COMMAND = %@ (%@)\n", command, [arguments componentsJoinedByString:@", "]]; return error; }]; [server start]; // TODO: Handle error return YES; } ``` And here's an example session: ```sh $ telnet localhost 2323 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. You are connected from 127.0.0.1 using "XTERM-256COLOR" Current device is iPad Simulator running iPhone OS 8.1 > test UNKNOWN COMMAND = test () > setwcolor Usage: setwcolor red green blue > setwcolor 1 0 0 OK > quitConnection closed by foreign host. ``` ================================================ FILE: Run-Tests.sh ================================================ #!/bin/bash -ex # Run GCDNetworking tests first pushd "GCDNetworking" ./Run-Tests.sh popd OSX_SDK="macosx" if [ -z "$TRAVIS" ]; then IOS_SDK="iphoneos" else IOS_SDK="iphonesimulator" fi OSX_TARGET="GCDTelnetServer (CLT)" IOS_TARGET="GCDTelnetServer (iOS)" CONFIGURATION="Release" BUILD_DIR="/tmp/GCDTelnetServer" PRODUCT="$BUILD_DIR/$CONFIGURATION/GCDTelnetServer" # Build for iOS for oldest deployment target rm -rf "$BUILD_DIR" xcodebuild -sdk "$IOS_SDK" -target "$IOS_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$BUILD_DIR" "IPHONEOS_DEPLOYMENT_TARGET=8.0" # Build for iOS for default deployment target rm -rf "$BUILD_DIR" xcodebuild -sdk "$IOS_SDK" -target "$IOS_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$BUILD_DIR" # Build for OS X for oldest deployment target rm -rf "$BUILD_DIR" xcodebuild -sdk "$OSX_SDK" -target "$OSX_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$BUILD_DIR" "MACOSX_DEPLOYMENT_TARGET=10.8" # Build for OS X for default deployment target rm -rf "$BUILD_DIR" xcodebuild -sdk "$OSX_SDK" -target "$OSX_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$BUILD_DIR" # Run tests xcodebuild test -scheme "Tests" # Done echo "\nAll tests completed successfully!" ================================================ FILE: Tests/GCDTelnetServer_Tests.m ================================================ /* Copyright (c) 2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" #pragma clang diagnostic ignored "-Wsign-compare" #import #import "GCDTelnetServer.h" #define kTestPort 3333 #define kCommunicationSleepDelay (100 * 1000) @interface GCDTelnetServer_Tests : XCTestCase @end @implementation GCDTelnetServer_Tests - (void)testCLIParsing { GCDTelnetConnection* connection = [[GCDTelnetConnection alloc] initWithSocket:0]; NSArray* array1 = [connection parseLineAsCommandAndArguments:@"this is 'a test' string \"using quoting\""]; NSArray* array2 = @[ @"this", @"is", @"a test", @"string", @"using quoting" ]; XCTAssertEqualObjects(array1, array2); } - (void)testHandlers { GCDTelnetServer* server = [[GCDTelnetServer alloc] initWithPort:kTestPort startHandler:^NSString*(GCDTelnetConnection* connection) { return @"Hello World!\n"; } lineHandler:^NSString*(GCDTelnetConnection* connection, NSString* line) { return [line stringByAppendingString:@"\n"]; }]; XCTAssertTrue([server start]); XCTestExpectation* expectation = [self expectationWithDescription:@""]; [GCDTCPConnection connectAsynchronouslyToHost:@"localhost" port:kTestPort timeout:1.0 completion:^(GCDTCPConnection* connection) { XCTAssertNotNil(connection); [connection open]; usleep(kCommunicationSleepDelay); NSData* data1 = [connection readDataWithTimeout:1.0]; XCTAssertEqual(data1.length, 3); unsigned char buffer1[] = {255, 253, 3}; XCTAssertTrue([connection writeData:[NSData dataWithBytes:buffer1 length:sizeof(buffer1)] withTimeout:1.0]); usleep(kCommunicationSleepDelay); NSData* data2 = [connection readDataWithTimeout:1.0]; XCTAssertEqual(data2.length, 3); unsigned char buffer2[] = {255, 253, 1}; XCTAssertTrue([connection writeData:[NSData dataWithBytes:buffer2 length:sizeof(buffer2)] withTimeout:1.0]); usleep(kCommunicationSleepDelay); NSData* data3 = [connection readDataWithTimeout:1.0]; XCTAssertEqual(data3.length, 3); unsigned char buffer3[] = {255, 254, 24}; XCTAssertTrue([connection writeData:[NSData dataWithBytes:buffer3 length:sizeof(buffer3)] withTimeout:1.0]); usleep(kCommunicationSleepDelay); NSData* data4 = [connection readDataWithTimeout:1.0]; XCTAssertNotNil(data4); NSString* string4 = [[NSString alloc] initWithData:data4 encoding:NSASCIIStringEncoding]; XCTAssertEqualObjects(string4, @"Hello World!\r\n> "); XCTAssertTrue([connection writeString:@"B" withTimeout:1.0]); usleep(kCommunicationSleepDelay); XCTAssertTrue([connection writeString:@"o" withTimeout:1.0]); usleep(kCommunicationSleepDelay); XCTAssertTrue([connection writeString:@"n" withTimeout:1.0]); usleep(kCommunicationSleepDelay); XCTAssertTrue([connection writeString:@"j" withTimeout:1.0]); usleep(kCommunicationSleepDelay); XCTAssertTrue([connection writeString:@"o" withTimeout:1.0]); usleep(kCommunicationSleepDelay); XCTAssertTrue([connection writeString:@"u" withTimeout:1.0]); usleep(kCommunicationSleepDelay); XCTAssertTrue([connection writeString:@"r" withTimeout:1.0]); usleep(kCommunicationSleepDelay); XCTAssertTrue([connection writeString:@"\r\0" withTimeout:1.0]); usleep(kCommunicationSleepDelay); NSData* data5 = [connection readDataWithTimeout:1.0]; XCTAssertNotNil(data5); NSString* string5 = [[NSString alloc] initWithData:data5 encoding:NSASCIIStringEncoding]; XCTAssertEqualObjects(string5, @"Bonjour\r\nBonjour\r\n> "); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:10.0 handler:NULL]; [server stop]; } @end ================================================ FILE: iOS/AppDelegate.h ================================================ /* Copyright (c) 2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #import @interface AppDelegate : UIResponder @property(retain, nonatomic) UIWindow* window; @end ================================================ FILE: iOS/AppDelegate.m ================================================ /* Copyright (c) 2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #import "AppDelegate.h" #import "GCDTelnetServer.h" #import "NSMutableString+ANSI.h" @implementation AppDelegate - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { _window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; _window.backgroundColor = [UIColor whiteColor]; _window.rootViewController = [[UIViewController alloc] init]; _window.rootViewController.view = [[UIView alloc] init]; [_window makeKeyAndVisible]; GCDTCPServer* server = [[GCDTelnetServer alloc] initWithPort:2323 startHandler:^NSString*(GCDTelnetConnection* connection) { UIDevice* device = [UIDevice currentDevice]; NSMutableString* welcome = [[NSMutableString alloc] init]; [welcome appendANSIStringWithColor:kANSIColor_Green bold:NO format:@"You are connected from %@ using \"%@\"\n", connection.remoteIPAddress, connection.terminalType]; [welcome appendANSIStringWithColor:kANSIColor_Green bold:NO format:@"Current device is %@ running %@ %@\n", device.model, device.systemName, device.systemVersion]; return welcome; } commandHandler:^NSString*(GCDTelnetConnection* connection, NSString* command, NSArray* arguments) { if ([command isEqualToString:@"quit"]) { [connection close]; return nil; } else if ([command isEqualToString:@"crash"]) { abort(); } else if ([command isEqualToString:@"setwcolor"]) { if (arguments.count == 3) { dispatch_async(dispatch_get_main_queue(), ^{ _window.backgroundColor = [UIColor colorWithRed:[arguments[0] doubleValue] green:[arguments[1] doubleValue] blue:[arguments[2] doubleValue] alpha:1.0]; }); return @"OK\n"; } return @"Usage: setwcolor red green blue\n"; } NSMutableString* error = [[NSMutableString alloc] init]; [error appendANSIStringWithColor:kANSIColor_Red bold:YES format:@"UNKNOWN COMMAND = %@ (%@)\n", command, [arguments componentsJoinedByString:@", "]]; return error; }]; if (![server start]) { abort(); } NSLog(@"Telnet server running on %@", GCDTCPServerGetPrimaryIPAddress(false)); return YES; } @end ================================================ FILE: iOS/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleDisplayName ${PRODUCT_NAME} CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier net.pol-online.${PRODUCT_NAME:rfc1034identifier} CFBundleInfoDictionaryVersion 6.0 CFBundleName ${PRODUCT_NAME} CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleVersion 1.0 LSRequiresIPhoneOS UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: iOS/main.m ================================================ /* Copyright (c) 2014, Pierre-Olivier Latour 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. * The name of Pierre-Olivier Latour may not 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 PIERRE-OLIVIER LATOUR 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. */ #import "AppDelegate.h" int main(int argc, char* argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } }