Newer
Older
// Compile on OSX only.
#if defined(__APPLE__)
#include "MantidKernel/NetworkProxy.h"
#include <SystemConfiguration/SystemConfiguration.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CFNetwork/CFProxySupport.h>
#include <sstream>
#include <string>
#include <vector>
namespace Mantid {
namespace Kernel {
/**
* Helper function to convert CFStringRefs to std::string
* @param str : CRFStringRef variable
* @return std::string containing the contents of the input string
std::string toString(CFStringRef str) {
if (!str)
return std::string();
CFIndex length = CFStringGetLength(str);
const UniChar *chars = CFStringGetCharactersPtr(str);
if (chars)
return std::string(reinterpret_cast<const char *>(chars), length);
std::vector<UniChar> buffer(length);
CFStringGetCharacters(str, CFRangeMake(0, length), buffer.data());
return std::string(reinterpret_cast<const char *>(buffer.data()), length);
}
/**
* Helper enums.
*/
enum ProxyType {
DefaultProxy,
Socks5Proxy,
NoProxy,
HttpProxy,
HttpCachingProxy,
FtpCachingProxy
};
/// Typedef Collection of proxy information.
typedef std::vector<ProxyInfo> ProxyInfoVec;
/**
* Extract proxy information from a CFDistionaryRef
* @param dict : CFDictionary item
* @return ProxyInfo object.
*/
ProxyInfo proxyFromDictionary(CFDictionaryRef dict) {
ProxyInfo proxyInfo;
ProxyType proxyType = NoProxy;
CFStringRef cfProxyType = reinterpret_cast<CFStringRef>(
CFDictionaryGetValue(dict, kCFProxyTypeKey));
if (CFStringCompare(cfProxyType, kCFProxyTypeFTP, 0) == kCFCompareEqualTo) {
proxyType = FtpCachingProxy;
} else if (CFStringCompare(cfProxyType, kCFProxyTypeHTTP, 0) ==
kCFCompareEqualTo) {
proxyType = HttpProxy;
} else if (CFStringCompare(cfProxyType, kCFProxyTypeHTTPS, 0) ==
kCFCompareEqualTo) {
proxyType = HttpProxy;
} else if (CFStringCompare(cfProxyType, kCFProxyTypeSOCKS, 0) ==
kCFCompareEqualTo) {
proxyType = Socks5Proxy;
}
int port = 0;
std::string hostName = toString(reinterpret_cast<CFStringRef>(
CFDictionaryGetValue(dict, kCFProxyHostNameKey)));
CFNumberRef portNumber = reinterpret_cast<CFNumberRef>(
CFDictionaryGetValue(dict, kCFProxyPortNumberKey));
if (portNumber) {
CFNumberGetValue(portNumber, kCFNumberSInt16Type, &port);
}
if (proxyType != NoProxy) {
proxyInfo = ProxyInfo(hostName, port, proxyType == HttpProxy);
}
return proxyInfo;
}
/**
* Get proxy information from a proxy script.
* @param dict : Dictionary to search through.
* @param targetURLString : Target remote URL
* @param logger : Log object
* @return Collection of proxy information.
*/
ProxyInfoVec proxyInformationFromPac(CFDictionaryRef dict,
const std::string &targetURLString,
Logger &logger) {
ProxyInfoVec proxyInfoVec;
// is there a PAC enabled? If so, use it first.
CFNumberRef pacEnabled;
if ((pacEnabled = reinterpret_cast<CFNumberRef>(CFDictionaryGetValue(
dict, kSCPropNetProxiesProxyAutoConfigEnable)))) {
int enabled;
if (CFNumberGetValue(pacEnabled, kCFNumberIntType, &enabled) && enabled) {
// PAC is enabled
CFStringRef cfPacLocation =
reinterpret_cast<CFStringRef>(CFDictionaryGetValue(
dict, kSCPropNetProxiesProxyAutoConfigURLString));
CFDataRef pacData;
CFURLRef pacURL =
CFURLCreateWithString(kCFAllocatorDefault, cfPacLocation, nullptr);
if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, pacURL,
&pacData, nullptr, nullptr,
&errorCode)) {
logger.debug() << "Unable to get the PAC script at "
<< toString(cfPacLocation) << "Error code: " << errorCode
CFStringRef pacScript = CFStringCreateFromExternalRepresentation(
kCFAllocatorDefault, pacData, kCFStringEncodingISOLatin1);
CFURLRef targetURL = CFURLCreateWithBytes(
kCFAllocatorDefault, reinterpret_cast<UInt8 *>(
const_cast<char *>(targetURLString.c_str())),
targetURLString.size(), kCFStringEncodingUTF8, nullptr);
if (!targetURL) {
logger.debug("Problem with Target URI for proxy script");
return proxyInfoVec;
CFErrorRef pacError;
CFArrayRef proxies = CFNetworkCopyProxiesForAutoConfigurationScript(
pacScript, targetURL, &pacError);
if (!proxies) {
std::string pacLocation = toString(cfPacLocation);
CFStringRef pacErrorDescription = CFErrorCopyDescription(pacError);
logger.debug() << "Execution of PAC script at \"%s\" failed: %s"
<< pacLocation << toString(pacErrorDescription) << '\n';
CFIndex size = CFArrayGetCount(proxies);
for (CFIndex i = 0; i < size; ++i) {
CFDictionaryRef proxy = reinterpret_cast<CFDictionaryRef>(
CFArrayGetValueAtIndex(proxies, i));
proxyInfoVec.push_back(proxyFromDictionary(proxy));
}
return proxyInfoVec;
}
/**
* Proxy from dictionary.
* @param dict
* @param enableKey
* @param hostKey
* @param portKey
* @return return Proxy object.
*/
ProxyInfo proxyFromDictionary(CFDictionaryRef dict, CFStringRef enableKey,
CFStringRef hostKey, CFStringRef portKey) {
ProxyInfo proxyInfo;
CFNumberRef protoEnabled;
CFNumberRef protoPort;
CFStringRef protoHost;
if (enableKey && (protoEnabled = reinterpret_cast<CFNumberRef>(
CFDictionaryGetValue(dict, enableKey))) &&
(protoHost = reinterpret_cast<CFStringRef>(
CFDictionaryGetValue(dict, hostKey))) &&
(protoPort = reinterpret_cast<CFNumberRef>(
CFDictionaryGetValue(dict, portKey)))) {
int enabled;
if (CFNumberGetValue(protoEnabled, kCFNumberIntType, &enabled) && enabled) {
std::string host = toString(protoHost);
int port;
CFNumberGetValue(protoPort, kCFNumberIntType, &port);
proxyInfo = ProxyInfo(host, port, HttpProxy);
}
// proxy not enabled
return proxyInfo;
}
/**
* Specifially look for http proxy settings.
* @param dict :
* @return Return the proxy info object.
*/
ProxyInfo httpProxyFromSystem(CFDictionaryRef dict) {
ProxyInfo tempProxy = proxyFromDictionary(dict, kSCPropNetProxiesHTTPEnable,
kSCPropNetProxiesHTTPProxy,
kSCPropNetProxiesHTTPPort);
return tempProxy;
}
/**
* Find the http proxy.
* Look through the proxy settings script first.
* @param targetURLString : Target remote URL string
* @param logger : ref to log object.
* @return Proxy object.
*/
ProxyInfo findHttpProxy(const std::string &targetURLString,
Mantid::Kernel::Logger &logger) {
ProxyInfo httpProxy;
CFDictionaryRef dict = SCDynamicStoreCopyProxies(nullptr);
logger.debug("NetworkProxyOSX SCDynamicStoreCopyProxies returned NULL");
}
// Query the proxy pac first.
ProxyInfoVec info = proxyInformationFromPac(dict, targetURLString, logger);
bool foundHttpProxy = false;
if (proxyInfo.isHttpProxy()) {
foundHttpProxy = true;
break;
}
}
// Query the http proxy settings second.
if (!foundHttpProxy) {
ProxyInfo tempProxy = httpProxyFromSystem(dict);
if (tempProxy.isHttpProxy()) {
httpProxy = tempProxy;
foundHttpProxy = true;
}
}
logger.debug("NetworkProxyOSX. No system HTTP Proxy set!");
//----------------------------------------------------------------------------------------------
/** Constructor
*/
NetworkProxy::NetworkProxy() : m_logger("network_proxy_logger_osx") {}
ProxyInfo NetworkProxy::getHttpProxy(const std::string &targetURLString) {
return findHttpProxy(targetURLString, m_logger);
}
} // namespace Mantid
#endif