diff --git a/source/lib/sysdep/os/unix/unix.cpp b/source/lib/sysdep/os/unix/unix.cpp index 82cb3bbf74..5014bcf3f2 100644 --- a/source/lib/sysdep/os/unix/unix.cpp +++ b/source/lib/sysdep/os/unix/unix.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2010 Wildfire Games +/* Copyright (c) 2011 Wildfire Games * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -393,6 +393,11 @@ LibError sys_generate_random_bytes(u8* buf, size_t count) return INFO::OK; } +LibError sys_get_proxy_config(const std::string& UNUSED(url), std::string& UNUSED(proxy)) +{ + return INFO::SKIPPED; +} + LibError sys_open_url(const std::string& url) { pid_t pid = fork(); diff --git a/source/lib/sysdep/os/win/wsysdep.cpp b/source/lib/sysdep/os/win/wsysdep.cpp index 5688fae1ef..76e4438056 100644 --- a/source/lib/sysdep/os/win/wsysdep.cpp +++ b/source/lib/sysdep/os/win/wsysdep.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2010 Wildfire Games +/* Copyright (c) 2011 Wildfire Games * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -32,15 +32,18 @@ #include // open_url #include #include // message crackers +#include #include "lib/sysdep/clipboard.h" #include "lib/sysdep/os/win/error_dialog.h" #include "lib/sysdep/os/win/wutil.h" +#include #if MSC_VERSION #pragma comment(lib, "shell32.lib") // for sys_pick_directory SH* calls +#pragma comment(lib, "winhttp.lib") #endif @@ -469,3 +472,112 @@ LibError sys_generate_random_bytes(u8* buffer, size_t size) return INFO::OK; } + +/* + * Given a string of the form + * "example.com:80" + * or + * "ftp=ftp.example.com:80;http=example.com:80;https=example.com:80" + * separated by semicolons or whitespace, + * return the string "example.com:80". + */ +static std::wstring parse_proxy(const std::wstring& input) +{ + if(input.find('=') == input.npos) + return input; + + std::vector parts; + split(parts, input, boost::algorithm::is_any_of("; \t\r\n"), boost::algorithm::token_compress_on); + + for(size_t i = 0; i < parts.size(); ++i) + if(boost::algorithm::starts_with(parts[i], "http=")) + return parts[i].substr(5); + + // If we got this far, proxies were only set for non-HTTP protocols + return L""; +} + +LibError sys_get_proxy_config(const std::wstring& url, std::wstring& proxy) +{ + WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions; + memset(&autoProxyOptions, 0, sizeof(autoProxyOptions)); + autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT; + autoProxyOptions.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A; + autoProxyOptions.fAutoLogonIfChallenged = TRUE; + + WINHTTP_PROXY_INFO proxyInfo; + memset(&proxyInfo, 0, sizeof(proxyInfo)); + + WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ieConfig; + memset(&ieConfig, 0, sizeof(ieConfig)); + + HINTERNET hSession = NULL; + + LibError err = INFO::SKIPPED; + + bool useAutoDetect; + + if(WinHttpGetIEProxyConfigForCurrentUser(&ieConfig)) + { + if(ieConfig.lpszAutoConfigUrl) + { + // Use explicit auto-config script if specified + useAutoDetect = true; + autoProxyOptions.dwFlags |= WINHTTP_AUTOPROXY_CONFIG_URL; + autoProxyOptions.lpszAutoConfigUrl = ieConfig.lpszAutoConfigUrl; + } + else + { + // Use auto-discovery if enabled + useAutoDetect = (ieConfig.fAutoDetect == TRUE); + } + } + else + { + // Can't find IE config settings - fall back to auto-discovery + useAutoDetect = true; + } + + if(useAutoDetect) + { + hSession = WinHttpOpen(NULL, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); + + if(hSession && WinHttpGetProxyForUrl(hSession, url.c_str(), &autoProxyOptions, &proxyInfo)) + { + proxy = parse_proxy(proxyInfo.lpszProxy); + if(!proxy.empty()) + { + err = INFO::OK; + goto done; + } + } + } + + // No valid auto-config; try explicit proxy instead + if(ieConfig.lpszProxy) + { + proxy = parse_proxy(ieConfig.lpszProxy); + if(!proxy.empty()) + { + err = INFO::OK; + goto done; + } + } + +done: + if(ieConfig.lpszProxy) + GlobalFree(ieConfig.lpszProxy); + if(ieConfig.lpszProxyBypass) + GlobalFree(ieConfig.lpszProxyBypass); + if(ieConfig.lpszAutoConfigUrl) + GlobalFree(ieConfig.lpszAutoConfigUrl); + if(proxyInfo.lpszProxy) + GlobalFree(proxyInfo.lpszProxy); + if(proxyInfo.lpszProxyBypass) + GlobalFree(proxyInfo.lpszProxyBypass); + if(hSession) + WinHttpCloseHandle(hSession); + + return err; +} diff --git a/source/lib/sysdep/sysdep.h b/source/lib/sysdep/sysdep.h index 49aff253aa..071bb09783 100644 --- a/source/lib/sysdep/sysdep.h +++ b/source/lib/sysdep/sysdep.h @@ -168,6 +168,15 @@ extern size_t sys_max_sector_size(); **/ LIB_API LibError sys_generate_random_bytes(u8* buf, size_t count); +/** + * get the proxy address for accessing the given HTTP URL. + * + * this may be very slow (tens of seconds). + * + * @return INFO::OK on success; INFO::SKIPPED if no proxy found. + **/ +LIB_API LibError sys_get_proxy_config(const std::wstring& url, std::wstring& proxy); + /** * directory separation character **/ diff --git a/source/ps/UserReport.cpp b/source/ps/UserReport.cpp index 5f0241719c..5c7198e8a6 100644 --- a/source/ps/UserReport.cpp +++ b/source/ps/UserReport.cpp @@ -20,6 +20,7 @@ #include "UserReport.h" #include "lib/timer.h" +#include "lib/utf8.h" #include "lib/external_libraries/curl.h" #include "lib/external_libraries/sdl.h" #include "lib/external_libraries/zlib.h" @@ -82,7 +83,7 @@ class CUserReporterWorker { public: CUserReporterWorker(const std::string& userID, const std::string& url) : - m_UserID(userID), m_Enabled(false), m_Shutdown(false), m_Status("disabled"), + m_URL(url), m_UserID(userID), m_Enabled(false), m_Shutdown(false), m_Status("disabled"), m_PauseUntilTime(timer_Time()), m_LastUpdateTime(timer_Time()) { // Set up libcurl: @@ -237,6 +238,15 @@ private: void Run() { + // Set libcurl's proxy configuration + // (This has to be done in the thread because it's potentially very slow) + SetStatus("proxy"); + std::wstring proxy; + if (sys_get_proxy_config(wstring_from_utf8(m_URL), proxy) == INFO::OK) + curl_easy_setopt(m_Curl, CURLOPT_PROXY, utf8_from_wstring(proxy).c_str()); + + SetStatus("waiting"); + /* * We use a semaphore to let the thread be woken up when it has * work to do. Various actions from the main thread can wake it: @@ -463,6 +473,7 @@ private: std::string m_Status; // Initialised in constructor by main thread; otherwise used only by worker thread: + std::string m_URL; std::string m_UserID; CURL* m_Curl; curl_slist* m_Headers;