summaryrefslogtreecommitdiff
path: root/cpp/src/Ice/ios
diff options
context:
space:
mode:
authorBenoit Foucher <benoit@zeroc.com>2016-05-31 08:59:20 +0200
committerBenoit Foucher <benoit@zeroc.com>2016-05-31 08:59:20 +0200
commit9f99bad130508d529814c778852621f6a1b814cc (patch)
tree4fa8328539c80f719cca38a0fb7f67b2995618fc /cpp/src/Ice/ios
parentPHP 7 fix for printing Exception info (diff)
downloadice-9f99bad130508d529814c778852621f6a1b814cc.tar.bz2
ice-9f99bad130508d529814c778852621f6a1b814cc.tar.xz
ice-9f99bad130508d529814c778852621f6a1b814cc.zip
Added support for building Xcode SDK, added iAP transport
Diffstat (limited to 'cpp/src/Ice/ios')
-rw-r--r--cpp/src/Ice/ios/Notifications.mm138
-rw-r--r--cpp/src/Ice/ios/RegisterPluginsInit.cpp30
-rw-r--r--cpp/src/Ice/ios/StreamAcceptor.cpp160
-rw-r--r--cpp/src/Ice/ios/StreamAcceptor.h54
-rw-r--r--cpp/src/Ice/ios/StreamConnector.cpp169
-rw-r--r--cpp/src/Ice/ios/StreamConnector.h52
-rw-r--r--cpp/src/Ice/ios/StreamEndpointI.cpp820
-rw-r--r--cpp/src/Ice/ios/StreamEndpointI.h170
-rw-r--r--cpp/src/Ice/ios/StreamTransceiver.cpp793
-rw-r--r--cpp/src/Ice/ios/StreamTransceiver.h102
-rw-r--r--cpp/src/Ice/ios/iAPConnector.h58
-rw-r--r--cpp/src/Ice/ios/iAPConnector.mm153
-rw-r--r--cpp/src/Ice/ios/iAPEndpointI.h109
-rw-r--r--cpp/src/Ice/ios/iAPEndpointI.mm640
-rw-r--r--cpp/src/Ice/ios/iAPTransceiver.h85
-rw-r--r--cpp/src/Ice/ios/iAPTransceiver.mm490
16 files changed, 4023 insertions, 0 deletions
diff --git a/cpp/src/Ice/ios/Notifications.mm b/cpp/src/Ice/ios/Notifications.mm
new file mode 100644
index 00000000000..bab9a73824c
--- /dev/null
+++ b/cpp/src/Ice/ios/Notifications.mm
@@ -0,0 +1,138 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2016 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice Touch is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#import <Ice/Config.h>
+
+#if TARGET_OS_IPHONE != 0
+
+#import <Foundation/NSObject.h>
+#import <Foundation/NSNotification.h>
+#import <UIKit/UIApplication.h>
+
+#include <Ice/ConnectionFactory.h>
+
+#include <set>
+
+using namespace std;
+using namespace IceInternal;
+
+namespace IceInternal
+{
+
+bool registerForBackgroundNotification(const IncomingConnectionFactoryPtr&);
+void unregisterForBackgroundNotification(const IncomingConnectionFactoryPtr&);
+
+}
+
+namespace
+{
+
+class Observer
+{
+public:
+
+ Observer() : _background(false)
+ {
+ [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification
+ object:nil
+ queue:nil
+ usingBlock:^(NSNotification*)
+ {
+ didEnterBackground();
+ }];
+
+ [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillEnterForegroundNotification
+ object:nil
+ queue:nil
+ usingBlock:^(NSNotification*)
+ {
+ willEnterForeground();
+ }];
+ }
+
+ bool
+ add(const IncomingConnectionFactoryPtr& factory)
+ {
+ IceUtil::Mutex::Lock sync(_mutex);
+ _factories.insert(factory);
+ if(_background)
+ {
+ factory->stopAcceptor();
+ }
+ else
+ {
+ factory->startAcceptor();
+ }
+ return _background;
+ }
+
+ void
+ remove(const IncomingConnectionFactoryPtr& factory)
+ {
+ IceUtil::Mutex::Lock sync(_mutex);
+ _factories.erase(factory);
+ }
+
+ void
+ didEnterBackground()
+ {
+ IceUtil::Mutex::Lock sync(_mutex);
+
+ //
+ // Notify all the incoming connection factories that we are
+ // entering the background mode.
+ //
+ for(set<IncomingConnectionFactoryPtr>::const_iterator p = _factories.begin(); p != _factories.end(); ++p)
+ {
+ (*p)->stopAcceptor();
+ }
+ _background = true;
+ }
+
+ void
+ willEnterForeground()
+ {
+ IceUtil::Mutex::Lock sync(_mutex);
+
+ //
+ // Notify all the incoming connection factories that we are
+ // entering the foreground mode.
+ //
+ _background = false;
+ for(set<IncomingConnectionFactoryPtr>::const_iterator p = _factories.begin(); p != _factories.end(); ++p)
+ {
+ (*p)->startAcceptor();
+ }
+ }
+
+private:
+
+ IceUtil::Mutex _mutex;
+ bool _background;
+ set<IncomingConnectionFactoryPtr> _factories;
+};
+
+
+}
+
+static Observer* observer = new Observer();
+
+bool
+IceInternal::registerForBackgroundNotification(const IncomingConnectionFactoryPtr& factory)
+{
+ return observer->add(factory);
+}
+
+void
+IceInternal::unregisterForBackgroundNotification(const IncomingConnectionFactoryPtr& factory)
+{
+ observer->remove(factory);
+}
+
+#endif
diff --git a/cpp/src/Ice/ios/RegisterPluginsInit.cpp b/cpp/src/Ice/ios/RegisterPluginsInit.cpp
new file mode 100644
index 00000000000..712f4dcc765
--- /dev/null
+++ b/cpp/src/Ice/ios/RegisterPluginsInit.cpp
@@ -0,0 +1,30 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2016 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#include <Ice/RegisterPluginsInit.h>
+#include <Ice/CommunicatorF.h>
+#include <Ice/Initialize.h>
+
+extern "C"
+{
+
+Ice::Plugin* createIceUDP(const Ice::CommunicatorPtr&, const std::string&, const Ice::StringSeq&);
+Ice::Plugin* createIceTCP(const Ice::CommunicatorPtr&, const std::string&, const Ice::StringSeq&);
+Ice::Plugin* createIceSSL(const Ice::CommunicatorPtr&, const std::string&, const Ice::StringSeq&);
+Ice::Plugin* createIceIAP(const Ice::CommunicatorPtr&, const std::string&, const Ice::StringSeq&);
+
+};
+
+IceInternal::RegisterPluginsInit::RegisterPluginsInit()
+{
+ Ice::registerPluginFactory("IceUDP", createIceUDP, true);
+ Ice::registerPluginFactory("IceTCP", createIceTCP, true);
+ Ice::registerPluginFactory("IceSSL", createIceSSL, true);
+ Ice::registerPluginFactory("IceIAP", createIceIAP, true);
+}
diff --git a/cpp/src/Ice/ios/StreamAcceptor.cpp b/cpp/src/Ice/ios/StreamAcceptor.cpp
new file mode 100644
index 00000000000..3d1a4a956b0
--- /dev/null
+++ b/cpp/src/Ice/ios/StreamAcceptor.cpp
@@ -0,0 +1,160 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2016 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice Touch is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#include "StreamTransceiver.h"
+#include "StreamEndpointI.h"
+#include "StreamAcceptor.h"
+
+#include <IceUtil/StringUtil.h>
+
+#include <Ice/Instance.h>
+#include <Ice/Network.h>
+#include <Ice/Exception.h>
+#include <Ice/Properties.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+
+using namespace std;
+using namespace Ice;
+using namespace IceInternal;
+
+NativeInfoPtr
+IceObjC::StreamAcceptor::getNativeInfo()
+{
+ return this;
+}
+
+void
+IceObjC::StreamAcceptor::close()
+{
+ if(_fd != INVALID_SOCKET)
+ {
+ closeSocketNoThrow(_fd);
+ _fd = INVALID_SOCKET;
+ }
+}
+
+EndpointIPtr
+IceObjC::StreamAcceptor::listen()
+{
+ try
+ {
+ const_cast<Address&>(_addr) = doBind(_fd, _addr);
+ doListen(_fd, _backlog);
+ }
+ catch(...)
+ {
+ _fd = INVALID_SOCKET;
+ throw;
+ }
+
+ _endpoint = _endpoint->endpoint(this);
+ return _endpoint;
+}
+
+TransceiverPtr
+IceObjC::StreamAcceptor::accept()
+{
+ SOCKET fd = doAccept(_fd);
+ setBlock(fd, false);
+ setTcpBufSize(fd, _instance);
+
+ //
+ // Create the read/write streams
+ //
+ CFReadStreamRef readStream = nil;
+ CFWriteStreamRef writeStream = nil;
+ try
+ {
+ CFStreamCreatePairWithSocket(NULL, fd, &readStream, &writeStream);
+ _instance->setupStreams(readStream, writeStream, true, "");
+ return new StreamTransceiver(_instance, readStream, writeStream, fd);
+ }
+ catch(const Ice::LocalException& ex)
+ {
+ if(fd != INVALID_SOCKET)
+ {
+ closeSocketNoThrow(fd);
+ }
+ if(readStream)
+ {
+ CFRelease(readStream);
+ }
+ if(writeStream)
+ {
+ CFRelease(writeStream);
+ }
+ throw;
+ }
+}
+
+string
+IceObjC::StreamAcceptor::protocol() const
+{
+ return _instance->protocol();
+}
+
+string
+IceObjC::StreamAcceptor::toString() const
+{
+ return addrToString(_addr);
+}
+
+string
+IceObjC::StreamAcceptor::toDetailedString() const
+{
+ ostringstream os;
+ os << "local address = " << toString();
+ vector<string> intfs = getHostsForEndpointExpand(inetAddrToString(_addr), _instance->protocolSupport(), true);
+ if(!intfs.empty())
+ {
+ os << "\nlocal interfaces = ";
+ os << IceUtilInternal::joinString(intfs, ", ");
+ }
+ return os.str();
+}
+
+int
+IceObjC::StreamAcceptor::effectivePort() const
+{
+ return getPort(_addr);
+}
+
+IceObjC::StreamAcceptor::StreamAcceptor(const StreamEndpointIPtr& endpoint,
+ const InstancePtr& instance,
+ const string& host,
+ int port) :
+ _endpoint(endpoint),
+ _instance(instance),
+ _addr(getAddressForServer(host, port, instance->protocolSupport(), instance->preferIPv6()))
+{
+#ifdef SOMAXCONN
+ _backlog = instance->properties()->getPropertyAsIntWithDefault("Ice.TCP.Backlog", SOMAXCONN);
+#else
+ _backlog = instance->properties()->getPropertyAsIntWithDefault("Ice.TCP.Backlog", 511);
+#endif
+
+ try
+ {
+ _fd = createSocket(false, _addr);
+ setBlock(_fd, false);
+ setTcpBufSize(_fd, _instance);
+ setReuseAddress(_fd, true);
+ }
+ catch(...)
+ {
+ _fd = INVALID_SOCKET;
+ throw;
+ }
+}
+
+IceObjC::StreamAcceptor::~StreamAcceptor()
+{
+ assert(_fd == INVALID_SOCKET);
+}
diff --git a/cpp/src/Ice/ios/StreamAcceptor.h b/cpp/src/Ice/ios/StreamAcceptor.h
new file mode 100644
index 00000000000..5997ac7c6db
--- /dev/null
+++ b/cpp/src/Ice/ios/StreamAcceptor.h
@@ -0,0 +1,54 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2016 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice Touch is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#ifndef ICE_STREAM_ACCEPTOR_H
+#define ICE_STREAM_ACCEPTOR_H
+
+#include <Ice/TransceiverF.h>
+#include <Ice/Acceptor.h>
+
+namespace IceObjC
+{
+
+class StreamEndpointI;
+#ifdef ICE_CPP11_MAPPING // C++11 mapping
+typedef ::std::shared_ptr<StreamEndpointI> StreamEndpointIPtr;
+#else
+typedef IceUtil::Handle<StreamEndpointI> StreamEndpointIPtr;
+#endif
+
+class StreamAcceptor : public IceInternal::Acceptor, public IceInternal::NativeInfo
+{
+public:
+
+ virtual IceInternal::NativeInfoPtr getNativeInfo();
+ virtual void close();
+ virtual IceInternal::EndpointIPtr listen();
+ virtual IceInternal::TransceiverPtr accept();
+ virtual std::string protocol() const;
+ virtual std::string toString() const;
+ virtual std::string toDetailedString() const;
+
+ int effectivePort() const;
+
+private:
+
+ StreamAcceptor(const StreamEndpointIPtr&, const InstancePtr&, const std::string&, int);
+ virtual ~StreamAcceptor();
+ friend class StreamEndpointI;
+
+ StreamEndpointIPtr _endpoint;
+ InstancePtr _instance;
+ int _backlog;
+ IceInternal::Address _addr;
+};
+
+}
+
+#endif
diff --git a/cpp/src/Ice/ios/StreamConnector.cpp b/cpp/src/Ice/ios/StreamConnector.cpp
new file mode 100644
index 00000000000..a5b87b07db0
--- /dev/null
+++ b/cpp/src/Ice/ios/StreamConnector.cpp
@@ -0,0 +1,169 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2016 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice Touch is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#include "StreamTransceiver.h"
+#include "StreamEndpointI.h"
+#include "StreamConnector.h"
+
+#include <Ice/Network.h>
+#include <Ice/Exception.h>
+#include <Ice/Properties.h>
+#include <Ice/NetworkProxy.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+
+using namespace std;
+using namespace Ice;
+using namespace IceInternal;
+
+TransceiverPtr
+IceObjC::StreamConnector::connect()
+{
+ CFReadStreamRef readStream = nil;
+ CFWriteStreamRef writeStream = nil;
+ try
+ {
+ CFStringRef h = CFStringCreateWithCString(NULL, _host.c_str(), kCFStringEncodingUTF8);
+ CFHostRef host = CFHostCreateWithName(NULL, h);
+ CFRelease(h);
+ CFStreamCreatePairWithSocketToCFHost(NULL, host, _port, &readStream, &writeStream);
+ CFRelease(host);
+
+ _instance->setupStreams(readStream, writeStream, false, _host);
+ return new StreamTransceiver(_instance, readStream, writeStream, _host, _port);
+ }
+ catch(const Ice::LocalException& ex)
+ {
+ if(readStream)
+ {
+ CFRelease(readStream);
+ }
+ if(writeStream)
+ {
+ CFRelease(writeStream);
+ }
+ throw;
+ }
+}
+
+Short
+IceObjC::StreamConnector::type() const
+{
+ return _instance->type();
+}
+
+string
+IceObjC::StreamConnector::toString() const
+{
+ string proxyHost = _instance->proxyHost();
+ ostringstream os;
+ if(!proxyHost.empty())
+ {
+ os << proxyHost << ":" << _instance->proxyPort();
+ }
+ else
+ {
+ os << _host << ":" << _port;
+ }
+ return os.str();
+}
+
+bool
+IceObjC::StreamConnector::operator==(const IceInternal::Connector& r) const
+{
+ const StreamConnector* p = dynamic_cast<const StreamConnector*>(&r);
+ if(!p)
+ {
+ return false;
+ }
+
+ if(_timeout != p->_timeout)
+ {
+ return false;
+ }
+
+ if(_connectionId != p->_connectionId)
+ {
+ return false;
+ }
+
+ if(_host != p->_host)
+ {
+ return false;
+ }
+
+ if(_port != p->_port)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool
+IceObjC::StreamConnector::operator!=(const IceInternal::Connector& r) const
+{
+ return !operator==(r);
+}
+
+bool
+IceObjC::StreamConnector::operator<(const IceInternal::Connector& r) const
+{
+ const StreamConnector* p = dynamic_cast<const StreamConnector*>(&r);
+ if(!p)
+ {
+ return type() < r.type();
+ }
+
+ if(_timeout < p->_timeout)
+ {
+ return true;
+ }
+ else if(p->_timeout < _timeout)
+ {
+ return false;
+ }
+
+ if(_connectionId < p->_connectionId)
+ {
+ return true;
+ }
+ else if(p->_connectionId < _connectionId)
+ {
+ return false;
+ }
+
+ if(_host < p->_host)
+ {
+ return true;
+ }
+ else if(p->_host < _host)
+ {
+ return false;
+ }
+
+ return _port < p->_port;
+}
+
+IceObjC::StreamConnector::StreamConnector(const InstancePtr& instance,
+ const string& host,
+ Ice::Int port,
+ Ice::Int timeout,
+ const string& connectionId) :
+ _instance(instance),
+ _host(host.empty() ? string("127.0.0.1") : host),
+ _port(port),
+ _timeout(timeout),
+ _connectionId(connectionId)
+{
+}
+
+IceObjC::StreamConnector::~StreamConnector()
+{
+}
diff --git a/cpp/src/Ice/ios/StreamConnector.h b/cpp/src/Ice/ios/StreamConnector.h
new file mode 100644
index 00000000000..91d806fd707
--- /dev/null
+++ b/cpp/src/Ice/ios/StreamConnector.h
@@ -0,0 +1,52 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2016 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice Touch is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#ifndef ICE_STREAM_CONNECTOR_H
+#define ICE_STREAM_CONNECTOR_H
+
+#include <Ice/TransceiverF.h>
+#include <Ice/Connector.h>
+
+namespace IceObjC
+{
+
+class StreamEndpointI;
+
+class Instance;
+typedef IceUtil::Handle<Instance> InstancePtr;
+
+class StreamConnector : public IceInternal::Connector
+{
+public:
+
+ virtual IceInternal::TransceiverPtr connect();
+
+ virtual Ice::Short type() const;
+ virtual std::string toString() const;
+
+ virtual bool operator==(const IceInternal::Connector&) const;
+ virtual bool operator!=(const IceInternal::Connector&) const;
+ virtual bool operator<(const IceInternal::Connector&) const;
+
+private:
+
+ StreamConnector(const InstancePtr&, const std::string&, Ice::Int, Ice::Int, const std::string&);
+ virtual ~StreamConnector();
+ friend class StreamEndpointI;
+
+ const InstancePtr _instance;
+ const std::string _host;
+ const Ice::Int _port;
+ const Ice::Int _timeout;
+ const std::string _connectionId;
+};
+
+}
+
+#endif
diff --git a/cpp/src/Ice/ios/StreamEndpointI.cpp b/cpp/src/Ice/ios/StreamEndpointI.cpp
new file mode 100644
index 00000000000..0be2584140c
--- /dev/null
+++ b/cpp/src/Ice/ios/StreamEndpointI.cpp
@@ -0,0 +1,820 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2016 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice Touch is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#include "StreamEndpointI.h"
+#include "StreamAcceptor.h"
+#include "StreamConnector.h"
+
+#include <IceUtil/StringUtil.h>
+
+#include <Ice/Network.h>
+#include <Ice/InputStream.h>
+#include <Ice/OutputStream.h>
+#include <Ice/LocalException.h>
+#include <Ice/Communicator.h>
+#include <Ice/EndpointFactoryManager.h>
+#include <Ice/Properties.h>
+#include <Ice/HashUtil.h>
+#include <Ice/NetworkProxy.h>
+
+#include <IceSSL/EndpointInfo.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/Security.h>
+
+#include <fstream>
+
+using namespace std;
+using namespace Ice;
+using namespace IceInternal;
+
+extern "C"
+{
+
+Plugin*
+createIceTCP(const CommunicatorPtr& com, const string&, const StringSeq&)
+{
+ IceObjC::InstancePtr tcpInstance = new IceObjC::Instance(com, TCPEndpointType, "tcp", false);
+ return new EndpointFactoryPlugin(com, new IceObjC::StreamEndpointFactory(tcpInstance));
+}
+
+Plugin*
+createIceSSL(const CommunicatorPtr& com, const string&, const StringSeq&)
+{
+ IceObjC::InstancePtr sslInstance = new IceObjC::Instance(com, SSLEndpointType, "ssl", true);
+ return new EndpointFactoryPlugin(com, new IceObjC::StreamEndpointFactory(sslInstance));
+}
+
+}
+
+namespace Ice
+{
+
+void
+registerIceSSL(bool)
+{
+ // Nothing to do, we always register IceSSL
+}
+
+}
+
+inline CFStringRef
+toCFString(const string& s)
+{
+ return CFStringCreateWithCString(NULL, s.c_str(), kCFStringEncodingUTF8);
+}
+
+inline int
+hexValue(char c)
+{
+ if(c >= '0' && c <= '9')
+ {
+ return c - '0';
+ }
+ else if(c >= 'A' && c <= 'F')
+ {
+ return (c - 'A') + 10;
+ }
+ else if(c >= 'a' && c <= 'f')
+ {
+ return (c - 'a') + 10;
+ }
+ return -1;
+}
+
+inline CFDataRef
+parseKey(const string& keyStr)
+{
+ int i = 0, j = 0;
+ const char* m = keyStr.c_str();
+ CFMutableDataRef data = CFDataCreateMutable(0, 160);
+ unsigned char buf[160];
+ while(i < (int)keyStr.size())
+ {
+ if(isspace(m[i]) || m[i] == ':')
+ {
+ ++i;
+ continue;
+ }
+ else if(i == (int)keyStr.size() - 1)
+ {
+ CFRelease(data);
+ return 0; // Not enough bytes.
+ }
+
+ int vh = hexValue(m[i++]);
+ int vl = hexValue(m[i++]);
+ if(vh < 0 || vl < 0)
+ {
+ CFRelease(data);
+ return 0;
+ }
+ buf[j] = vh << 4;
+ buf[j++] += vl;
+
+ if(j == sizeof(buf))
+ {
+ CFDataAppendBytes(data, (UInt8*)buf, j);
+ j = 0;
+ }
+ }
+
+ if(j > 0)
+ {
+ CFDataAppendBytes(data, buf, j);
+ }
+
+ return data;
+}
+
+namespace
+{
+
+CFDataRef
+readCert(const string& defaultDir, const string& certFile)
+{
+ string path;
+ CFURLRef url = 0;
+ CFBundleRef bundle = CFBundleGetMainBundle();
+ if(bundle)
+ {
+ CFStringRef resourceName = toCFString(certFile);
+ CFStringRef subDirName = toCFString(defaultDir);
+ url = CFBundleCopyResourceURL(bundle, resourceName, 0, subDirName);
+ CFRelease(resourceName);
+ CFRelease(subDirName);
+
+ UInt8 filePath[PATH_MAX];
+ if(CFURLGetFileSystemRepresentation(url, true, filePath, sizeof(filePath)))
+ {
+ path = string(reinterpret_cast<char*>(filePath));
+ }
+ }
+
+ if(!url || path.empty())
+ {
+ path = defaultDir.empty() ? certFile : defaultDir + "/" + certFile;
+ }
+
+ FILE *file = fopen(path.c_str(), "rb");
+ if(!file)
+ {
+ ostringstream os;
+ os << "IceSSL: unable to open file " << certFile << " (error = " << IceUtilInternal::lastErrorToString() << ")";
+ throw InitializationException(__FILE__, __LINE__, os.str());
+ }
+
+ fseek(file, 0, SEEK_END);
+ unsigned long size = ftell(file);
+ fseek(file, 0, SEEK_SET);
+ CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, size);
+ CFDataSetLength(data, size);
+ if(fread(CFDataGetMutableBytePtr(data), 1, size, file) != size)
+ {
+ CFRelease(data);
+ ostringstream os;
+ os << "IceSSL: error while reading file " << certFile;
+ throw InitializationException(__FILE__, __LINE__, os.str());
+ }
+ fclose(file);
+ return data;
+}
+
+}
+
+IceObjC::Instance::Instance(const Ice::CommunicatorPtr& com, Short type, const string& protocol, bool secure) :
+ ProtocolInstance(com, type, protocol, secure),
+ _voip(com->getProperties()->getPropertyAsIntWithDefault("Ice.Voip", 0) > 0),
+ _communicator(com),
+ _serverSettings(0),
+ _clientSettings(0),
+ _proxySettings(0),
+ _certificateAuthorities(0),
+ _trustOnlyKeyID(0)
+{
+ const Ice::PropertiesPtr properties = com->getProperties();
+ if(secure)
+ {
+ _clientSettings = CFDictionaryCreateMutable(0, 1, &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+ string defaultDir = properties->getProperty("IceSSL.DefaultDir");
+ string certAuthFile = properties->getProperty("IceSSL.CAs");
+ if(certAuthFile.empty())
+ {
+ certAuthFile = properties->getProperty("IceSSL.CertAuthFile");
+ }
+ string certFile = properties->getProperty("IceSSL.CertFile");
+
+ OSStatus err;
+ if(!certAuthFile.empty())
+ {
+ CFDataRef cert = readCert(defaultDir, certAuthFile);
+ if(!cert)
+ {
+ InitializationException ex(__FILE__, __LINE__);
+ ex.reason = "IceSSL: unable to open file " + certAuthFile;
+ throw ex;
+ }
+
+ SecCertificateRef result = SecCertificateCreateWithData(0, cert);
+ CFRelease(cert);
+ if(!result)
+ {
+ InitializationException ex(__FILE__, __LINE__);
+ ex.reason = "IceSSL: certificate " + certAuthFile + " is not a valid DER-encoded certificate";
+ throw ex;
+ }
+
+ SecCertificateRef certs[] = { result };
+ _certificateAuthorities = CFArrayCreate(0, (const void**)certs, 1, &kCFTypeArrayCallBacks);
+ CFRelease(result);
+
+ // The root CA will be validated by the transceiver.
+ // NOTE: on the iPhone, setting kCFStreamSSLAllowsAnyRoot = true isn't enough.
+ //CFDictionarySetValue(_clientSettings, kCFStreamSSLAllowsAnyRoot, kCFBooleanTrue);
+ CFDictionarySetValue(_clientSettings, kCFStreamSSLValidatesCertificateChain, kCFBooleanFalse);
+ }
+ else if(properties->getPropertyAsInt("IceSSL.UsePlatformCAs") <= 0)
+ {
+ // Setup an empty list of Root CAs to not use the system root CAs.
+ _certificateAuthorities = CFArrayCreate(0, 0, 0, 0);
+ }
+
+ if(!certFile.empty())
+ {
+ CFDataRef cert = readCert(defaultDir, certFile);
+ if(!cert)
+ {
+ InitializationException ex(__FILE__, __LINE__);
+ ex.reason = "IceSSL: unable to open file " + certFile;
+ throw ex;
+ }
+
+ CFMutableDictionaryRef settings = CFDictionaryCreateMutable(0, 1, &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ CFStringRef password = toCFString(properties->getProperty("IceSSL.Password"));
+ CFDictionarySetValue(settings, kSecImportExportPassphrase, password);
+ CFRelease(password);
+
+ CFArrayRef items = 0;
+ err = SecPKCS12Import(cert, settings, &items);
+ CFRelease(cert);
+ CFRelease(settings);
+ if(err != noErr)
+ {
+ ostringstream os;
+ os << "IceSSL: unable to import certificate from file " << certFile << " (error = " << err << ")";
+ throw InitializationException(__FILE__, __LINE__, os.str());
+ }
+
+ SecIdentityRef identity = 0;
+ if(CFArrayGetCount(items) > 0)
+ {
+ identity = (SecIdentityRef)CFDictionaryGetValue((CFDictionaryRef)CFArrayGetValueAtIndex(items, 0),
+ kSecImportItemIdentity);
+ }
+ if(identity == 0)
+ {
+ ostringstream os;
+ os << "IceSSL: couldn't find identity in file " << certFile << " (error = " << err << ")";
+ throw InitializationException(__FILE__, __LINE__, os.str());
+ }
+ CFRetain(identity);
+ CFRelease(items);
+
+ SecIdentityRef identities[] = { identity };
+ items = CFArrayCreate(0, (const void**)identities, 1, &kCFTypeArrayCallBacks);
+ CFDictionarySetValue(_clientSettings, kCFStreamSSLCertificates, items);
+ CFRelease(identity);
+ CFRelease(items);
+ }
+
+ string trustOnly = properties->getProperty("IceSSL.TrustOnly.Client");
+ if(!trustOnly.empty())
+ {
+ _trustOnlyKeyID = parseKey(trustOnly);
+ if(!_trustOnlyKeyID)
+ {
+ ostringstream os;
+ os << "IceSSL: invalid `IceSSL.TrustOnly.Client' property value";
+ throw InitializationException(__FILE__, __LINE__, os.str());
+ }
+ }
+
+ _serverSettings = CFDictionaryCreateMutableCopy(0, 0, _clientSettings);
+ CFDictionarySetValue(_serverSettings, kCFStreamSSLIsServer, kCFBooleanTrue);
+ }
+
+ //
+ // Proxy settings
+ //
+ _proxyHost = properties->getProperty("Ice.SOCKSProxyHost");
+ if(!_proxyHost.empty())
+ {
+#if TARGET_IPHONE_SIMULATOR != 0
+ throw Ice::FeatureNotSupportedException(__FILE__, __LINE__, "SOCKS proxy not supported");
+#endif
+ _proxySettings = CFDictionaryCreateMutable(0, 3, &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+ _proxyPort = properties->getPropertyAsIntWithDefault("Ice.SOCKSProxyPort", 1080);
+
+ CFStringRef host = toCFString(_proxyHost);
+ CFDictionarySetValue(_proxySettings, kCFStreamPropertySOCKSProxyHost, host);
+ CFRelease(host);
+
+ CFNumberRef port = CFNumberCreate(0, kCFNumberSInt32Type, &_proxyPort);
+ CFDictionarySetValue(_proxySettings, kCFStreamPropertySOCKSProxyPort, port);
+ CFRelease(port);
+
+ CFDictionarySetValue(_proxySettings, kCFStreamPropertySOCKSVersion, kCFStreamSocketSOCKSVersion4);
+ }
+}
+
+IceObjC::Instance::~Instance()
+{
+ if(_trustOnlyKeyID)
+ {
+ CFRelease(_trustOnlyKeyID);
+ }
+ if(_serverSettings)
+ {
+ CFRelease(_serverSettings);
+ }
+ if(_clientSettings)
+ {
+ CFRelease(_clientSettings);
+ }
+ if(_certificateAuthorities)
+ {
+ CFRelease(_certificateAuthorities);
+ }
+ if(_proxySettings)
+ {
+ CFRelease(_proxySettings);
+ }
+}
+
+void
+IceObjC::Instance::setupStreams(CFReadStreamRef readStream,
+ CFWriteStreamRef writeStream,
+ bool server,
+ const string& host) const
+{
+ if(_voip)
+ {
+#if TARGET_IPHONE_SIMULATOR == 0
+ if(!CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP) ||
+ !CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP))
+ {
+ throw Ice::SyscallException(__FILE__, __LINE__);
+ }
+#endif
+ }
+
+ if(!server && _proxySettings)
+ {
+ if(!CFReadStreamSetProperty(readStream, kCFStreamPropertySOCKSProxy, _proxySettings) ||
+ !CFWriteStreamSetProperty(writeStream, kCFStreamPropertySOCKSProxy, _proxySettings))
+ {
+ throw Ice::SyscallException(__FILE__, __LINE__);
+ }
+ }
+
+ if(secure())
+ {
+ CFDictionaryRef settings = server ? _serverSettings : _clientSettings;
+
+ if(!CFReadStreamSetProperty(readStream, kCFStreamPropertySocketSecurityLevel,
+ kCFStreamSocketSecurityLevelNegotiatedSSL) ||
+ !CFWriteStreamSetProperty(writeStream, kCFStreamPropertySocketSecurityLevel,
+ kCFStreamSocketSecurityLevelNegotiatedSSL))
+ {
+ throw Ice::SecurityException(__FILE__, __LINE__, "couldn't set security level");
+ }
+
+ if(!server && properties()->getPropertyAsIntWithDefault("IceSSL.CheckCertName", 1))
+ {
+ settings = CFDictionaryCreateMutableCopy(0, 0, settings);
+
+ CFStringRef h = toCFString(host);
+ CFDictionarySetValue((CFMutableDictionaryRef)settings, kCFStreamSSLPeerName, h);
+ CFRelease(h);
+ }
+ else
+ {
+ CFRetain(settings);
+ }
+
+ if(!CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, settings) ||
+ !CFWriteStreamSetProperty(writeStream, kCFStreamPropertySSLSettings, settings))
+ {
+ CFRelease(settings);
+ throw Ice::SecurityException(__FILE__, __LINE__, "couldn't set security options");
+ }
+ CFRelease(settings);
+ }
+}
+
+IceObjC::Instance*
+IceObjC::Instance::clone(const ProtocolInstancePtr& instance)
+{
+ return new Instance(_communicator, instance->type(), instance->protocol(), instance->secure());
+}
+
+IceObjC::StreamEndpointI::StreamEndpointI(const InstancePtr& instance, const string& ho, Int po,
+ const Address& sourceAddr, Int ti, const string& conId, bool co) :
+ IceInternal::IPEndpointI(instance, ho, po, sourceAddr, conId),
+ _instance(instance),
+ _timeout(ti),
+ _compress(co)
+{
+}
+
+IceObjC::StreamEndpointI::StreamEndpointI(const InstancePtr& instance) :
+ IceInternal::IPEndpointI(instance),
+ _instance(instance),
+ _timeout(instance->defaultTimeout()),
+ _compress(false)
+{
+}
+
+IceObjC::StreamEndpointI::StreamEndpointI(const InstancePtr& instance, Ice::InputStream* s) :
+ IPEndpointI(instance, s),
+ _instance(instance),
+ _timeout(-1),
+ _compress(false)
+{
+ s->read(const_cast<Int&>(_timeout));
+ s->read(const_cast<bool&>(_compress));
+}
+
+EndpointInfoPtr
+IceObjC::StreamEndpointI::getInfo() const
+{
+ IPEndpointInfoPtr info;
+ if(_instance->secure())
+ {
+ info = ICE_MAKE_SHARED(InfoI<IceSSL::EndpointInfo>, ICE_DYNAMIC_CAST(StreamEndpointI, shared_from_this()));
+ }
+ else
+ {
+ info = ICE_MAKE_SHARED(InfoI<Ice::TCPEndpointInfo>, ICE_DYNAMIC_CAST(StreamEndpointI, shared_from_this()));
+ }
+ fillEndpointInfo(info.get());
+ return info;
+}
+
+EndpointInfoPtr
+IceObjC::StreamEndpointI::getWSInfo(const string& resource) const
+{
+ IPEndpointInfoPtr info;
+ if(_instance->secure())
+ {
+ IceSSL::WSSEndpointInfoPtr i;
+ i = ICE_MAKE_SHARED(InfoI<IceSSL::WSSEndpointInfo>, ICE_DYNAMIC_CAST(StreamEndpointI, shared_from_this()));
+ i->resource = resource;
+ info = i;
+ }
+ else
+ {
+ Ice::WSEndpointInfoPtr i;
+ i = ICE_MAKE_SHARED(InfoI<Ice::WSEndpointInfo>, ICE_DYNAMIC_CAST(StreamEndpointI, shared_from_this()));
+ i->resource = resource;
+ info = i;
+ }
+ fillEndpointInfo(info.get());
+ return info;
+}
+
+Int
+IceObjC::StreamEndpointI::timeout() const
+{
+ return _timeout;
+}
+
+EndpointIPtr
+IceObjC::StreamEndpointI::timeout(Int t) const
+{
+ if(t == _timeout)
+ {
+ return shared_from_this();
+ }
+ else
+ {
+ return ICE_MAKE_SHARED(StreamEndpointI, _instance, _host, _port, _sourceAddr, t, _connectionId, _compress);
+ }
+}
+
+bool
+IceObjC::StreamEndpointI::compress() const
+{
+ return _compress;
+}
+
+EndpointIPtr
+IceObjC::StreamEndpointI::compress(bool c) const
+{
+ if(c == _compress)
+ {
+ return shared_from_this();
+ }
+ else
+ {
+ return ICE_MAKE_SHARED(StreamEndpointI, _instance, _host, _port, _sourceAddr, _timeout, _connectionId, c);
+ }
+}
+
+bool
+IceObjC::StreamEndpointI::datagram() const
+{
+ return false;
+}
+
+bool
+IceObjC::StreamEndpointI::secure() const
+{
+ return _instance->secure();
+}
+
+void
+IceObjC::StreamEndpointI::connectors_async(Ice::EndpointSelectionType selType, const EndpointI_connectorsPtr& cb) const
+{
+ vector<ConnectorPtr> connectors;
+ connectors.push_back(new StreamConnector(_instance, _host, _port, _timeout, _connectionId));
+ cb->connectors(connectors);
+}
+
+TransceiverPtr
+IceObjC::StreamEndpointI::transceiver() const
+{
+ return 0;
+}
+
+AcceptorPtr
+IceObjC::StreamEndpointI::acceptor(const string&) const
+{
+ return new StreamAcceptor(ICE_DYNAMIC_CAST(StreamEndpointI, shared_from_this()), _instance, _host, _port);
+}
+
+IceObjC::StreamEndpointIPtr
+IceObjC::StreamEndpointI::endpoint(const StreamAcceptorPtr& a) const
+{
+ return ICE_MAKE_SHARED(StreamEndpointI, _instance, _host, a->effectivePort(), _sourceAddr, _timeout, _connectionId,
+ _compress);
+}
+
+string
+IceObjC::StreamEndpointI::options() const
+{
+ //
+ // WARNING: Certain features, such as proxy validation in Glacier2,
+ // depend on the format of proxy strings. Changes to toString() and
+ // methods called to generate parts of the reference string could break
+ // these features. Please review for all features that depend on the
+ // format of proxyToString() before changing this and related code.
+ //
+ ostringstream s;
+
+ s << IPEndpointI::options();
+
+ if(_timeout == -1)
+ {
+ s << " -t infinite";
+ }
+ else
+ {
+ s << " -t " << _timeout;
+ }
+
+ if(_compress)
+ {
+ s << " -z";
+ }
+
+ return s.str();
+}
+
+bool
+#ifdef ICE_CPP11_MAPPING
+IceObjC::StreamEndpointI::operator==(const Endpoint& r) const
+#else
+IceObjC::StreamEndpointI::operator==(const LocalObject& r) const
+#endif
+{
+ if(!IPEndpointI::operator==(r))
+ {
+ return false;
+ }
+
+ const StreamEndpointI* p = dynamic_cast<const StreamEndpointI*>(&r);
+ if(!p)
+ {
+ return false;
+ }
+
+ if(this == p)
+ {
+ return true;
+ }
+
+ if(_timeout != p->_timeout)
+ {
+ return false;
+ }
+
+ if(_compress != p->_compress)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool
+#ifdef ICE_CPP11_MAPPING
+IceObjC::StreamEndpointI::operator<(const Endpoint& r) const
+#else
+IceObjC::StreamEndpointI::operator<(const LocalObject& r) const
+#endif
+{
+ const StreamEndpointI* p = dynamic_cast<const StreamEndpointI*>(&r);
+ if(!p)
+ {
+ const IceInternal::EndpointI* e = dynamic_cast<const IceInternal::EndpointI*>(&r);
+ if(!e)
+ {
+ return false;
+ }
+ return type() < e->type();
+ }
+
+ if(this == p)
+ {
+ return false;
+ }
+
+ if(_timeout < p->_timeout)
+ {
+ return true;
+ }
+ else if(p->_timeout < _timeout)
+ {
+ return false;
+ }
+
+ if(!_compress && p->_compress)
+ {
+ return true;
+ }
+ else if(p->_compress < _compress)
+ {
+ return false;
+ }
+
+ return IPEndpointI::operator<(r);
+}
+
+void
+IceObjC::StreamEndpointI::streamWriteImpl(Ice::OutputStream* s) const
+{
+ IPEndpointI::streamWriteImpl(s);
+ s->write(_timeout);
+ s->write(_compress);
+}
+
+void
+IceObjC::StreamEndpointI::hashInit(Ice::Int& h) const
+{
+ IPEndpointI::hashInit(h);
+ hashAdd(h, _timeout);
+ hashAdd(h, _compress);
+}
+
+void
+IceObjC::StreamEndpointI::fillEndpointInfo(IPEndpointInfo* info) const
+{
+ IPEndpointI::fillEndpointInfo(info);
+ info->timeout = _timeout;
+ info->compress = _compress;
+}
+
+bool
+IceObjC::StreamEndpointI::checkOption(const string& option, const string& argument, const string& endpoint)
+{
+ if(IPEndpointI::checkOption(option, argument, endpoint))
+ {
+ return true;
+ }
+
+ switch(option[1])
+ {
+ case 't':
+ {
+ if(argument.empty())
+ {
+ EndpointParseException ex(__FILE__, __LINE__);
+ ex.str = "no argument provided for -t option in endpoint " + endpoint;
+ throw ex;
+ }
+
+ if(argument == "infinite")
+ {
+ const_cast<Int&>(_timeout) = -1;
+ }
+ else
+ {
+ istringstream t(argument);
+ if(!(t >> const_cast<Int&>(_timeout)) || !t.eof() || _timeout < 1)
+ {
+ EndpointParseException ex(__FILE__, __LINE__);
+ ex.str = "invalid timeout value `" + argument + "' in endpoint " + endpoint;
+ throw ex;
+ }
+ }
+ return true;
+ }
+
+ case 'z':
+ {
+ if(!argument.empty())
+ {
+ EndpointParseException ex(__FILE__, __LINE__);
+ ex.str = "unexpected argument `" + argument + "' provided for -z option in " + endpoint;
+ throw ex;
+ }
+ const_cast<bool&>(_compress) = true;
+ return true;
+ }
+
+ default:
+ {
+ return false;
+ }
+ }
+}
+
+ConnectorPtr
+IceObjC::StreamEndpointI::createConnector(const Address& address, const NetworkProxyPtr& proxy) const
+{
+ assert(false);
+}
+
+IPEndpointIPtr
+IceObjC::StreamEndpointI::createEndpoint(const string& host, int port, const string& connectionId) const
+{
+ return ICE_MAKE_SHARED(StreamEndpointI, _instance, host, port, _sourceAddr, _timeout, connectionId, _compress);
+}
+
+IceObjC::StreamEndpointFactory::StreamEndpointFactory(const InstancePtr& instance) : _instance(instance)
+{
+}
+
+IceObjC::StreamEndpointFactory::~StreamEndpointFactory()
+{
+}
+
+Short
+IceObjC::StreamEndpointFactory::type() const
+{
+ return _instance->type();
+}
+
+string
+IceObjC::StreamEndpointFactory::protocol() const
+{
+ return _instance->protocol();
+}
+
+EndpointIPtr
+IceObjC::StreamEndpointFactory::create(vector<string>& args, bool oaEndpoint) const
+{
+ IPEndpointIPtr endpt = ICE_MAKE_SHARED(StreamEndpointI, _instance);
+ endpt->initWithOptions(args, oaEndpoint);
+ return endpt;
+}
+
+EndpointIPtr
+IceObjC::StreamEndpointFactory::read(Ice::InputStream* s) const
+{
+ return ICE_MAKE_SHARED(StreamEndpointI, _instance, s);
+}
+
+void
+IceObjC::StreamEndpointFactory::destroy()
+{
+ _instance = 0;
+}
+
+EndpointFactoryPtr
+IceObjC::StreamEndpointFactory::clone(const ProtocolInstancePtr& instance) const
+{
+ return new StreamEndpointFactory(_instance->clone(instance));
+}
diff --git a/cpp/src/Ice/ios/StreamEndpointI.h b/cpp/src/Ice/ios/StreamEndpointI.h
new file mode 100644
index 00000000000..8e45febc64b
--- /dev/null
+++ b/cpp/src/Ice/ios/StreamEndpointI.h
@@ -0,0 +1,170 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2016 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice Touch is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#ifndef ICE_STREAM_ENDPOINT_I_H
+#define ICE_STREAM_ENDPOINT_I_H
+
+#include <Ice/ProtocolInstance.h>
+#include <Ice/IPEndpointI.h>
+#include <Ice/WSEndpoint.h>
+#include <Ice/EndpointFactory.h>
+#include <Ice/InstanceF.h>
+
+#include <CoreFoundation/CFDictionary.h>
+#include <CFNetwork/CFNetwork.h>
+
+namespace Ice
+{
+
+class OutputStream;
+class InputStream;
+
+}
+
+namespace IceObjC
+{
+
+class Instance : public IceInternal::ProtocolInstance
+{
+public:
+
+ Instance(const Ice::CommunicatorPtr&, Ice::Short, const std::string&, bool);
+ virtual ~Instance();
+
+ CFArrayRef certificateAuthorities() const
+ {
+ return _certificateAuthorities;
+ }
+
+ CFDataRef trustOnlyKeyID() const
+ {
+ return _trustOnlyKeyID;
+ }
+
+ const std::string& proxyHost() const
+ {
+ return _proxyHost;
+ }
+
+ int proxyPort() const
+ {
+ return _proxyPort;
+ }
+
+ void setupStreams(CFReadStreamRef, CFWriteStreamRef, bool, const std::string&) const;
+
+ Instance* clone(const IceInternal::ProtocolInstancePtr&);
+
+private:
+
+ const bool _voip;
+ const Ice::CommunicatorPtr _communicator;
+ CFMutableDictionaryRef _serverSettings;
+ CFMutableDictionaryRef _clientSettings;
+ CFMutableDictionaryRef _proxySettings;
+ CFArrayRef _certificateAuthorities;
+ CFDataRef _trustOnlyKeyID;
+
+ std::string _proxyHost;
+ int _proxyPort;
+};
+typedef IceUtil::Handle<Instance> InstancePtr;
+
+class StreamAcceptor;
+typedef IceUtil::Handle<StreamAcceptor> StreamAcceptorPtr;
+
+class StreamEndpointI;
+#ifdef ICE_CPP11_MAPPING // C++11 mapping
+typedef ::std::shared_ptr<StreamEndpointI> StreamEndpointIPtr;
+#else
+typedef IceUtil::Handle<StreamEndpointI> StreamEndpointIPtr;
+#endif
+
+class StreamEndpointI : public IceInternal::IPEndpointI, public IceInternal::WSEndpointDelegate
+{
+public:
+
+ StreamEndpointI(const InstancePtr&, const std::string&, Ice::Int, const IceInternal::Address&, Ice::Int,
+ const std::string&, bool);
+ StreamEndpointI(const InstancePtr&);
+ StreamEndpointI(const InstancePtr&, Ice::InputStream*);
+
+ virtual Ice::EndpointInfoPtr getInfo() const;
+ virtual Ice::EndpointInfoPtr getWSInfo(const std::string&) const;
+
+ virtual Ice::Int timeout() const;
+ virtual IceInternal::EndpointIPtr timeout(Ice::Int) const;
+ virtual bool compress() const;
+ virtual IceInternal::EndpointIPtr compress(bool) const;
+ virtual bool datagram() const;
+ virtual bool secure() const;
+
+ virtual void connectors_async(Ice::EndpointSelectionType, const IceInternal::EndpointI_connectorsPtr&) const;
+ virtual IceInternal::TransceiverPtr transceiver() const;
+ virtual IceInternal::AcceptorPtr acceptor(const std::string&) const;
+ virtual std::string options() const;
+
+#ifdef ICE_CPP11_MAPPING
+ virtual bool operator==(const Ice::Endpoint&) const;
+ virtual bool operator<(const Ice::Endpoint&) const;
+#else
+ virtual bool operator==(const Ice::LocalObject&) const;
+ virtual bool operator<(const Ice::LocalObject&) const;
+#endif
+
+ StreamEndpointIPtr endpoint(const StreamAcceptorPtr&) const;
+
+ using IPEndpointI::connectionId;
+
+protected:
+
+ virtual void streamWriteImpl(Ice::OutputStream*) const;
+ virtual void hashInit(Ice::Int&) const;
+ virtual void fillEndpointInfo(Ice::IPEndpointInfo*) const;
+ virtual bool checkOption(const std::string&, const std::string&, const std::string&);
+
+ virtual IceInternal::ConnectorPtr createConnector(const IceInternal::Address&,
+ const IceInternal::NetworkProxyPtr&) const;
+ virtual IceInternal::IPEndpointIPtr createEndpoint(const std::string&, int, const std::string&) const;
+
+private:
+
+ const InstancePtr _instance;
+
+ //
+ // All members are const, because endpoints are immutable.
+ //
+ const Ice::Int _timeout;
+ const bool _compress;
+};
+
+class StreamEndpointFactory : public IceInternal::EndpointFactory
+{
+public:
+
+ StreamEndpointFactory(const InstancePtr&);
+
+ virtual ~StreamEndpointFactory();
+
+ virtual Ice::Short type() const;
+ virtual std::string protocol() const;
+ virtual IceInternal::EndpointIPtr create(std::vector<std::string>&, bool) const;
+ virtual IceInternal::EndpointIPtr read(Ice::InputStream*) const;
+ virtual void destroy();
+
+ virtual IceInternal::EndpointFactoryPtr clone(const IceInternal::ProtocolInstancePtr&) const;
+
+private:
+
+ InstancePtr _instance;
+};
+
+}
+
+#endif
diff --git a/cpp/src/Ice/ios/StreamTransceiver.cpp b/cpp/src/Ice/ios/StreamTransceiver.cpp
new file mode 100644
index 00000000000..90e500eed85
--- /dev/null
+++ b/cpp/src/Ice/ios/StreamTransceiver.cpp
@@ -0,0 +1,793 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2016 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice Touch is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#include "StreamTransceiver.h"
+#include "StreamEndpointI.h"
+
+#include <Ice/Properties.h>
+#include <Ice/TraceLevels.h>
+#include <Ice/Connection.h>
+#include <Ice/LoggerUtil.h>
+#include <Ice/Buffer.h>
+#include <Ice/Network.h>
+#include <IceSSL/ConnectionInfo.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/Security.h>
+
+using namespace std;
+using namespace Ice;
+using namespace IceInternal;
+
+namespace
+{
+
+void selectorReadCallback(CFReadStreamRef, CFStreamEventType event, void* info)
+{
+ SelectorReadyCallback* callback = reinterpret_cast<SelectorReadyCallback*>(info);
+ switch(event)
+ {
+ case kCFStreamEventOpenCompleted:
+ callback->readyCallback(static_cast<SocketOperation>(SocketOperationConnect | SocketOperationRead));
+ break;
+ case kCFStreamEventHasBytesAvailable:
+ callback->readyCallback(SocketOperationRead);
+ break;
+ default:
+ callback->readyCallback(SocketOperationRead, -1); // Error
+ break;
+ }
+}
+
+void selectorWriteCallback(CFWriteStreamRef, CFStreamEventType event, void* info)
+{
+ SelectorReadyCallback* callback = reinterpret_cast<SelectorReadyCallback*>(info);
+ switch(event)
+ {
+ case kCFStreamEventOpenCompleted:
+ callback->readyCallback(static_cast<SocketOperation>(SocketOperationConnect | SocketOperationWrite));
+ break;
+ case kCFStreamEventCanAcceptBytes:
+ callback->readyCallback(SocketOperationWrite);
+ break;
+ default:
+ callback->readyCallback(SocketOperationWrite, -1); // Error
+ break;
+ }
+}
+
+}
+
+static inline string
+fromCFString(CFStringRef ref)
+{
+ const char* s = CFStringGetCStringPtr(ref, kCFStringEncodingUTF8);
+ if(s)
+ {
+ return string(s);
+ }
+
+ // Not great, but is good enough for this purpose.
+ char buf[1024];
+ CFStringGetCString(ref, buf, sizeof(buf), kCFStringEncodingUTF8);
+ return string(buf);
+}
+
+IceInternal::NativeInfoPtr
+IceObjC::StreamTransceiver::getNativeInfo()
+{
+ return this;
+}
+
+void
+IceObjC::StreamTransceiver::initStreams(SelectorReadyCallback* callback)
+{
+ CFOptionFlags events;
+ CFStreamClientContext ctx = { 0, callback, 0, 0, 0 };
+ events = kCFStreamEventOpenCompleted | kCFStreamEventCanAcceptBytes | kCFStreamEventErrorOccurred |
+ kCFStreamEventEndEncountered;
+ CFWriteStreamSetClient(_writeStream, events, selectorWriteCallback, &ctx);
+
+ events = kCFStreamEventOpenCompleted | kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred |
+ kCFStreamEventEndEncountered;
+ CFReadStreamSetClient(_readStream, events, selectorReadCallback, &ctx);
+}
+
+SocketOperation
+IceObjC::StreamTransceiver::registerWithRunLoop(SocketOperation op)
+{
+ IceUtil::Mutex::Lock sync(_mutex);
+ SocketOperation readyOp = SocketOperationNone;
+ if(op & SocketOperationConnect)
+ {
+ if(CFWriteStreamGetStatus(_writeStream) != kCFStreamStatusNotOpen ||
+ CFReadStreamGetStatus(_readStream) != kCFStreamStatusNotOpen)
+ {
+ return SocketOperationConnect;
+ }
+
+ _opening = true;
+
+ CFWriteStreamScheduleWithRunLoop(_writeStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+ CFReadStreamScheduleWithRunLoop(_readStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+
+ _writeStreamRegistered = true; // Note: this must be set after the schedule call
+ _readStreamRegistered = true; // Note: this must be set after the schedule call
+
+ CFReadStreamOpen(_readStream);
+ CFWriteStreamOpen(_writeStream);
+ }
+ else
+ {
+ if(op & SocketOperationWrite)
+ {
+ if(CFWriteStreamCanAcceptBytes(_writeStream))
+ {
+ readyOp = static_cast<SocketOperation>(readyOp | SocketOperationWrite);
+ }
+ else if(!_writeStreamRegistered)
+ {
+ CFWriteStreamScheduleWithRunLoop(_writeStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+ _writeStreamRegistered = true; // Note: this must be set after the schedule call
+ if(CFWriteStreamCanAcceptBytes(_writeStream))
+ {
+ readyOp = static_cast<SocketOperation>(readyOp | SocketOperationWrite);
+ }
+ }
+ }
+
+ if(op & SocketOperationRead)
+ {
+ if(CFReadStreamHasBytesAvailable(_readStream))
+ {
+ readyOp = static_cast<SocketOperation>(readyOp | SocketOperationRead);
+ }
+ else if(!_readStreamRegistered)
+ {
+ CFReadStreamScheduleWithRunLoop(_readStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+ _readStreamRegistered = true; // Note: this must be set after the schedule call
+ if(CFReadStreamHasBytesAvailable(_readStream))
+ {
+ readyOp = static_cast<SocketOperation>(readyOp | SocketOperationRead);
+ }
+ }
+ }
+ }
+ return readyOp;
+}
+
+SocketOperation
+IceObjC::StreamTransceiver::unregisterFromRunLoop(SocketOperation op, bool error)
+{
+ IceUtil::Mutex::Lock sync(_mutex);
+ _error |= error;
+
+ if(_opening)
+ {
+ // Wait for the stream to be ready for write
+ if(op == SocketOperationWrite)
+ {
+ _writeStreamRegistered = false;
+ }
+
+ //
+ // We don't wait for the stream to be ready for read (even if
+ // it's a client connection) because there's no guarantees that
+ // the server might actually send data right away. If we use
+ // the WebSocket transport, the server actually waits for the
+ // client to write the HTTP upgrade request.
+ //
+ //if(op & SocketOperationRead && (_fd != INVALID_SOCKET || !(op & SocketOperationConnect)))
+ if(op == (SocketOperationRead | SocketOperationConnect))
+ {
+ _readStreamRegistered = false;
+ }
+
+ if(error || (!_readStreamRegistered && !_writeStreamRegistered))
+ {
+ CFWriteStreamUnscheduleFromRunLoop(_writeStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+ CFReadStreamUnscheduleFromRunLoop(_readStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+ _opening = false;
+ return SocketOperationConnect;
+ }
+ else
+ {
+ return SocketOperationNone;
+ }
+ }
+ else
+ {
+ if(op & SocketOperationWrite && _writeStreamRegistered)
+ {
+ CFWriteStreamUnscheduleFromRunLoop(_writeStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+ _writeStreamRegistered = false;
+ }
+
+ if(op & SocketOperationRead && _readStreamRegistered)
+ {
+ CFReadStreamUnscheduleFromRunLoop(_readStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+ _readStreamRegistered = false;
+ }
+ }
+ return op;
+}
+
+void
+IceObjC::StreamTransceiver::closeStreams()
+{
+ CFReadStreamSetClient(_readStream, kCFStreamEventNone, 0, 0);
+ CFWriteStreamSetClient(_writeStream, kCFStreamEventNone, 0, 0);
+
+ CFReadStreamClose(_readStream);
+ CFWriteStreamClose(_writeStream);
+}
+
+SocketOperation
+IceObjC::StreamTransceiver::initialize(Buffer& readBuffer, Buffer& writeBuffer)
+{
+ IceUtil::Mutex::Lock sync(_mutex);
+ if(_state == StateNeedConnect)
+ {
+ _state = StateConnectPending;
+ return SocketOperationConnect;
+ }
+
+ if(_state <= StateConnectPending)
+ {
+ if(_error)
+ {
+ CFErrorRef err = NULL;
+ if(CFWriteStreamGetStatus(_writeStream) == kCFStreamStatusError)
+ {
+ err = CFWriteStreamCopyError(_writeStream);
+ }
+ else if(CFReadStreamGetStatus(_readStream) == kCFStreamStatusError)
+ {
+ err = CFReadStreamCopyError(_readStream);
+ }
+ checkError(err, __FILE__, __LINE__);
+ }
+
+ _state = StateConnected;
+
+ if(_fd == INVALID_SOCKET)
+ {
+ if(!CFReadStreamSetProperty(_readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse) ||
+ !CFWriteStreamSetProperty(_writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse))
+ {
+ throw Ice::SocketException(__FILE__, __LINE__, 0);
+ }
+
+ CFDataRef d = (CFDataRef)CFReadStreamCopyProperty(_readStream, kCFStreamPropertySocketNativeHandle);
+ CFDataGetBytes(d, CFRangeMake(0, sizeof(SOCKET)), reinterpret_cast<UInt8*>(&_fd));
+ CFRelease(d);
+ }
+
+ ostringstream s;
+ Address localAddr;
+ fdToLocalAddress(_fd, localAddr);
+ s << "local address = " << addrToString(localAddr);
+ string proxyHost = _instance->proxyHost();
+ if(!proxyHost.empty())
+ {
+ s << "\nSOCKS proxy address = " << proxyHost << ":" << _instance->proxyPort();
+ }
+ Address remoteAddr;
+ bool peerConnected = fdToRemoteAddress(_fd, remoteAddr);
+ if(peerConnected)
+ {
+ s << "\nremote address = " << addrToString(remoteAddr);
+ }
+ else
+ {
+ s << "\nremote address = " << _host << ":" << _port;
+ }
+ _desc = s.str();
+
+ setBlock(_fd, false);
+ setTcpBufSize(_fd, _instance);
+
+ //
+ // Limit the size of packets passed to SSLWrite/SSLRead to avoid
+ // blocking and holding too much memory.
+ //
+ _maxSendPacketSize = std::max(512, getSendBufferSize(_fd));
+ _maxRecvPacketSize = std::max(512, getRecvBufferSize(_fd));
+ }
+ assert(_state == StateConnected);
+ return SocketOperationNone;
+}
+
+SocketOperation
+#ifdef ICE_CPP11_MAPPING
+IceObjC::StreamTransceiver::closing(bool initiator, exception_ptr)
+#else
+IceObjC::StreamTransceiver::closing(bool initiator, const Ice::LocalException&)
+#endif
+{
+ // If we are initiating the connection closure, wait for the peer
+ // to close the TCP/IP connection. Otherwise, close immediately.
+ return initiator ? SocketOperationRead : SocketOperationNone;
+}
+
+void
+IceObjC::StreamTransceiver::close()
+{
+ if(_fd != INVALID_SOCKET)
+ {
+ try
+ {
+ closeSocket(_fd);
+ _fd = INVALID_SOCKET;
+ }
+ catch(const SocketException&)
+ {
+ _fd = INVALID_SOCKET;
+ throw;
+ }
+ }
+}
+
+SocketOperation
+IceObjC::StreamTransceiver::write(Buffer& buf)
+{
+ IceUtil::Mutex::Lock sync(_mutex);
+ if(_error)
+ {
+ assert(CFWriteStreamGetStatus(_writeStream) == kCFStreamStatusError);
+ checkError(CFWriteStreamCopyError(_writeStream), __FILE__, __LINE__);
+ }
+
+ // Its impossible for the packetSize to be more than an Int.
+ size_t packetSize = std::min(static_cast<size_t>(buf.b.end() - buf.i), _maxSendPacketSize);
+ while(buf.i != buf.b.end())
+ {
+ if(!CFWriteStreamCanAcceptBytes(_writeStream))
+ {
+ return SocketOperationWrite;
+ }
+
+ if(_checkCertificates)
+ {
+ _checkCertificates = false;
+ checkCertificates();
+ }
+
+ assert(_fd != INVALID_SOCKET);
+ CFIndex ret = CFWriteStreamWrite(_writeStream, reinterpret_cast<const UInt8*>(&*buf.i), packetSize);
+
+ if(ret == SOCKET_ERROR)
+ {
+ if(CFWriteStreamGetStatus(_writeStream) == kCFStreamStatusAtEnd)
+ {
+ ConnectionLostException ex(__FILE__, __LINE__);
+ ex.error = getSocketErrno();
+ throw ex;
+ }
+
+ assert(CFWriteStreamGetStatus(_writeStream) == kCFStreamStatusError);
+ checkError(CFWriteStreamCopyError(_writeStream), __FILE__, __LINE__);
+ if(noBuffers() && packetSize > 1024)
+ {
+ packetSize /= 2;
+ }
+ continue;
+ }
+
+ buf.i += ret;
+
+ if(packetSize > buf.b.end() - buf.i)
+ {
+ packetSize = static_cast<int>(buf.b.end() - buf.i);
+ }
+ }
+ return SocketOperationNone;
+}
+
+SocketOperation
+IceObjC::StreamTransceiver::read(Buffer& buf)
+{
+ IceUtil::Mutex::Lock sync(_mutex);
+ if(_error)
+ {
+ assert(CFReadStreamGetStatus(_readStream) == kCFStreamStatusError);
+ checkError(CFReadStreamCopyError(_readStream), __FILE__, __LINE__);
+ }
+
+ // Its impossible for the packetSize to be more than an Int.
+ size_t packetSize = std::min(static_cast<size_t>(buf.b.end() - buf.i), _maxRecvPacketSize);
+ while(buf.i != buf.b.end())
+ {
+ if(!CFReadStreamHasBytesAvailable(_readStream))
+ {
+ return SocketOperationRead;
+ }
+
+ if(_checkCertificates)
+ {
+ _checkCertificates = false;
+ checkCertificates();
+ }
+
+ assert(_fd != INVALID_SOCKET);
+ CFIndex ret = CFReadStreamRead(_readStream, reinterpret_cast<UInt8*>(&*buf.i), packetSize);
+
+ if(ret == 0)
+ {
+ ConnectionLostException ex(__FILE__, __LINE__);
+ ex.error = 0;
+ throw ex;
+ }
+
+ if(ret == SOCKET_ERROR)
+ {
+ if(CFReadStreamGetStatus(_readStream) == kCFStreamStatusAtEnd)
+ {
+ ConnectionLostException ex(__FILE__, __LINE__);
+ ex.error = getSocketErrno();
+ throw ex;
+ }
+
+ assert(CFReadStreamGetStatus(_readStream) == kCFStreamStatusError);
+ checkError(CFReadStreamCopyError(_readStream), __FILE__, __LINE__);
+ if(noBuffers() && packetSize > 1024)
+ {
+ packetSize /= 2;
+ }
+ continue;
+ }
+
+ buf.i += ret;
+
+ if(packetSize > buf.b.end() - buf.i)
+ {
+ packetSize = static_cast<int>(buf.b.end() - buf.i);
+ }
+ }
+
+ return SocketOperationNone;
+}
+
+string
+IceObjC::StreamTransceiver::protocol() const
+{
+ return _instance->protocol();
+}
+
+string
+IceObjC::StreamTransceiver::toString() const
+{
+ return _desc;
+}
+
+string
+IceObjC::StreamTransceiver::toDetailedString() const
+{
+ return _desc;
+}
+
+Ice::ConnectionInfoPtr
+IceObjC::StreamTransceiver::getInfo() const
+{
+ if(_instance->secure())
+ {
+ IceSSL::ConnectionInfoPtr info = ICE_MAKE_SHARED(IceSSL::ConnectionInfo);
+ fillConnectionInfo(info);
+ info->verified = _state == StateConnected;
+ return info;
+ }
+ else
+ {
+ Ice::TCPConnectionInfoPtr info = ICE_MAKE_SHARED(Ice::TCPConnectionInfo);
+ fillConnectionInfo(info);
+ return info;
+ }
+}
+
+Ice::ConnectionInfoPtr
+IceObjC::StreamTransceiver::getWSInfo(const Ice::HeaderDict& headers) const
+{
+ if(_instance->secure())
+ {
+ IceSSL::WSSConnectionInfoPtr info = ICE_MAKE_SHARED(IceSSL::WSSConnectionInfo);
+ fillConnectionInfo(info);
+ info->verified = _state == StateConnected;
+ info->headers = headers;
+ return info;
+ }
+ else
+ {
+ Ice::WSConnectionInfoPtr info = ICE_MAKE_SHARED(Ice::WSConnectionInfo);
+ fillConnectionInfo(info);
+ info->headers = headers;
+ return info;
+ }
+}
+
+void
+IceObjC::StreamTransceiver::checkSendSize(const Buffer& buf)
+{
+}
+
+void
+IceObjC::StreamTransceiver::setBufferSize(int rcvSize, int sndSize)
+{
+ setTcpBufSize(_fd, rcvSize, sndSize, _instance);
+}
+
+IceObjC::StreamTransceiver::StreamTransceiver(const InstancePtr& instance,
+ CFReadStreamRef readStream,
+ CFWriteStreamRef writeStream,
+ const string& host,
+ Ice::Int port) :
+ StreamNativeInfo(INVALID_SOCKET),
+ _instance(instance),
+ _host(host),
+ _port(port),
+ _readStream(readStream),
+ _writeStream(writeStream),
+ _readStreamRegistered(false),
+ _writeStreamRegistered(false),
+ _opening(false),
+ _checkCertificates(instance->secure()),
+ _error(false),
+ _state(StateNeedConnect)
+{
+ ostringstream s;
+ s << "local address = <not available>";
+ string proxyHost = instance->proxyHost();
+ if(!proxyHost.empty())
+ {
+ s << "\nSOCKS proxy address = " << proxyHost << ":" << instance->proxyPort();
+ }
+ s << "\nremote address = " << host << ":" << port;
+ _desc = s.str();
+}
+
+IceObjC::StreamTransceiver::StreamTransceiver(const InstancePtr& instance,
+ CFReadStreamRef readStream,
+ CFWriteStreamRef writeStream,
+ SOCKET fd) :
+ StreamNativeInfo(fd),
+ _instance(instance),
+ _port(0),
+ _readStream(readStream),
+ _writeStream(writeStream),
+ _readStreamRegistered(false),
+ _writeStreamRegistered(false),
+ _opening(false),
+ _checkCertificates(false),
+ _error(false),
+ _state(StateNeedConnect),
+ _desc(fdToString(fd))
+{
+}
+
+IceObjC::StreamTransceiver::~StreamTransceiver()
+{
+ assert(_fd == INVALID_SOCKET);
+ CFRelease(_readStream);
+ CFRelease(_writeStream);
+}
+
+void
+IceObjC::StreamTransceiver::checkCertificates()
+{
+ SecTrustRef trust = (SecTrustRef)CFWriteStreamCopyProperty(_writeStream, kCFStreamPropertySSLPeerTrust);
+ if(!trust)
+ {
+ throw Ice::SecurityException(__FILE__, __LINE__, "unable to obtain trust object");
+ }
+
+ try
+ {
+ SecPolicyRef policy = 0;
+ if(_host.empty() || _instance->properties()->getPropertyAsIntWithDefault("IceSSL.CheckCertName", 1) == 0)
+ {
+ policy = SecPolicyCreateBasicX509();
+ }
+ else
+ {
+ CFStringRef h = CFStringCreateWithCString(NULL, _host.c_str(), kCFStringEncodingUTF8);
+ policy = SecPolicyCreateSSL(false, h);
+ CFRelease(h);
+ }
+
+ OSStatus err = SecTrustSetPolicies(trust, policy);
+ CFRelease(policy);
+ if(err != noErr)
+ {
+ ostringstream os;
+ os << "unable to set trust object policy (error = " << err << ")";
+ throw Ice::SecurityException(__FILE__, __LINE__, os.str());
+ }
+
+ //
+ // If IceSSL.CertAuthFile is set, we use the certificate authorities from this file
+ // instead of the ones from the keychain.
+ //
+ if((err = SecTrustSetAnchorCertificates(trust, _instance->certificateAuthorities())) != noErr)
+ {
+ ostringstream os;
+ os << "couldn't set root CA certificates with trust object (error = " << err << ")";
+ throw Ice::SecurityException(__FILE__, __LINE__, os.str());
+ }
+
+ SecTrustResultType result = kSecTrustResultInvalid;
+ if((err = SecTrustEvaluate(trust, &result)) != noErr)
+ {
+ ostringstream os;
+ os << "unable to evaluate the peer certificate trust (error = " << err << ")";
+ throw Ice::SecurityException(__FILE__, __LINE__, os.str());
+ }
+
+ //
+ // The kSecTrustResultUnspecified result indicates that the user didn't set any trust
+ // settings for the root CA. This is expected if the root CA is provided by the user
+ // with IceSSL.CertAuthFile or if the user didn't explicitly set any trust settings
+ // for the certificate.
+ //
+ if(result != kSecTrustResultProceed && result != kSecTrustResultUnspecified)
+ {
+ ostringstream os;
+ os << "certificate validation failed (result = " << result << ")";
+ throw Ice::SecurityException(__FILE__, __LINE__, os.str());
+ }
+
+ if(_instance->trustOnlyKeyID())
+ {
+ if(SecTrustGetCertificateCount(trust) < 0)
+ {
+ throw Ice::SecurityException(__FILE__, __LINE__, "unable to obtain peer certificate");
+ }
+
+ SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, 0);
+
+ //
+ // To check the subject key ID, we add the peer certificate to the keychain with SetItemAdd,
+ // then we lookup for the cert using the kSecAttrSubjectKeyID. Then we remove the cert from
+ // the keychain. NOTE: according to the Apple documentation, it should in theory be possible
+ // to not add/remove the item to the keychain by specifying the kSecMatchItemList key (or
+ // kSecUseItemList?) when calling SecItemCopyMatching. Unfortunately this doesn't appear to
+ // work. Similarly, it should be possible to get back the attributes of the certificate
+ // once it added by setting kSecReturnAttributes in the add query, again this doesn't seem
+ // to work.
+ //
+ CFMutableDictionaryRef query;
+ query = CFDictionaryCreateMutable(0, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ CFDictionarySetValue(query, kSecClass, kSecClassCertificate);
+ CFDictionarySetValue(query, kSecValueRef, cert);
+ err = SecItemAdd(query, 0);
+ if(err != noErr && err != errSecDuplicateItem)
+ {
+ CFRelease(query);
+ ostringstream os;
+ os << "unable to add peer certificate to keychain (error = " << err << ")";
+ throw Ice::SecurityException(__FILE__, __LINE__, os.str());
+ }
+ CFRelease(query);
+
+ query = CFDictionaryCreateMutable(0, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ CFDictionarySetValue(query, kSecClass, kSecClassCertificate);
+ CFDictionarySetValue(query, kSecValueRef, cert);
+ CFDictionarySetValue(query, kSecAttrSubjectKeyID, _instance->trustOnlyKeyID());
+ err = SecItemCopyMatching(query, 0);
+ OSStatus foundErr = err;
+ CFRelease(query);
+
+ query = CFDictionaryCreateMutable(0, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ CFDictionarySetValue(query, kSecClass, kSecClassCertificate);
+ CFDictionarySetValue(query, kSecValueRef, cert);
+ err = SecItemDelete(query);
+ if(err != noErr)
+ {
+ CFRelease(query);
+ ostringstream os;
+ os << "unable to remove peer certificate from keychain (error = " << err << ")";
+ throw Ice::SecurityException(__FILE__, __LINE__, os.str());
+ }
+ CFRelease(query);
+
+ if(foundErr != noErr)
+ {
+ ostringstream os;
+ os << "the certificate subject key ID doesn't match the `IceSSL.TrustOnly.Client' property ";
+ os << "(error = " << foundErr << ")";
+ throw Ice::SecurityException(__FILE__, __LINE__, os.str());
+ }
+ }
+ CFRelease(trust);
+ }
+ catch(...)
+ {
+ if(trust)
+ {
+ CFRelease(trust);
+ }
+ throw;
+ }
+}
+
+void
+IceObjC::StreamTransceiver::checkError(CFErrorRef err, const char* file, int line)
+{
+ assert(err);
+ CFStringRef domain = CFErrorGetDomain(err);
+ if(CFStringCompare(domain, kCFErrorDomainPOSIX, 0) == kCFCompareEqualTo)
+ {
+ errno = CFErrorGetCode(err);
+ CFRelease(err);
+ if(interrupted() || noBuffers())
+ {
+ return;
+ }
+
+ if(connectionLost())
+ {
+ ConnectionLostException ex(file, line);
+ ex.error = getSocketErrno();
+ throw ex;
+ }
+ else if(connectionRefused())
+ {
+ ConnectionRefusedException ex(file, line);
+ ex.error = getSocketErrno();
+ throw ex;
+ }
+ else if(connectFailed())
+ {
+ ConnectFailedException ex(file, line);
+ ex.error = getSocketErrno();
+ throw ex;
+ }
+ else
+ {
+ SocketException ex(file, line);
+ ex.error = getSocketErrno();
+ throw ex;
+ }
+ }
+
+ int error = CFErrorGetCode(err);
+ if(error == kCFHostErrorHostNotFound || error == kCFHostErrorUnknown)
+ {
+ int rs = 0;
+ if(error == kCFHostErrorUnknown)
+ {
+ CFDictionaryRef dict = CFErrorCopyUserInfo(err);
+ CFNumberRef d = (CFNumberRef)CFDictionaryGetValue(dict, kCFGetAddrInfoFailureKey);
+ if(d != 0)
+ {
+ CFNumberGetValue(d, kCFNumberSInt32Type, &rs);
+ }
+ CFRelease(dict);
+ }
+
+ CFRelease(err);
+
+ DNSException ex(file, line);
+ ex.error = rs;
+ ex.host = _host;
+ throw ex;
+ }
+
+ CFNetworkException ex(file, line);
+ ex.domain = fromCFString(domain);
+ ex.error = CFErrorGetCode(err);
+ CFRelease(err);
+ throw ex;
+}
+
+void
+IceObjC::StreamTransceiver::fillConnectionInfo(const Ice::IPConnectionInfoPtr& info) const
+{
+ fdToAddressAndPort(_fd, info->localAddress, info->localPort, info->remoteAddress, info->remotePort);
+ info->rcvSize = getRecvBufferSize(_fd);
+ info->sndSize = getSendBufferSize(_fd);
+}
diff --git a/cpp/src/Ice/ios/StreamTransceiver.h b/cpp/src/Ice/ios/StreamTransceiver.h
new file mode 100644
index 00000000000..088001887e2
--- /dev/null
+++ b/cpp/src/Ice/ios/StreamTransceiver.h
@@ -0,0 +1,102 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2016 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice Touch is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#ifndef ICE_STREAM_TRANSCEIVER_H
+#define ICE_STREAM_TRANSCEIVER_H
+
+#include <Ice/WSTransceiver.h>
+#include <Ice/Network.h>
+#include <Ice/Selector.h>
+
+struct __CFError;
+typedef struct __CFError * CFErrorRef;
+
+struct __CFWriteStream;
+typedef struct __CFWriteStream * CFWriteStreamRef;
+
+struct __CFReadStream;
+typedef struct __CFReadStream * CFReadStreamRef;
+
+namespace IceObjC
+{
+
+class Instance;
+typedef IceUtil::Handle<Instance> InstancePtr;
+
+class StreamTransceiver : public IceInternal::Transceiver, public IceInternal::StreamNativeInfo,
+ public IceInternal::WSTransceiverDelegate
+{
+ enum State
+ {
+ StateNeedConnect,
+ StateConnectPending,
+ StateConnected
+ };
+
+public:
+
+ StreamTransceiver(const InstancePtr&, CFReadStreamRef, CFWriteStreamRef, const std::string&, Ice::Int);
+ StreamTransceiver(const InstancePtr&, CFReadStreamRef, CFWriteStreamRef, SOCKET);
+ virtual ~StreamTransceiver();
+
+ virtual IceInternal::NativeInfoPtr getNativeInfo();
+
+ virtual void initStreams(IceInternal::SelectorReadyCallback*);
+ virtual IceInternal::SocketOperation registerWithRunLoop(IceInternal::SocketOperation);
+ virtual IceInternal::SocketOperation unregisterFromRunLoop(IceInternal::SocketOperation, bool);
+ virtual void closeStreams();
+
+ virtual IceInternal::SocketOperation initialize(IceInternal::Buffer&, IceInternal::Buffer&);
+#ifdef ICE_CPP11_MAPPING
+ virtual IceInternal::SocketOperation closing(bool initiator, std::exception_ptr);
+#else
+ virtual IceInternal::SocketOperation closing(bool, const Ice::LocalException&);
+#endif
+ virtual void close();
+
+ virtual IceInternal::SocketOperation write(IceInternal::Buffer&);
+ virtual IceInternal::SocketOperation read(IceInternal::Buffer&);
+
+ virtual std::string protocol() const;
+ virtual std::string toString() const;
+ virtual std::string toDetailedString() const;
+ virtual Ice::ConnectionInfoPtr getInfo() const;
+ virtual Ice::ConnectionInfoPtr getWSInfo(const Ice::HeaderDict&) const;
+ virtual void checkSendSize(const IceInternal::Buffer&);
+ virtual void setBufferSize(int, int);
+
+private:
+
+ void checkCertificates();
+ void checkError(CFErrorRef, const char*, int);
+ void fillConnectionInfo(const Ice::IPConnectionInfoPtr&) const;
+
+ const InstancePtr _instance;
+ const std::string _host;
+ const Ice::Int _port;
+ CFReadStreamRef _readStream;
+ CFWriteStreamRef _writeStream;
+ bool _readStreamRegistered;
+ bool _writeStreamRegistered;
+ bool _opening;
+ bool _checkCertificates;
+
+ IceUtil::Mutex _mutex;
+ bool _error;
+
+ State _state;
+ std::string _desc;
+
+ size_t _maxSendPacketSize;
+ size_t _maxRecvPacketSize;
+};
+
+}
+
+#endif
diff --git a/cpp/src/Ice/ios/iAPConnector.h b/cpp/src/Ice/ios/iAPConnector.h
new file mode 100644
index 00000000000..42fe9f9029e
--- /dev/null
+++ b/cpp/src/Ice/ios/iAPConnector.h
@@ -0,0 +1,58 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice Touch is licensed to you under the terms described in the
+// ICE_TOUCH_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#ifndef ICE_IAP_CONNECTOR_H
+#define ICE_IAP_CONNECTOR_H
+
+#include <Ice/TransceiverF.h>
+#include <Ice/ProtocolInstanceF.h>
+#include <Ice/TraceLevelsF.h>
+#include <Ice/LoggerF.h>
+#include <Ice/Connector.h>
+
+#import <Foundation/Foundation.h>
+#import <ExternalAccessory/ExternalAccessory.h>
+
+namespace IceObjC
+{
+
+class iAPEndpointI;
+
+class Instance;
+typedef IceUtil::Handle<Instance> InstancePtr;
+
+class iAPConnector : public IceInternal::Connector
+{
+public:
+
+ virtual IceInternal::TransceiverPtr connect();
+
+ virtual Ice::Short type() const;
+ virtual std::string toString() const;
+
+ virtual bool operator==(const IceInternal::Connector&) const;
+ virtual bool operator!=(const IceInternal::Connector&) const;
+ virtual bool operator<(const IceInternal::Connector&) const;
+
+private:
+
+ iAPConnector(const IceInternal::ProtocolInstancePtr&, Ice::Int, const std::string&, NSString*, EAAccessory*);
+ virtual ~iAPConnector();
+ friend class iAPEndpointI;
+
+ const IceInternal::ProtocolInstancePtr _instance;
+ const Ice::Int _timeout;
+ const std::string _connectionId;
+ NSString* _protocol;
+ EAAccessory* _accessory;
+};
+
+}
+
+#endif
diff --git a/cpp/src/Ice/ios/iAPConnector.mm b/cpp/src/Ice/ios/iAPConnector.mm
new file mode 100644
index 00000000000..37b2b7fc2dc
--- /dev/null
+++ b/cpp/src/Ice/ios/iAPConnector.mm
@@ -0,0 +1,153 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice Touch is licensed to you under the terms described in the
+// ICE_TOUCH_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#include "iAPTransceiver.h"
+#include "iAPEndpointI.h"
+#include "iAPConnector.h"
+
+#include <Ice/ProtocolInstance.h>
+#include <Ice/Exception.h>
+
+using namespace std;
+using namespace Ice;
+using namespace IceInternal;
+
+TransceiverPtr
+IceObjC::iAPConnector::connect()
+{
+ EASession* session = [[EASession alloc] initWithAccessory:_accessory forProtocol:_protocol];
+ if(!session)
+ {
+ throw Ice::ConnectFailedException(__FILE__, __LINE__, 0);
+ }
+ return new iAPTransceiver(_instance, session);
+}
+
+Short
+IceObjC::iAPConnector::type() const
+{
+ return _instance->type();
+}
+
+string
+IceObjC::iAPConnector::toString() const
+{
+ ostringstream os;
+ os << [_accessory.name UTF8String];
+ os << " model `" << [_accessory.modelNumber UTF8String] << "'";
+ os << " made by `" << [_accessory.manufacturer UTF8String] << "'";
+ os << " protocol `" << [_protocol UTF8String] << "'";
+ return os.str();
+}
+
+bool
+IceObjC::iAPConnector::operator==(const IceInternal::Connector& r) const
+{
+ const iAPConnector* p = dynamic_cast<const iAPConnector*>(&r);
+ if(!p)
+ {
+ return false;
+ }
+
+ if(_timeout != p->_timeout)
+ {
+ return false;
+ }
+
+ if(_connectionId != p->_connectionId)
+ {
+ return false;
+ }
+
+ if(![_accessory isEqual:p->_accessory])
+ {
+ return false;
+ }
+
+ if(![_protocol isEqual:p->_protocol])
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool
+IceObjC::iAPConnector::operator!=(const IceInternal::Connector& r) const
+{
+ return !operator==(r);
+}
+
+bool
+IceObjC::iAPConnector::operator<(const IceInternal::Connector& r) const
+{
+ const iAPConnector* p = dynamic_cast<const iAPConnector*>(&r);
+ if(!p)
+ {
+ return type() < r.type();
+ }
+
+ if(_timeout < p->_timeout)
+ {
+ return true;
+ }
+ else if(p->_timeout < _timeout)
+ {
+ return false;
+ }
+
+ if(_connectionId < p->_connectionId)
+ {
+ return true;
+ }
+ else if(p->_connectionId < _connectionId)
+ {
+ return false;
+ }
+
+ if([_accessory hash] < [p->_accessory hash])
+ {
+ return true;
+ }
+ else if([p->_accessory hash] < [_accessory hash])
+ {
+ return false;
+ }
+
+ NSInteger order = [_protocol compare:p->_protocol];
+ if(order == NSOrderedAscending)
+ {
+ return true;
+ }
+ else if(order == NSOrderedDescending)
+ {
+ return false;
+ }
+
+ return false;
+}
+
+IceObjC::iAPConnector::iAPConnector(const ProtocolInstancePtr& instance,
+ Ice::Int timeout,
+ const string& connectionId,
+ NSString* protocol,
+ EAAccessory* accessory) :
+ _instance(instance),
+ _timeout(timeout),
+ _connectionId(connectionId),
+ _protocol([protocol retain]),
+ _accessory([accessory retain])
+{
+}
+
+IceObjC::iAPConnector::~iAPConnector()
+{
+ [_protocol release];
+ [_accessory release];
+}
diff --git a/cpp/src/Ice/ios/iAPEndpointI.h b/cpp/src/Ice/ios/iAPEndpointI.h
new file mode 100644
index 00000000000..242574d0edb
--- /dev/null
+++ b/cpp/src/Ice/ios/iAPEndpointI.h
@@ -0,0 +1,109 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice Touch is licensed to you under the terms described in the
+// ICE_TOUCH_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#ifndef ICE_IAP_ENDPOINT_I_H
+#define ICE_IAP_ENDPOINT_I_H
+
+#include <Ice/ProtocolInstanceF.h>
+#include <Ice/EndpointI.h>
+#include <Ice/EndpointFactory.h>
+
+namespace IceObjC
+{
+
+class iAPEndpointI;
+#ifdef ICE_CPP11_MAPPING // C++11 mapping
+typedef ::std::shared_ptr<iAPEndpointI> iAPEndpointIPtr;
+#else
+typedef IceUtil::Handle<iAPEndpointI> iAPEndpointIPtr;
+#endif
+
+class iAPEndpointI : public IceInternal::EndpointI, public Ice::EnableSharedFromThis<iAPEndpointI>
+{
+public:
+
+ iAPEndpointI(const IceInternal::ProtocolInstancePtr&, const std::string&, const std::string&, const std::string&,
+ const std::string&, Ice::Int, const std::string&, bool);
+ iAPEndpointI(const IceInternal::ProtocolInstancePtr&);
+ iAPEndpointI(const IceInternal::ProtocolInstancePtr&, Ice::InputStream*);
+
+ virtual void streamWrite(Ice::OutputStream*) const;
+
+ virtual Ice::EndpointInfoPtr getInfo() const;
+ virtual Ice::Short type() const;
+ virtual const std::string& protocol() const;
+ virtual bool datagram() const;
+ virtual bool secure() const;
+
+ virtual Ice::Int timeout() const;
+ virtual IceInternal::EndpointIPtr timeout(Ice::Int) const;
+ virtual const std::string& connectionId() const;
+ virtual IceInternal::EndpointIPtr connectionId(const std::string&) const;
+ virtual bool compress() const;
+ virtual IceInternal::EndpointIPtr compress(bool) const;
+
+ virtual IceInternal::TransceiverPtr transceiver() const;
+ virtual void connectors_async(Ice::EndpointSelectionType, const IceInternal::EndpointI_connectorsPtr&) const;
+ virtual IceInternal::AcceptorPtr acceptor(const std::string&) const;
+
+ virtual std::vector<IceInternal::EndpointIPtr> expand() const;
+ virtual bool equivalent(const IceInternal::EndpointIPtr&) const;
+
+#ifdef ICE_CPP11_MAPPING
+ virtual bool operator==(const Ice::Endpoint&) const;
+ virtual bool operator<(const Ice::Endpoint&) const;
+#else
+ virtual bool operator==(const Ice::LocalObject&) const;
+ virtual bool operator<(const Ice::LocalObject&) const;
+#endif
+
+ virtual std::string options() const;
+ virtual ::Ice::Int hash() const;
+
+private:
+
+ virtual bool checkOption(const std::string&, const std::string&, const std::string&);
+
+ //
+ // All members are const, because endpoints are immutable.
+ //
+ const IceInternal::ProtocolInstancePtr _instance;
+ const std::string _manufacturer;
+ const std::string _modelNumber;
+ const std::string _name;
+ const std::string _protocol;
+ const Ice::Int _timeout;
+ const std::string _connectionId;
+ const bool _compress;
+};
+
+class iAPEndpointFactory : public IceInternal::EndpointFactory
+{
+public:
+
+ iAPEndpointFactory(const IceInternal::ProtocolInstancePtr&);
+
+ virtual ~iAPEndpointFactory();
+
+ virtual Ice::Short type() const;
+ virtual std::string protocol() const;
+ virtual IceInternal::EndpointIPtr create(std::vector<std::string>&, bool) const;
+ virtual IceInternal::EndpointIPtr read(Ice::InputStream*) const;
+ virtual void destroy();
+
+ virtual IceInternal::EndpointFactoryPtr clone(const IceInternal::ProtocolInstancePtr&) const;
+
+private:
+
+ IceInternal::ProtocolInstancePtr _instance;
+};
+
+}
+
+#endif
diff --git a/cpp/src/Ice/ios/iAPEndpointI.mm b/cpp/src/Ice/ios/iAPEndpointI.mm
new file mode 100644
index 00000000000..c6bc8dc66fb
--- /dev/null
+++ b/cpp/src/Ice/ios/iAPEndpointI.mm
@@ -0,0 +1,640 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2016 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice Touch is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#include "iAPEndpointI.h"
+#include "iAPConnector.h"
+
+#include <Ice/Network.h>
+#include <Ice/InputStream.h>
+#include <Ice/OutputStream.h>
+#include <Ice/LocalException.h>
+#include <Ice/ProtocolInstance.h>
+#include <Ice/DefaultsAndOverrides.h>
+#include <Ice/Initialize.h>
+#include <Ice/EndpointFactoryManager.h>
+#include <Ice/Properties.h>
+#include <Ice/HashUtil.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include <fstream>
+
+using namespace std;
+using namespace Ice;
+using namespace IceInternal;
+
+extern "C"
+{
+
+Plugin*
+createIceIAP(const CommunicatorPtr& com, const string&, const StringSeq&)
+{
+ ProtocolInstancePtr instance = new ProtocolInstance(com, iAPEndpointType, "iap", false);
+ return new EndpointFactoryPlugin(com, new IceObjC::iAPEndpointFactory(instance));
+}
+
+}
+
+IceObjC::iAPEndpointI::iAPEndpointI(const ProtocolInstancePtr& instance, const string& m,
+ const string& o, const string& n, const string& p, Int ti,
+ const string& conId, bool co) :
+ _instance(instance),
+ _manufacturer(m),
+ _modelNumber(o),
+ _name(n),
+ _protocol(p),
+ _timeout(ti),
+ _connectionId(conId),
+ _compress(co)
+{
+}
+
+IceObjC::iAPEndpointI::iAPEndpointI(const ProtocolInstancePtr& instance) :
+ _instance(instance),
+ _timeout(-1),
+ _compress(false)
+{
+}
+
+IceObjC::iAPEndpointI::iAPEndpointI(const ProtocolInstancePtr& instance, InputStream* s) :
+ _instance(instance),
+ _timeout(-1),
+ _compress(false)
+{
+ s->read(const_cast<string&>(_manufacturer), false);
+ s->read(const_cast<string&>(_modelNumber), false);
+ s->read(const_cast<string&>(_name), false);
+ s->read(const_cast<Int&>(_timeout));
+ s->read(const_cast<bool&>(_compress));
+}
+
+void
+IceObjC::iAPEndpointI::streamWrite(OutputStream* s) const
+{
+ s->startEncapsulation();
+ s->write(_manufacturer, false);
+ s->write(_modelNumber, false);
+ s->write(_name, false);
+ s->write(_timeout);
+ s->write(_compress);
+ s->endEncapsulation();
+}
+
+EndpointInfoPtr
+IceObjC::iAPEndpointI::getInfo() const
+{
+ return 0;
+}
+
+Short
+IceObjC::iAPEndpointI::type() const
+{
+ return _instance->type();
+}
+
+const string&
+IceObjC::iAPEndpointI::protocol() const
+{
+ return _instance->protocol();
+}
+
+bool
+IceObjC::iAPEndpointI::datagram() const
+{
+ return false;
+}
+
+bool
+IceObjC::iAPEndpointI::secure() const
+{
+ return _instance->secure();
+}
+
+Int
+IceObjC::iAPEndpointI::timeout() const
+{
+ return _timeout;
+}
+
+EndpointIPtr
+IceObjC::iAPEndpointI::timeout(Int t) const
+{
+ if(t == _timeout)
+ {
+ return shared_from_this();
+ }
+ else
+ {
+ return ICE_MAKE_SHARED(iAPEndpointI, _instance, _manufacturer, _modelNumber, _name, _protocol, t, _connectionId, _compress);
+ }
+}
+
+const string&
+IceObjC::iAPEndpointI::connectionId() const
+{
+ return _connectionId;
+}
+
+EndpointIPtr
+IceObjC::iAPEndpointI::connectionId(const string& cId) const
+{
+ if(cId == _connectionId)
+ {
+ return shared_from_this();
+ }
+ else
+ {
+ return ICE_MAKE_SHARED(iAPEndpointI, _instance, _manufacturer, _modelNumber, _name, _protocol, _timeout, cId, _compress);
+ }
+}
+
+bool
+IceObjC::iAPEndpointI::compress() const
+{
+ return _compress;
+}
+
+EndpointIPtr
+IceObjC::iAPEndpointI::compress(bool c) const
+{
+ if(c == _compress)
+ {
+ return shared_from_this();
+ }
+ else
+ {
+ return ICE_MAKE_SHARED(iAPEndpointI, _instance, _manufacturer, _modelNumber, _name, _protocol, _timeout, _connectionId, c);
+ }
+}
+
+TransceiverPtr
+IceObjC::iAPEndpointI::transceiver() const
+{
+ return 0;
+}
+
+void
+IceObjC::iAPEndpointI::connectors_async(Ice::EndpointSelectionType selType,
+ const EndpointI_connectorsPtr& callback) const
+{
+ try
+ {
+ vector<ConnectorPtr> c;
+
+ EAAccessoryManager* manager = [EAAccessoryManager sharedAccessoryManager];
+ if(manager == nil)
+ {
+ throw Ice::ConnectFailedException(__FILE__, __LINE__, 0);
+ }
+
+ NSString* protocol = _protocol.empty() ? @"com.zeroc.ice" : [[NSString alloc] initWithUTF8String:_protocol.c_str()];
+ NSArray* array = [manager connectedAccessories];
+ NSEnumerator* enumerator = [array objectEnumerator];
+ EAAccessory* accessory = nil;
+ int lastError = 0;
+ while((accessory = [enumerator nextObject]))
+ {
+ if(!accessory.connected)
+ {
+ lastError = 1;
+ continue;
+ }
+
+ if(!_manufacturer.empty() && _manufacturer != [accessory.manufacturer UTF8String])
+ {
+ lastError = 2;
+ continue;
+ }
+ if(!_modelNumber.empty() && _modelNumber != [accessory.modelNumber UTF8String])
+ {
+ lastError = 3;
+ continue;
+ }
+ if(!_name.empty() && _name != [accessory.name UTF8String])
+ {
+ lastError = 4;
+ continue;
+ }
+
+ if(![accessory.protocolStrings containsObject:protocol])
+ {
+ lastError = 5;
+ continue;
+ }
+
+ c.push_back(new iAPConnector(_instance, _timeout, _connectionId, protocol, accessory));
+ }
+ [protocol release];
+ if(c.empty())
+ {
+ throw Ice::ConnectFailedException(__FILE__, __LINE__, 0);
+ }
+ callback->connectors(c);
+ }
+ catch(const Ice::LocalException& ex)
+ {
+ callback->exception(ex);
+ }
+}
+
+AcceptorPtr
+IceObjC::iAPEndpointI::acceptor(const string&) const
+{
+ assert(false);
+ return 0;
+}
+
+vector<EndpointIPtr>
+IceObjC::iAPEndpointI::expand() const
+{
+ vector<EndpointIPtr> endps;
+ endps.push_back(shared_from_this());
+ return endps;
+}
+
+bool
+IceObjC::iAPEndpointI::equivalent(const EndpointIPtr& endpoint) const
+{
+ const iAPEndpointI* endpointI = dynamic_cast<const iAPEndpointI*>(endpoint.get());
+ if(!endpointI)
+ {
+ return false;
+ }
+ return endpointI->_manufacturer == _manufacturer &&
+ endpointI->_modelNumber == _modelNumber &&
+ endpointI->_name == _name;
+}
+
+bool
+#ifdef ICE_CPP11_MAPPING
+IceObjC::iAPEndpointI::operator==(const Ice::Endpoint& r) const
+#else
+IceObjC::iAPEndpointI::operator==(const Ice::LocalObject& r) const
+#endif
+{
+ const iAPEndpointI* p = dynamic_cast<const iAPEndpointI*>(&r);
+ if(!p)
+ {
+ return false;
+ }
+
+ if(this == p)
+ {
+ return true;
+ }
+
+ if(_manufacturer != p->_manufacturer)
+ {
+ return false;
+ }
+
+ if(_modelNumber != p->_modelNumber)
+ {
+ return false;
+ }
+
+ if(_name != p->_name)
+ {
+ return false;
+ }
+
+ if(_timeout != p->_timeout)
+ {
+ return false;
+ }
+
+ if(_connectionId != p->_connectionId)
+ {
+ return false;
+ }
+
+ if(_compress != p->_compress)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool
+#ifdef ICE_CPP11_MAPPING
+IceObjC::iAPEndpointI::operator<(const Ice::Endpoint& r) const
+#else
+IceObjC::iAPEndpointI::operator<(const Ice::LocalObject& r) const
+#endif
+{
+ const iAPEndpointI* p = dynamic_cast<const iAPEndpointI*>(&r);
+ if(!p)
+ {
+ const IceInternal::EndpointI* e = dynamic_cast<const IceInternal::EndpointI*>(&r);
+ if(!e)
+ {
+ return false;
+ }
+ return type() < e->type();
+ }
+
+ if(this == p)
+ {
+ return false;
+ }
+
+ if(_manufacturer < p->_manufacturer)
+ {
+ return true;
+ }
+ else if(p->_manufacturer < _manufacturer)
+ {
+ return false;
+ }
+
+ if(_modelNumber < p->_modelNumber)
+ {
+ return true;
+ }
+ else if(p->_modelNumber < _modelNumber)
+ {
+ return false;
+ }
+
+ if(_name < p->_name)
+ {
+ return true;
+ }
+ else if(p->_name < _name)
+ {
+ return false;
+ }
+
+ if(_timeout < p->_timeout)
+ {
+ return true;
+ }
+ else if(p->_timeout < _timeout)
+ {
+ return false;
+ }
+
+ if(_connectionId < p->_connectionId)
+ {
+ return true;
+ }
+ else if(p->_connectionId < _connectionId)
+ {
+ return false;
+ }
+
+ if(!_compress && p->_compress)
+ {
+ return true;
+ }
+ else if(p->_compress < _compress)
+ {
+ return false;
+ }
+
+ return false;
+}
+
+string
+IceObjC::iAPEndpointI::options() const
+{
+ //
+ // WARNING: Certain features, such as proxy validation in Glacier2,
+ // depend on the format of proxy strings. Changes to toString() and
+ // methods called to generate parts of the reference string could break
+ // these features. Please review for all features that depend on the
+ // format of proxyToString() before changing this and related code.
+ //
+ ostringstream s;
+ if(!_manufacturer.empty())
+ {
+ s << " -m ";
+ bool addQuote = _manufacturer.find(':') != string::npos;
+ if(addQuote)
+ {
+ s << "\"";
+ }
+ s << _manufacturer;
+ if(addQuote)
+ {
+ s << "\"";
+ }
+ }
+
+ if(!_modelNumber.empty())
+ {
+ s << " -o ";
+ bool addQuote = _modelNumber.find(':') != string::npos;
+ if(addQuote)
+ {
+ s << "\"";
+ }
+ s << _modelNumber;
+ if(addQuote)
+ {
+ s << "\"";
+ }
+ }
+
+ if(!_name.empty())
+ {
+ s << " -n ";
+ bool addQuote = _name.find(':') != string::npos;
+ if(addQuote)
+ {
+ s << "\"";
+ }
+ s << _name;
+ if(addQuote)
+ {
+ s << "\"";
+ }
+ }
+
+ if(!_protocol.empty())
+ {
+ s << " -p ";
+ bool addQuote = _protocol.find(':') != string::npos;
+ if(addQuote)
+ {
+ s << "\"";
+ }
+ s << _protocol;
+ if(addQuote)
+ {
+ s << "\"";
+ }
+ }
+
+ if(_timeout != -1)
+ {
+ s << " -t " << _timeout;
+ }
+ if(_compress)
+ {
+ s << " -z";
+ }
+ return s.str();
+}
+
+Ice::Int
+IceObjC::iAPEndpointI::hash() const
+{
+ Ice::Int h = 5381;
+ hashAdd(h, _manufacturer);
+ hashAdd(h, _modelNumber);
+ hashAdd(h, _name);
+ hashAdd(h, _timeout);
+ hashAdd(h, _connectionId);
+ return h;
+}
+
+bool
+IceObjC::iAPEndpointI::checkOption(const string& option, const string& argument, const string& endpoint)
+{
+ switch(option[1])
+ {
+ case 'm':
+ {
+ if(argument.empty())
+ {
+ EndpointParseException ex(__FILE__, __LINE__);
+ ex.str = "no argument provided for -h option in endpoint " + endpoint;
+ throw ex;
+ }
+ const_cast<string&>(_manufacturer) = argument;
+ break;
+ }
+
+ case 'o':
+ {
+ if(argument.empty())
+ {
+ EndpointParseException ex(__FILE__, __LINE__);
+ ex.str = "no argument provided for -h option in endpoint " + endpoint;
+ throw ex;
+ }
+ const_cast<string&>(_modelNumber) = argument;
+ break;
+ }
+
+ case 'n':
+ {
+ if(argument.empty())
+ {
+ EndpointParseException ex(__FILE__, __LINE__);
+ ex.str = "no argument provided for -h option in endpoint " + endpoint;
+ throw ex;
+ }
+ const_cast<string&>(_name) = argument;
+ break;
+ }
+
+ case 'p':
+ {
+ if(argument.empty())
+ {
+ EndpointParseException ex(__FILE__, __LINE__);
+ ex.str = "no argument provided for -h option in endpoint " + endpoint;
+ throw ex;
+ }
+ const_cast<string&>(_protocol) = argument;
+ break;
+ }
+
+ case 't':
+ {
+ if(argument == "infinite")
+ {
+ const_cast<Int&>(_timeout) = -1;
+ }
+ else
+ {
+ istringstream t(argument);
+ if(!(t >> const_cast<Int&>(_timeout)) || !t.eof() || _timeout < 1)
+ {
+ EndpointParseException ex(__FILE__, __LINE__);
+ ex.str = "invalid timeout value `" + argument + "' in endpoint " + endpoint;
+ throw ex;
+ }
+ }
+ break;
+ }
+
+ case 'z':
+ {
+ if(!argument.empty())
+ {
+ EndpointParseException ex(__FILE__, __LINE__);
+ ex.str = "no argument provided for -h option in endpoint " + endpoint;
+ throw ex;
+ }
+ const_cast<bool&>(_compress) = true;
+ break;
+ }
+
+ default:
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+IceObjC::iAPEndpointFactory::iAPEndpointFactory(const ProtocolInstancePtr& instance) :
+ _instance(instance)
+{
+}
+
+IceObjC::iAPEndpointFactory::~iAPEndpointFactory()
+{
+}
+
+Short
+IceObjC::iAPEndpointFactory::type() const
+{
+ return _instance->type();
+}
+
+string
+IceObjC::iAPEndpointFactory::protocol() const
+{
+ return _instance->protocol();
+}
+
+EndpointIPtr
+IceObjC::iAPEndpointFactory::create(vector<string>& args, bool oaEndpoint) const
+{
+ if(oaEndpoint)
+ {
+ return 0;
+ }
+ EndpointIPtr endpt = ICE_MAKE_SHARED(iAPEndpointI, _instance);
+ endpt->initWithOptions(args);
+ return endpt;
+}
+
+EndpointIPtr
+IceObjC::iAPEndpointFactory::read(InputStream* s) const
+{
+ return ICE_MAKE_SHARED(iAPEndpointI, _instance, s);
+}
+
+void
+IceObjC::iAPEndpointFactory::destroy()
+{
+ _instance = 0;
+}
+
+EndpointFactoryPtr
+IceObjC::iAPEndpointFactory::clone(const ProtocolInstancePtr& instance) const
+{
+ return new iAPEndpointFactory(instance);
+}
diff --git a/cpp/src/Ice/ios/iAPTransceiver.h b/cpp/src/Ice/ios/iAPTransceiver.h
new file mode 100644
index 00000000000..b5e10c80f1e
--- /dev/null
+++ b/cpp/src/Ice/ios/iAPTransceiver.h
@@ -0,0 +1,85 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice Touch is licensed to you under the terms described in the
+// ICE_TOUCH_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#ifndef ICE_IAP_TRANSCEIVER_H
+#define ICE_IAP_TRANSCEIVER_H
+
+#include <Ice/ProtocolInstanceF.h>
+#include <Ice/Transceiver.h>
+#include <Ice/Network.h>
+#include <Ice/Selector.h>
+
+#import <Foundation/Foundation.h>
+#import <ExternalAccessory/ExternalAccessory.h>
+
+@class iAPTransceiverCallback;
+
+namespace IceObjC
+{
+
+class iAPTransceiver : public IceInternal::Transceiver, public IceInternal::StreamNativeInfo
+{
+ enum State
+ {
+ StateNeedConnect,
+ StateConnectPending,
+ StateConnected
+ };
+
+public:
+
+ iAPTransceiver(const IceInternal::ProtocolInstancePtr&, EASession*);
+ virtual ~iAPTransceiver();
+
+ virtual void initStreams(IceInternal::SelectorReadyCallback*);
+ virtual IceInternal::SocketOperation registerWithRunLoop(IceInternal::SocketOperation);
+ virtual IceInternal::SocketOperation unregisterFromRunLoop(IceInternal::SocketOperation, bool);
+ virtual void closeStreams();
+
+ virtual IceInternal::NativeInfoPtr getNativeInfo();
+
+ virtual IceInternal::SocketOperation initialize(IceInternal::Buffer&, IceInternal::Buffer&);
+#ifdef ICE_CPP11_MAPPING
+ virtual IceInternal::SocketOperation closing(bool, std::exception_ptr);
+#else
+ virtual IceInternal::SocketOperation closing(bool, const Ice::LocalException&);
+#endif
+ virtual void close();
+ virtual IceInternal::SocketOperation write(IceInternal::Buffer&);
+ virtual IceInternal::SocketOperation read(IceInternal::Buffer&);
+
+ virtual std::string protocol() const;
+ virtual std::string toString() const;
+ virtual std::string toDetailedString() const;
+ virtual Ice::ConnectionInfoPtr getInfo() const;
+ virtual void checkSendSize(const IceInternal::Buffer&);
+ virtual void setBufferSize(int, int);
+
+private:
+
+ void checkError(NSError*, const char*, int);
+
+ IceInternal::ProtocolInstancePtr _instance;
+ NSInputStream* _readStream;
+ NSOutputStream* _writeStream;
+ iAPTransceiverCallback* _callback;
+ bool _readStreamRegistered;
+ bool _writeStreamRegistered;
+ bool _opening;
+
+ IceUtil::Mutex _mutex;
+ bool _error;
+
+ State _state;
+ std::string _desc;
+};
+
+}
+
+#endif
diff --git a/cpp/src/Ice/ios/iAPTransceiver.mm b/cpp/src/Ice/ios/iAPTransceiver.mm
new file mode 100644
index 00000000000..1e7b989f8e1
--- /dev/null
+++ b/cpp/src/Ice/ios/iAPTransceiver.mm
@@ -0,0 +1,490 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2016 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice Touch is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#include "iAPTransceiver.h"
+#include "iAPEndpointI.h"
+
+#include <Ice/LocalException.h>
+#include <Ice/ProtocolInstance.h>
+#include <Ice/Buffer.h>
+
+#import <Foundation/NSRunLoop.h>
+#import <Foundation/NSError.h>
+#import <Foundation/NSString.h>
+
+using namespace std;
+using namespace Ice;
+using namespace IceInternal;
+
+@interface iAPTransceiverCallback : NSObject<NSStreamDelegate>
+{
+@private
+
+ SelectorReadyCallback* callback;
+}
+-(id) init:(SelectorReadyCallback*)cb;
+@end
+
+@implementation iAPTransceiverCallback
+-(id) init:(SelectorReadyCallback*)cb;
+{
+ if(![super init])
+ {
+ return nil;
+ }
+ callback = cb;
+ return self;
+}
+
+- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode
+{
+ switch(eventCode)
+ {
+ case NSStreamEventHasBytesAvailable:
+ callback->readyCallback(SocketOperationRead);
+ break;
+ case NSStreamEventHasSpaceAvailable:
+ callback->readyCallback(SocketOperationWrite);
+ break;
+ case NSStreamEventOpenCompleted:
+ if([[stream class] isSubclassOfClass:[NSInputStream class]])
+ {
+ callback->readyCallback(static_cast<SocketOperation>(SocketOperationConnect | SocketOperationRead));
+ }
+ else
+ {
+ callback->readyCallback(static_cast<SocketOperation>(SocketOperationConnect | SocketOperationWrite));
+ }
+ break;
+ case NSStreamEventEndEncountered:
+ case NSStreamEventErrorOccurred:
+ if([[stream class] isSubclassOfClass:[NSInputStream class]])
+ {
+ callback->readyCallback(SocketOperationRead, -1); // Error
+ }
+ else
+ {
+ callback->readyCallback(SocketOperationWrite, -1); // Error
+ }
+ }
+}
+@end
+
+void
+IceObjC::iAPTransceiver::initStreams(SelectorReadyCallback* callback)
+{
+ _callback = [[iAPTransceiverCallback alloc] init:callback];
+ [_writeStream setDelegate:_callback];
+ [_readStream setDelegate:_callback];
+}
+
+SocketOperation
+IceObjC::iAPTransceiver::registerWithRunLoop(SocketOperation op)
+{
+ IceUtil::Mutex::Lock sync(_mutex);
+
+ SocketOperation readyOp = SocketOperationNone;
+ if(op & SocketOperationConnect)
+ {
+ if([_writeStream streamStatus] != NSStreamStatusNotOpen || [_readStream streamStatus] != NSStreamStatusNotOpen)
+ {
+ return SocketOperationConnect;
+ }
+
+ _opening = true;
+
+ [_writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
+ [_readStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
+
+ _writeStreamRegistered = true; // Note: this must be set after the schedule call
+ _readStreamRegistered = true; // Note: this must be set after the schedule call
+
+ [_writeStream open];
+ [_readStream open];
+ }
+ else
+ {
+ if(op & SocketOperationWrite)
+ {
+ if([_writeStream hasSpaceAvailable])
+ {
+ readyOp = static_cast<SocketOperation>(readyOp | SocketOperationWrite);
+ }
+ else if(!_writeStreamRegistered)
+ {
+ [_writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
+ _writeStreamRegistered = true; // Note: this must be set after the schedule call
+ if([_writeStream hasSpaceAvailable])
+ {
+ readyOp = static_cast<SocketOperation>(readyOp | SocketOperationWrite);
+ }
+ }
+ }
+
+ if(op & SocketOperationRead)
+ {
+ if([_readStream hasBytesAvailable])
+ {
+ readyOp = static_cast<SocketOperation>(readyOp | SocketOperationRead);
+ }
+ else if(!_readStreamRegistered)
+ {
+ [_readStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
+ _readStreamRegistered = true; // Note: this must be set after the schedule call
+ if([_readStream hasBytesAvailable])
+ {
+ readyOp = static_cast<SocketOperation>(readyOp | SocketOperationRead);
+ }
+ }
+ }
+ }
+ return readyOp;
+}
+
+SocketOperation
+IceObjC::iAPTransceiver::unregisterFromRunLoop(SocketOperation op, bool error)
+{
+ IceUtil::Mutex::Lock sync(_mutex);
+ _error |= error;
+
+ if(_opening)
+ {
+ // Wait for the stream to be ready for write
+ if(op == SocketOperationWrite)
+ {
+ _writeStreamRegistered = false;
+ }
+
+ //
+ // We don't wait for the stream to be ready for read (even if
+ // it's a client connection) because there's no guarantees that
+ // the server might actually send data right away. If we use
+ // the WebSocket transport, the server actually waits for the
+ // client to write the HTTP upgrade request.
+ //
+ //if(op & SocketOperationRead && (_fd != INVALID_SOCKET || !(op & SocketOperationConnect)))
+ if(op == (SocketOperationRead | SocketOperationConnect))
+ {
+ _readStreamRegistered = false;
+ }
+
+ if(error || (!_readStreamRegistered && !_writeStreamRegistered))
+ {
+ [_writeStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
+ [_readStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
+ _opening = false;
+ return SocketOperationConnect;
+ }
+ else
+ {
+ return SocketOperationNone;
+ }
+ }
+ else
+ {
+ if(op & SocketOperationWrite && _writeStreamRegistered)
+ {
+ [_writeStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
+ _writeStreamRegistered = false;
+ }
+
+ if(op & SocketOperationRead && _readStreamRegistered)
+ {
+ [_readStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
+ _readStreamRegistered = false;
+ }
+ }
+ return op;
+}
+
+void
+IceObjC::iAPTransceiver::closeStreams()
+{
+ [_writeStream setDelegate:nil];
+ [_readStream setDelegate:nil];
+
+ [_callback release];
+ _callback = 0;
+
+ [_writeStream close];
+ [_readStream close];
+}
+
+IceInternal::NativeInfoPtr
+IceObjC::iAPTransceiver::getNativeInfo()
+{
+ return this;
+}
+
+SocketOperation
+IceObjC::iAPTransceiver::initialize(Buffer& readBuffer, Buffer& writeBuffer)
+{
+ IceUtil::Mutex::Lock sync(_mutex);
+ if(_state == StateNeedConnect)
+ {
+ _state = StateConnectPending;
+ return SocketOperationConnect;
+ }
+
+ if(_state <= StateConnectPending)
+ {
+ if(_error)
+ {
+ assert([_writeStream streamStatus] == NSStreamStatusError);
+ NSError* err = [_writeStream streamError];
+ NSString* domain = [err domain];
+ if([domain compare:NSPOSIXErrorDomain] == NSOrderedSame)
+ {
+ errno = [err code];
+ [err release];
+ if(connectionRefused())
+ {
+ ConnectionRefusedException ex(__FILE__, __LINE__);
+ ex.error = getSocketErrno();
+ throw ex;
+ }
+ else if(connectFailed())
+ {
+ ConnectFailedException ex(__FILE__, __LINE__);
+ ex.error = getSocketErrno();
+ throw ex;
+ }
+ else
+ {
+ SocketException ex(__FILE__, __LINE__);
+ ex.error = getSocketErrno();
+ throw ex;
+ }
+ }
+
+ // Otherwise throw a generic exception.
+ CFNetworkException ex(__FILE__, __LINE__);
+ ex.domain = [domain UTF8String];
+ ex.error = [err code];
+ [err release];
+ throw ex;
+ }
+ _state = StateConnected;
+ }
+ assert(_state == StateConnected);
+ return SocketOperationNone;
+}
+
+SocketOperation
+#ifdef ICE_CPP11_MAPPING
+IceObjC::iAPTransceiver::closing(bool initiator, exception_ptr)
+#else
+IceObjC::iAPTransceiver::closing(bool initiator, const Ice::LocalException&)
+#endif
+{
+ // If we are initiating the connection closure, wait for the peer
+ // to close the TCP/IP connection. Otherwise, close immediately.
+ return initiator ? SocketOperationRead : SocketOperationNone;
+}
+
+void
+IceObjC::iAPTransceiver::close()
+{
+}
+
+SocketOperation
+IceObjC::iAPTransceiver::write(Buffer& buf)
+{
+ IceUtil::Mutex::Lock sync(_mutex);
+ if(_error)
+ {
+ assert([_writeStream streamStatus] == NSStreamStatusError);
+ checkError([_writeStream streamError], __FILE__, __LINE__);
+ }
+
+ // Its impossible for the packetSize to be more than an Int.
+ int packetSize = static_cast<int>(buf.b.end() - buf.i);
+ while(buf.i != buf.b.end())
+ {
+ if(![_writeStream hasSpaceAvailable])
+ {
+ return SocketOperationWrite;
+ }
+ assert([_writeStream streamStatus] >= NSStreamStatusOpen);
+
+ NSInteger ret = [_writeStream write:reinterpret_cast<const UInt8*>(&*buf.i) maxLength:packetSize];
+ if(ret == SOCKET_ERROR)
+ {
+ if([_writeStream streamStatus] == NSStreamStatusAtEnd)
+ {
+ ConnectionLostException ex(__FILE__, __LINE__);
+ ex.error = getSocketErrno();
+ throw ex;
+ }
+
+ assert([_writeStream streamStatus] == NSStreamStatusError);
+ checkError([_writeStream streamError], __FILE__, __LINE__);
+ if(noBuffers() && packetSize > 1024)
+ {
+ packetSize /= 2;
+ }
+ continue;
+ }
+
+ buf.i += ret;
+
+ if(packetSize > buf.b.end() - buf.i)
+ {
+ packetSize = static_cast<int>(buf.b.end() - buf.i);
+ }
+ }
+
+ return SocketOperationNone;
+}
+
+SocketOperation
+IceObjC::iAPTransceiver::read(Buffer& buf)
+{
+ IceUtil::Mutex::Lock sync(_mutex);
+ if(_error)
+ {
+ assert([_readStream streamStatus] == NSStreamStatusError);
+ checkError([_readStream streamError], __FILE__, __LINE__);
+ }
+
+ // Its impossible for the packetSize to be more than an Int.
+ int packetSize = static_cast<int>(buf.b.end() - buf.i);
+ while(buf.i != buf.b.end())
+ {
+ if(![_readStream hasBytesAvailable] && [_readStream streamStatus] != NSStreamStatusError)
+ {
+ return SocketOperationRead;
+ }
+ assert([_readStream streamStatus] >= NSStreamStatusOpen);
+
+ NSInteger ret = [_readStream read:reinterpret_cast<UInt8*>(&*buf.i) maxLength:packetSize];
+ if(ret == 0)
+ {
+ ConnectionLostException ex(__FILE__, __LINE__);
+ ex.error = 0;
+ throw ex;
+ }
+
+ if(ret == SOCKET_ERROR)
+ {
+ if([_readStream streamStatus] == NSStreamStatusAtEnd)
+ {
+ ConnectionLostException ex(__FILE__, __LINE__);
+ ex.error = getSocketErrno();
+ throw ex;
+ }
+
+ assert([_readStream streamStatus] == NSStreamStatusError);
+ checkError([_readStream streamError], __FILE__, __LINE__);
+ if(noBuffers() && packetSize > 1024)
+ {
+ packetSize /= 2;
+ }
+ continue;
+ }
+
+ buf.i += ret;
+
+ if(packetSize > buf.b.end() - buf.i)
+ {
+ packetSize = static_cast<int>(buf.b.end() - buf.i);
+ }
+ }
+
+ return SocketOperationNone;
+}
+
+string
+IceObjC::iAPTransceiver::protocol() const
+{
+ return _instance->protocol();
+}
+
+string
+IceObjC::iAPTransceiver::toString() const
+{
+ return _desc;
+}
+
+string
+IceObjC::iAPTransceiver::toDetailedString() const
+{
+ return toString();
+}
+
+Ice::ConnectionInfoPtr
+IceObjC::iAPTransceiver::getInfo() const
+{
+ return 0;
+}
+
+void
+IceObjC::iAPTransceiver::checkSendSize(const Buffer& buf)
+{
+}
+
+void
+IceObjC::iAPTransceiver::setBufferSize(int, int)
+{
+}
+
+IceObjC::iAPTransceiver::iAPTransceiver(const ProtocolInstancePtr& instance, EASession* session) :
+ StreamNativeInfo(INVALID_SOCKET),
+ _instance(instance),
+ _readStream([[session inputStream] retain]),
+ _writeStream([[session outputStream] retain]),
+ _readStreamRegistered(false),
+ _writeStreamRegistered(false),
+ _error(false),
+ _state(StateNeedConnect)
+{
+ _desc = string("name = ") + [session.accessory.name UTF8String];
+}
+
+IceObjC::iAPTransceiver::~iAPTransceiver()
+{
+ [_readStream release];
+ [_writeStream release];
+}
+
+void
+IceObjC::iAPTransceiver::checkError(NSError* err, const char* file, int line)
+{
+ NSString* domain = [err domain];
+ if([domain compare:NSPOSIXErrorDomain] == NSOrderedSame)
+ {
+ errno = [err code];
+ [err release];
+ if(connectionRefused())
+ {
+ ConnectionRefusedException ex(file, line);
+ ex.error = getSocketErrno();
+ throw ex;
+ }
+ else if(connectFailed())
+ {
+ ConnectFailedException ex(file, line);
+ ex.error = getSocketErrno();
+ throw ex;
+ }
+ else
+ {
+ SocketException ex(file, line);
+ ex.error = getSocketErrno();
+ throw ex;
+ }
+ }
+
+ // Otherwise throw a generic exception.
+ CFNetworkException ex(file, line);
+ ex.domain = [domain UTF8String];
+ ex.error = [err code];
+ [err release];
+ throw ex;
+}