Showing preview only (228K chars total). Download the full file or copy to clipboard to get everything.
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 <Foundation/Foundation.h>
/**
* 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 <Foundation/Foundation.h>
#import <sys/socket.h>
/**
* 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 <net/if.h>
#import <netdb.h>
#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 <TargetConditionals.h>
#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#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 <TargetConditionals.h>
#if TARGET_OS_IPHONE
#import <MobileCoreServices/MobileCoreServices.h>
#else
#import <SystemConfiguration/SystemConfiguration.h>
#endif
#import <ifaddrs.h>
#import <net/if.h>
#import <netdb.h>
#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 = "<group>"; };
E22E917F19F2F0850043457E /* GCDNetworking.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDNetworking.h; sourceTree = "<group>"; };
E26ABC2B19EC9F9A00654D9F /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
E2812BB219F2E9780046DDC1 /* GCDTCPClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDTCPClient.m; sourceTree = "<group>"; };
E2812BB319F2E9780046DDC1 /* GCDTCPConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDTCPConnection.h; sourceTree = "<group>"; };
E2812BB419F2E9780046DDC1 /* GCDTCPConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDTCPConnection.m; sourceTree = "<group>"; };
E2812BB519F2E9780046DDC1 /* GCDTCPPeer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDTCPPeer.h; sourceTree = "<group>"; };
E2812BB619F2E9780046DDC1 /* GCDTCPPeer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDTCPPeer.m; sourceTree = "<group>"; };
E2812BB719F2E9780046DDC1 /* GCDTCPServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDTCPServer.h; sourceTree = "<group>"; };
E2812BB819F2E9780046DDC1 /* GCDTCPServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDTCPServer.m; sourceTree = "<group>"; };
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 = "<group>"; };
E2B03E3A19F493A500D56CA6 /* GCDNetworkingLoggingBridgePrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDNetworkingLoggingBridgePrivate.h; sourceTree = "<group>"; };
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 = "<group>"; };
E2B3CF1819E91825003ED065 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
E2B3CF1919E91825003ED065 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
E2B3CF1A19E91825003ED065 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
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 = "<group>";
};
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 = "<group>";
tabWidth = 2;
};
E26DC16819E8494700C68DDC /* Products */ = {
isa = PBXGroup;
children = (
E26DC16719E8494700C68DDC /* GCDNetworking */,
E2B3CEF019E917BB003ED065 /* GCDNetworking.app */,
E298C46C19ED859F00C76821 /* Tests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
E26DC19E19E883CC00C68DDC /* Mac Frameworks and Libraries */ = {
isa = PBXGroup;
children = (
E295AF8B1E6B4B1F00EAC2FF /* SystemConfiguration.framework */,
E280BF1319E9FF5000D85595 /* CFNetwork.framework */,
);
name = "Mac Frameworks and Libraries";
sourceTree = "<group>";
};
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 = "<group>";
};
E298C47719ED866100C76821 /* Tests */ = {
isa = PBXGroup;
children = (
E298C47919ED866100C76821 /* GCDNetworking_Tests.m */,
);
path = Tests;
sourceTree = "<group>";
};
E2B3CF1619E91825003ED065 /* iOS */ = {
isa = PBXGroup;
children = (
E2B3CF1719E91825003ED065 /* AppDelegate.h */,
E2B3CF1819E91825003ED065 /* AppDelegate.m */,
E2B3CF1919E91825003ED065 /* Info.plist */,
E2B3CF1A19E91825003ED065 /* main.m */,
);
path = iOS;
sourceTree = "<group>";
};
E2B3CF2E19E919AC003ED065 /* iOS Frameworks and Libraries */ = {
isa = PBXGroup;
children = (
E2B3CF2F19E919DD003ED065 /* UIKit.framework */,
E280BF1519E9FF5500D85595 /* CFNetwork.framework */,
);
name = "iOS Frameworks and Libraries";
sourceTree = "<group>";
};
/* 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
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0830"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "NO"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E298C46B19ED859F00C76821"
BuildableName = "Tests.xctest"
BlueprintName = "GCDNetworking (Tests)"
ReferencedContainer = "container:GCDNetworking.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "NO"
enableAddressSanitizer = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E298C46B19ED859F00C76821"
BuildableName = "Tests.xctest"
BlueprintName = "GCDNetworking (Tests)"
ReferencedContainer = "container:GCDNetworking.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E298C46B19ED859F00C76821"
BuildableName = "Tests.xctest"
BlueprintName = "GCDNetworking (Tests)"
ReferencedContainer = "container:GCDNetworking.xcodeproj">
</BuildableReference>
</MacroExpansion>
<EnvironmentVariables>
<EnvironmentVariable
key = "DYLD_INSERT_LIBRARIES"
value = "/usr/lib/libgmalloc.dylib"
isEnabled = "YES">
</EnvironmentVariable>
<EnvironmentVariable
key = "NSZombieEnabled"
value = "YES"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<AdditionalOptions>
<AdditionalOption
key = "NSZombieEnabled"
value = "YES"
isEnabled = "YES">
</AdditionalOption>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Release">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
================================================
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
========
[](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 <XCTest/XCTest.h>
#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 <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@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
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>net.pol-online.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
================================================
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 <Foundation/Foundation.h>
/**
* 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 <Foundation/Foundation.h>
/**
* 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 <Foundation/Foundation.h>
/**
* 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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
E2B03DCB19F4659F00D56CA6 /* GCDTelnetServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDTelnetServer.h; sourceTree = "<group>"; };
E2B03DCC19F4659F00D56CA6 /* GCDTelnetServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDTelnetServer.m; sourceTree = "<group>"; };
E2B03DD019F465E100D56CA6 /* GCDTelnetServer.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = GCDTelnetServer.podspec; sourceTree = "<group>"; };
E2B03E0719F46A7300D56CA6 /* NSMutableString+ANSI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableString+ANSI.h"; sourceTree = "<group>"; };
E2B03E0819F46A7300D56CA6 /* NSMutableString+ANSI.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableString+ANSI.m"; sourceTree = "<group>"; };
E2B03E0C19F46B1C00D56CA6 /* GCDTelnetConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDTelnetConnection.h; sourceTree = "<group>"; };
E2B03E0D19F46B1C00D56CA6 /* GCDTelnetConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = GCDTelnetConnection.m; sourceTree = "<group>"; tabWidth = 2; };
E2B03E1119F46BBB00D56CA6 /* GCDTelnetPrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDTelnetPrivate.h; sourceTree = "<group>"; };
E2B03E3919F4939000D56CA6 /* GCDTelnetLoggingBridgePrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDTelnetLoggingBridgePrivate.h; sourceTree = "<group>"; };
E2B03E7C19F49EAD00D56CA6 /* GCDNetworkingLoggingBridgePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDNetworkingLoggingBridgePrivate.h; sourceTree = "<group>"; };
E2B03E7D19F49EAD00D56CA6 /* GCDNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDNetworking.h; sourceTree = "<group>"; };
E2B03E7E19F49EAD00D56CA6 /* GCDNetworkingPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDNetworkingPrivate.h; sourceTree = "<group>"; };
E2B03E7F19F49EAD00D56CA6 /* GCDTCPClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDTCPClient.h; sourceTree = "<group>"; };
E2B03E8019F49EAD00D56CA6 /* GCDTCPClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDTCPClient.m; sourceTree = "<group>"; };
E2B03E8119F49EAD00D56CA6 /* GCDTCPConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDTCPConnection.h; sourceTree = "<group>"; };
E2B03E8219F49EAD00D56CA6 /* GCDTCPConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDTCPConnection.m; sourceTree = "<group>"; };
E2B03E8319F49EAD00D56CA6 /* GCDTCPPeer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDTCPPeer.h; sourceTree = "<group>"; };
E2B03E8419F49EAD00D56CA6 /* GCDTCPPeer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDTCPPeer.m; sourceTree = "<group>"; };
E2B03E8519F49EAD00D56CA6 /* GCDTCPServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDTCPServer.h; sourceTree = "<group>"; };
E2B03E8619F49EAD00D56CA6 /* GCDTCPServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDTCPServer.m; sourceTree = "<group>"; };
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 = "<group>"; };
E2B3CF1819E91825003ED065 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
E2B3CF1919E91825003ED065 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
E2B3CF1A19E91825003ED065 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
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 = "<group>";
};
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 = "<group>";
tabWidth = 2;
};
E26DC16819E8494700C68DDC /* Products */ = {
isa = PBXGroup;
children = (
E26DC16719E8494700C68DDC /* GCDTelnetServer */,
E2B3CEF019E917BB003ED065 /* GCDTelnetServer.app */,
E298C46C19ED859F00C76821 /* Tests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
E26DC19E19E883CC00C68DDC /* Mac Frameworks and Libraries */ = {
isa = PBXGroup;
children = (
E295AF8F1E6B4C5E00EAC2FF /* SystemConfiguration.framework */,
E280BF1319E9FF5000D85595 /* CFNetwork.framework */,
);
name = "Mac Frameworks and Libraries";
sourceTree = "<group>";
};
E298C47719ED866100C76821 /* Tests */ = {
isa = PBXGroup;
children = (
E298C47919ED866100C76821 /* GCDTelnetServer_Tests.m */,
);
path = Tests;
sourceTree = "<group>";
};
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 = "<group>";
};
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 = "<group>";
};
E2B3CF1619E91825003ED065 /* iOS */ = {
isa = PBXGroup;
children = (
E2B3CF1719E91825003ED065 /* AppDelegate.h */,
E2B3CF1819E91825003ED065 /* AppDelegate.m */,
E2B3CF1919E91825003ED065 /* Info.plist */,
E2B3CF1A19E91825003ED065 /* main.m */,
);
path = iOS;
sourceTree = "<group>";
};
E2B3CF2E19E919AC003ED065 /* iOS Frameworks and Libraries */ = {
isa = PBXGroup;
children = (
E2B3CF2F19E919DD003ED065 /* UIKit.framework */,
E280BF1519E9FF5500D85595 /* CFNetwork.framework */,
);
name = "iOS Frameworks and Libraries";
sourceTree = "<group>";
};
/* 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
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0830"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "NO"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E298C46B19ED859F00C76821"
BuildableName = "Tests.xctest"
BlueprintName = "GCDTelnetServer (Tests)"
ReferencedContainer = "container:GCDTelnetServer.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "NO"
enableAddressSanitizer = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E298C46B19ED859F00C76821"
BuildableName = "Tests.xctest"
BlueprintName = "GCDTelnetServer (Tests)"
ReferencedContainer = "container:GCDTelnetServer.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
Bu
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
SYMBOL INDEX (3 symbols across 1 files) FILE: GCDTelnetServer/GCDTelnetServer.h type NSString (line 39) | typedef NSString* (^GCDTelnetStartHandler)(GCDTelnetConnection* connecti... type NSString (line 50) | typedef NSString* (^GCDTelnetLineHandler)(GCDTelnetConnection* connectio... type NSString (line 58) | typedef NSString* (^GCDTelnetCommandHandler)(GCDTelnetConnection* connec...
Condensed preview — 50 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (233K chars).
[
{
"path": ".clang-format",
"chars": 284,
"preview": "---\nBasedOnStyle: Google\nStandard: Cpp11\nColumnLimit: 0\nAlignTrailingComments: false\nNamespaceIndentation: All\nDerivePoi"
},
{
"path": ".gitignore",
"chars": 41,
"preview": ".DS_Store\nxcuserdata\nproject.xcworkspace\n"
},
{
"path": "CLT/main.m",
"chars": 2463,
"preview": "/*\n Copyright (c) 2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary forms"
},
{
"path": "Format-Source.sh",
"chars": 445,
"preview": "#!/bin/sh -ex\n\n# brew install clang-format\n\nCLANG_FORMAT_VERSION=`clang-format -version | awk '{ print $3 }'`\nif [[ \"$CL"
},
{
"path": "GCDNetworking/.clang-format",
"chars": 284,
"preview": "---\nBasedOnStyle: Google\nStandard: Cpp11\nColumnLimit: 0\nAlignTrailingComments: false\nNamespaceIndentation: All\nDerivePoi"
},
{
"path": "GCDNetworking/.gitignore",
"chars": 41,
"preview": ".DS_Store\nxcuserdata\nproject.xcworkspace\n"
},
{
"path": "GCDNetworking/.travis.yml",
"chars": 45,
"preview": "language: objective-c\nscript: ./Run-Tests.sh\n"
},
{
"path": "GCDNetworking/CLT/main.m",
"chars": 2188,
"preview": "/*\n Copyright (c) 2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary forms"
},
{
"path": "GCDNetworking/Format-Source.sh",
"chars": 443,
"preview": "#!/bin/sh -ex\n\n# brew install clang-format\n\nCLANG_FORMAT_VERSION=`clang-format -version | awk '{ print $3 }'`\nif [[ \"$CL"
},
{
"path": "GCDNetworking/GCDNetworking/GCDNetworking.h",
"chars": 1573,
"preview": "/*\n Copyright (c) 2012-2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary "
},
{
"path": "GCDNetworking/GCDNetworking/GCDNetworkingLoggingBridgePrivate.h",
"chars": 4090,
"preview": "/*\n Copyright (c) 2012-2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary "
},
{
"path": "GCDNetworking/GCDNetworking/GCDNetworkingPrivate.h",
"chars": 1781,
"preview": "/*\n Copyright (c) 2012-2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary "
},
{
"path": "GCDNetworking/GCDNetworking/GCDTCPClient.h",
"chars": 3704,
"preview": "/*\n Copyright (c) 2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary forms"
},
{
"path": "GCDNetworking/GCDNetworking/GCDTCPClient.m",
"chars": 5257,
"preview": "/*\n Copyright (c) 2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary forms"
},
{
"path": "GCDNetworking/GCDNetworking/GCDTCPConnection.h",
"chars": 5408,
"preview": "/*\n Copyright (c) 2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary forms"
},
{
"path": "GCDNetworking/GCDNetworking/GCDTCPConnection.m",
"chars": 15320,
"preview": "/*\n Copyright (c) 2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary forms"
},
{
"path": "GCDNetworking/GCDNetworking/GCDTCPPeer.h",
"chars": 4718,
"preview": "/*\n Copyright (c) 2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary forms"
},
{
"path": "GCDNetworking/GCDNetworking/GCDTCPPeer.m",
"chars": 6638,
"preview": "/*\n Copyright (c) 2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary forms"
},
{
"path": "GCDNetworking/GCDNetworking/GCDTCPServer.h",
"chars": 2820,
"preview": "/*\n Copyright (c) 2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary forms"
},
{
"path": "GCDNetworking/GCDNetworking/GCDTCPServer.m",
"chars": 8112,
"preview": "/*\n Copyright (c) 2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary forms"
},
{
"path": "GCDNetworking/GCDNetworking.xcodeproj/project.pbxproj",
"chars": 24584,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 48;\n\tobjects = {\n\n/* Begin PBXAggregateTarget sec"
},
{
"path": "GCDNetworking/GCDNetworking.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme",
"chars": 3535,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"0830\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "GCDNetworking/LICENSE",
"chars": 1476,
"preview": "Copyright (c) 2014, Pierre-Olivier Latour\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with "
},
{
"path": "GCDNetworking/README.md",
"chars": 374,
"preview": "Overview\n========\n\n[](https://travis-ci.o"
},
{
"path": "GCDNetworking/Run-Tests.sh",
"chars": 1151,
"preview": "#!/bin/bash -ex\n\nOSX_SDK=\"macosx\"\nif [ -z \"$TRAVIS\" ]; then\n IOS_SDK=\"iphoneos\"\nelse\n IOS_SDK=\"iphonesimulator\"\nfi\n\nOS"
},
{
"path": "GCDNetworking/Tests/GCDNetworking_Tests.m",
"chars": 5141,
"preview": "/*\n Copyright (c) 2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary forms"
},
{
"path": "GCDNetworking/iOS/AppDelegate.h",
"chars": 1604,
"preview": "/*\n Copyright (c) 2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary forms"
},
{
"path": "GCDNetworking/iOS/AppDelegate.m",
"chars": 2543,
"preview": "/*\n Copyright (c) 2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary forms"
},
{
"path": "GCDNetworking/iOS/Info.plist",
"chars": 1408,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "GCDNetworking/iOS/main.m",
"chars": 1640,
"preview": "/*\n Copyright (c) 2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary forms"
},
{
"path": "GCDTelnetServer/GCDTelnetConnection.h",
"chars": 6416,
"preview": "/*\n Copyright (c) 2012-2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary "
},
{
"path": "GCDTelnetServer/GCDTelnetConnection.m",
"chars": 15557,
"preview": "/*\n Copyright (c) 2012-2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary "
},
{
"path": "GCDTelnetServer/GCDTelnetLogging.h",
"chars": 6964,
"preview": "/*\n Copyright (c) 2012-2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary "
},
{
"path": "GCDTelnetServer/GCDTelnetLoggingBridgePrivate.h",
"chars": 4090,
"preview": "/*\n Copyright (c) 2012-2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary "
},
{
"path": "GCDTelnetServer/GCDTelnetPrivate.h",
"chars": 5695,
"preview": "/*\n Copyright (c) 2012-2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary "
},
{
"path": "GCDTelnetServer/GCDTelnetServer.h",
"chars": 3790,
"preview": "/*\n Copyright (c) 2012-2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary "
},
{
"path": "GCDTelnetServer/GCDTelnetServer.m",
"chars": 5729,
"preview": "/*\n Copyright (c) 2012-2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary "
},
{
"path": "GCDTelnetServer/NSMutableString+ANSI.h",
"chars": 2337,
"preview": "/*\n Copyright (c) 2012-2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary "
},
{
"path": "GCDTelnetServer/NSMutableString+ANSI.m",
"chars": 2096,
"preview": "/*\n Copyright (c) 2012-2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary "
},
{
"path": "GCDTelnetServer.podspec",
"chars": 1322,
"preview": "# http://guides.cocoapods.org/syntax/podspec.html\n# http://guides.cocoapods.org/making/getting-setup-with-trunk.html\n# $"
},
{
"path": "GCDTelnetServer.xcodeproj/project.pbxproj",
"chars": 29229,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 48;\n\tobjects = {\n\n/* Begin PBXAggregateTarget sec"
},
{
"path": "GCDTelnetServer.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme",
"chars": 3547,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"0830\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "LICENSE",
"chars": 1476,
"preview": "Copyright (c) 2014, Pierre-Olivier Latour\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with "
},
{
"path": "README.md",
"chars": 5051,
"preview": "Overview\n========\n\n[](http://cocoadocs.org/d"
},
{
"path": "Run-Tests.sh",
"chars": 1234,
"preview": "#!/bin/bash -ex\n\n# Run GCDNetworking tests first\npushd \"GCDNetworking\"\n./Run-Tests.sh\npopd\n\nOSX_SDK=\"macosx\"\nif [ -z \"$T"
},
{
"path": "Tests/GCDTelnetServer_Tests.m",
"chars": 6815,
"preview": "/*\n Copyright (c) 2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary forms"
},
{
"path": "iOS/AppDelegate.h",
"chars": 1604,
"preview": "/*\n Copyright (c) 2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary forms"
},
{
"path": "iOS/AppDelegate.m",
"chars": 3735,
"preview": "/*\n Copyright (c) 2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary forms"
},
{
"path": "iOS/Info.plist",
"chars": 1408,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "iOS/main.m",
"chars": 1640,
"preview": "/*\n Copyright (c) 2014, Pierre-Olivier Latour\n All rights reserved.\n \n Redistribution and use in source and binary forms"
}
]
About this extraction
This page contains the full source code of the swisspol/GCDTelnetServer GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 50 files (213.7 KB), approximately 58.2k tokens, and a symbol index with 3 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.