mirror of
https://github.com/textmate/textmate.git
synced 2026-04-28 03:00:34 -04:00
223 lines
6.7 KiB
C++
223 lines
6.7 KiB
C++
#include "proxy.h"
|
||
#include <regexp/regexp.h>
|
||
#include <text/encode.h>
|
||
#include <oak/debug.h>
|
||
#include <cf/cf.h>
|
||
|
||
OAK_DEBUG_VAR(Proxy);
|
||
|
||
static proxy_settings_t user_pw_settings (CFStringRef server, CFNumberRef portNumber)
|
||
{
|
||
std::string user = NULL_STR, pw = NULL_STR;
|
||
|
||
CFTypeRef keys[] = {
|
||
kSecMatchLimit, kSecReturnRef,
|
||
kSecClass,
|
||
kSecAttrProtocol,
|
||
kSecAttrPort,
|
||
kSecAttrServer
|
||
};
|
||
CFTypeRef vals[] = {
|
||
kSecMatchLimitAll, kCFBooleanTrue,
|
||
kSecClassInternetPassword,
|
||
kSecAttrProtocolHTTPProxy,
|
||
portNumber,
|
||
server
|
||
};
|
||
CFDictionaryRef query = CFDictionaryCreate(NULL, keys, vals, sizeofA(keys), NULL, NULL);
|
||
|
||
CFArrayRef results = NULL;
|
||
OSStatus err = SecItemCopyMatching(query, (CFTypeRef*)&results);
|
||
if(err == errSecSuccess)
|
||
{
|
||
CFIndex numResults = CFArrayGetCount(results);
|
||
for(CFIndex i = 0; user == NULL_STR && i < numResults; ++i)
|
||
{
|
||
SecKeychainItemRef item = (SecKeychainItemRef)CFArrayGetValueAtIndex(results, i);
|
||
|
||
UInt32 tag = kSecAccountItemAttr;
|
||
UInt32 format = CSSM_DB_ATTRIBUTE_FORMAT_STRING;
|
||
SecKeychainAttributeInfo info = { 1, &tag, &format };
|
||
|
||
void* data = NULL;
|
||
UInt32 dataLen = 0;
|
||
|
||
SecKeychainAttributeList* authAttrList = NULL;
|
||
if(SecKeychainItemCopyAttributesAndData(item, &info, NULL, &authAttrList, &dataLen, &data) == noErr)
|
||
{
|
||
ASSERT(authAttrList->count == 1 && authAttrList->attr->tag == kSecAccountItemAttr);
|
||
user = std::string((char const*)authAttrList->attr->data, ((char const*)authAttrList->attr->data) + authAttrList->attr->length);
|
||
pw = std::string((char const*)data, ((char const*)data) + dataLen);
|
||
SecKeychainItemFreeAttributesAndData(authAttrList, data);
|
||
D(DBF_Proxy, bug("found user ‘%s’\n", user.c_str()););
|
||
}
|
||
else
|
||
D(DBF_Proxy, bug("unable to obtain attributes from key chain entry\n"););
|
||
}
|
||
|
||
CFRelease(results);
|
||
}
|
||
else if(err != errSecItemNotFound)
|
||
{
|
||
CFStringRef message = SecCopyErrorMessageString(err, NULL);
|
||
fprintf(stderr, "TextMate/proxy: SecItemCopyMatching() failed with error ‘%s’\n", cf::to_s(message).c_str());
|
||
CFRelease(message);
|
||
}
|
||
|
||
if(query)
|
||
CFRelease(query);
|
||
|
||
int32_t port = 0;
|
||
CFNumberGetValue(portNumber, kCFNumberSInt32Type, &port);
|
||
|
||
return proxy_settings_t(true, cf::to_s(server), port, user, pw);
|
||
}
|
||
|
||
struct pac_proxy_callback_result_t
|
||
{
|
||
bool has_result = false;
|
||
CFArrayRef proxies = NULL;
|
||
CFErrorRef error = NULL;
|
||
};
|
||
|
||
static void pac_proxy_callback (void* client, CFArrayRef proxies, CFErrorRef error)
|
||
{
|
||
pac_proxy_callback_result_t* res = (pac_proxy_callback_result_t*)client;
|
||
res->proxies = proxies ? (CFArrayRef)CFRetain(proxies) : NULL;
|
||
res->error = error ? (CFErrorRef)CFRetain(error) : NULL;
|
||
res->has_result = true;
|
||
}
|
||
|
||
proxy_settings_t first_proxy_from_array (CFArrayRef proxies, CFURLRef targetURL)
|
||
{
|
||
for(CFIndex i = 0; i < CFArrayGetCount(proxies); ++i)
|
||
{
|
||
CFDictionaryRef proxy = (CFDictionaryRef)CFArrayGetValueAtIndex(proxies, i);
|
||
if(!proxy || CFGetTypeID(proxy) != CFDictionaryGetTypeID())
|
||
{
|
||
fprintf(stderr, "TextMate/proxy: Expected proxy info to be a CFDictionaryRef\n");
|
||
continue;
|
||
}
|
||
|
||
CFStringRef type = (CFStringRef)CFDictionaryGetValue(proxy, kCFProxyTypeKey);
|
||
if(!type || CFGetTypeID(type) != CFStringGetTypeID())
|
||
{
|
||
fprintf(stderr, "TextMate/proxy: Expected kCFProxyTypeKey to be a CFStringRef\n");
|
||
continue;
|
||
}
|
||
|
||
if(CFEqual(type, kCFProxyTypeNone))
|
||
{
|
||
break;
|
||
}
|
||
else if(CFEqual(type, kCFProxyTypeAutoConfigurationURL))
|
||
{
|
||
CFURLRef pacURL = (CFURLRef)CFDictionaryGetValue(proxy, kCFProxyAutoConfigurationURLKey);
|
||
if(pacURL && CFGetTypeID(pacURL) == CFURLGetTypeID())
|
||
{
|
||
fprintf(stderr, "TextMate DEBUG: Resolving PAC URL ‘%s’\n", cf::to_s(CFURLGetString(pacURL)).c_str());
|
||
|
||
pac_proxy_callback_result_t result;
|
||
|
||
CFStreamClientContext context = { 0, &result, NULL, NULL, NULL };
|
||
CFRunLoopSourceRef runLoopSource = CFNetworkExecuteProxyAutoConfigurationURL(pacURL, targetURL, &pac_proxy_callback, &context);
|
||
|
||
CFStringRef runLoopMode = CFSTR("OakRunPACScriptRunLoopMode");
|
||
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, runLoopMode);
|
||
while(!result.has_result)
|
||
CFRunLoopRunInMode(runLoopMode, 0.1, TRUE);
|
||
|
||
if(CFRunLoopSourceIsValid(runLoopSource))
|
||
CFRunLoopSourceInvalidate(runLoopSource);
|
||
CFRelease(runLoopSource);
|
||
|
||
proxy_settings_t res;
|
||
|
||
if(result.error)
|
||
{
|
||
CFStringRef str = CFErrorCopyDescription(result.error);
|
||
fprintf(stderr, "TextMate/proxy: PAC error ‘%s’ for ‘%s’\n", cf::to_s(str).c_str(), cf::to_s(CFURLGetString(pacURL)).c_str());
|
||
CFRelease(str);
|
||
}
|
||
else if(result.proxies)
|
||
{
|
||
res = first_proxy_from_array(result.proxies, targetURL);
|
||
}
|
||
else
|
||
{
|
||
fprintf(stderr, "TextMate/proxy: No proxies returned from ‘%s’\n", cf::to_s(CFURLGetString(pacURL)).c_str());
|
||
}
|
||
|
||
if(result.proxies)
|
||
CFRelease(result.proxies);
|
||
if(result.error)
|
||
CFRelease(result.error);
|
||
|
||
if(res)
|
||
{
|
||
fprintf(stderr, "return PAC proxy\n");
|
||
return res;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
fprintf(stderr, "TextMate/proxy: Expected kCFProxyAutoConfigurationURLKey to be a CFURLRef\n");
|
||
}
|
||
}
|
||
else if(CFEqual(type, kCFProxyTypeHTTP) || CFEqual(type, kCFProxyTypeHTTPS) || CFEqual(type, kCFProxyTypeSOCKS))
|
||
{
|
||
CFStringRef hostName = (CFStringRef)CFDictionaryGetValue(proxy, kCFProxyHostNameKey);
|
||
CFNumberRef portNumber = (CFNumberRef)CFDictionaryGetValue(proxy, kCFProxyPortNumberKey);
|
||
|
||
if(CFGetTypeID(hostName) == CFStringGetTypeID() && CFGetTypeID(portNumber) == CFNumberGetTypeID())
|
||
{
|
||
auto res = user_pw_settings(hostName, portNumber);
|
||
res.socks = CFEqual(type, kCFProxyTypeSOCKS);
|
||
return res;
|
||
}
|
||
|
||
fprintf(stderr, "TextMate/proxy: Expected kCFProxyHostNameKey/kCFProxyPortNumberKey to be CFStringRef/CFNumberRef\n");
|
||
}
|
||
else
|
||
{
|
||
fprintf(stderr, "TextMate/proxy: Unknown proxy type: %s\n", cf::to_s(type).c_str());
|
||
}
|
||
}
|
||
return proxy_settings_t();
|
||
}
|
||
|
||
proxy_settings_t get_proxy_settings (std::string const& url)
|
||
{
|
||
proxy_settings_t res(false);
|
||
if(regexp::search("^https?:/{2}localhost[:/]", url))
|
||
return res;
|
||
|
||
if(CFURLRef targetURL = CFURLCreateWithString(kCFAllocatorDefault, cf::wrap(url), NULL))
|
||
{
|
||
if(CFDictionaryRef proxySettings = SCDynamicStoreCopyProxies(NULL))
|
||
{
|
||
if(CFArrayRef proxies = CFNetworkCopyProxiesForURL(targetURL, proxySettings))
|
||
{
|
||
res = first_proxy_from_array(proxies, targetURL);
|
||
CFRelease(proxies);
|
||
}
|
||
else
|
||
{
|
||
fprintf(stderr, "TextMate/proxy: NULL returned from CFNetworkCopyProxiesForURL(‘%s’)\n", url.c_str());
|
||
}
|
||
CFRelease(proxySettings);
|
||
}
|
||
else
|
||
{
|
||
fprintf(stderr, "TextMate/proxy: NULL returned from SCDynamicStoreCopyProxies()\n");
|
||
}
|
||
CFRelease(targetURL);
|
||
}
|
||
else
|
||
{
|
||
fprintf(stderr, "TextMate/proxy: Unable to create CFURLRef from ‘%s’\n", url.c_str());
|
||
}
|
||
|
||
return res;
|
||
}
|