Home | History | Annotate | Download | only in base
      1 /*
      2  *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include "webrtc/base/proxydetect.h"
     12 
     13 #if defined(WEBRTC_WIN)
     14 #include "webrtc/base/win32.h"
     15 #include <shlobj.h>
     16 #endif  // WEBRTC_WIN
     17 
     18 #ifdef HAVE_CONFIG_H
     19 #include "config.h"
     20 #endif
     21 
     22 #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
     23 #include <SystemConfiguration/SystemConfiguration.h>
     24 #include <CoreFoundation/CoreFoundation.h>
     25 #include <CoreServices/CoreServices.h>
     26 #include <Security/Security.h>
     27 #include "macconversion.h"
     28 #endif
     29 
     30 #ifdef WEBRTC_IOS
     31 #include <CFNetwork/CFNetwork.h>
     32 #include "macconversion.h"
     33 #endif
     34 
     35 #include <map>
     36 
     37 #include "webrtc/base/arraysize.h"
     38 #include "webrtc/base/fileutils.h"
     39 #include "webrtc/base/httpcommon.h"
     40 #include "webrtc/base/httpcommon-inl.h"
     41 #include "webrtc/base/pathutils.h"
     42 #include "webrtc/base/stringutils.h"
     43 
     44 #if defined(WEBRTC_WIN)
     45 #define _TRY_WINHTTP 1
     46 #define _TRY_JSPROXY 0
     47 #define _TRY_WM_FINDPROXY 0
     48 #define _TRY_IE_LAN_SETTINGS 1
     49 #endif  // WEBRTC_WIN
     50 
     51 // For all platforms try Firefox.
     52 #define _TRY_FIREFOX 1
     53 
     54 // Use profiles.ini to find the correct profile for this user.
     55 // If not set, we'll just look for the default one.
     56 #define USE_FIREFOX_PROFILES_INI 1
     57 
     58 static const size_t kMaxLineLength = 1024;
     59 static const char kFirefoxPattern[] = "Firefox";
     60 static const char kInternetExplorerPattern[] = "MSIE";
     61 
     62 struct StringMap {
     63  public:
     64   void Add(const char * name, const char * value) { map_[name] = value; }
     65   const std::string& Get(const char * name, const char * def = "") const {
     66     std::map<std::string, std::string>::const_iterator it =
     67         map_.find(name);
     68     if (it != map_.end())
     69       return it->second;
     70     def_ = def;
     71     return def_;
     72   }
     73   bool IsSet(const char * name) const {
     74     return (map_.find(name) != map_.end());
     75   }
     76  private:
     77   std::map<std::string, std::string> map_;
     78   mutable std::string def_;
     79 };
     80 
     81 enum UserAgent {
     82   UA_FIREFOX,
     83   UA_INTERNETEXPLORER,
     84   UA_OTHER,
     85   UA_UNKNOWN
     86 };
     87 
     88 #if _TRY_WINHTTP
     89 //#include <winhttp.h>
     90 // Note: From winhttp.h
     91 
     92 const char WINHTTP[] = "winhttp";
     93 
     94 typedef LPVOID HINTERNET;
     95 
     96 typedef struct {
     97   DWORD  dwAccessType;      // see WINHTTP_ACCESS_* types below
     98   LPWSTR lpszProxy;         // proxy server list
     99   LPWSTR lpszProxyBypass;   // proxy bypass list
    100 } WINHTTP_PROXY_INFO, * LPWINHTTP_PROXY_INFO;
    101 
    102 typedef struct {
    103   DWORD   dwFlags;
    104   DWORD   dwAutoDetectFlags;
    105   LPCWSTR lpszAutoConfigUrl;
    106   LPVOID  lpvReserved;
    107   DWORD   dwReserved;
    108   BOOL    fAutoLogonIfChallenged;
    109 } WINHTTP_AUTOPROXY_OPTIONS;
    110 
    111 typedef struct {
    112   BOOL    fAutoDetect;
    113   LPWSTR  lpszAutoConfigUrl;
    114   LPWSTR  lpszProxy;
    115   LPWSTR  lpszProxyBypass;
    116 } WINHTTP_CURRENT_USER_IE_PROXY_CONFIG;
    117 
    118 extern "C" {
    119   typedef HINTERNET (WINAPI * pfnWinHttpOpen)
    120       (
    121           IN LPCWSTR pwszUserAgent,
    122           IN DWORD   dwAccessType,
    123           IN LPCWSTR pwszProxyName   OPTIONAL,
    124           IN LPCWSTR pwszProxyBypass OPTIONAL,
    125           IN DWORD   dwFlags
    126           );
    127   typedef BOOL (STDAPICALLTYPE * pfnWinHttpCloseHandle)
    128       (
    129           IN HINTERNET hInternet
    130           );
    131   typedef BOOL (STDAPICALLTYPE * pfnWinHttpGetProxyForUrl)
    132       (
    133           IN  HINTERNET                   hSession,
    134           IN  LPCWSTR                     lpcwszUrl,
    135           IN  WINHTTP_AUTOPROXY_OPTIONS * pAutoProxyOptions,
    136           OUT WINHTTP_PROXY_INFO *        pProxyInfo
    137           );
    138   typedef BOOL (STDAPICALLTYPE * pfnWinHttpGetIEProxyConfig)
    139       (
    140           IN OUT WINHTTP_CURRENT_USER_IE_PROXY_CONFIG * pProxyConfig
    141           );
    142 
    143 } // extern "C"
    144 
    145 #define WINHTTP_AUTOPROXY_AUTO_DETECT           0x00000001
    146 #define WINHTTP_AUTOPROXY_CONFIG_URL            0x00000002
    147 #define WINHTTP_AUTOPROXY_RUN_INPROCESS         0x00010000
    148 #define WINHTTP_AUTOPROXY_RUN_OUTPROCESS_ONLY   0x00020000
    149 #define WINHTTP_AUTO_DETECT_TYPE_DHCP           0x00000001
    150 #define WINHTTP_AUTO_DETECT_TYPE_DNS_A          0x00000002
    151 #define WINHTTP_ACCESS_TYPE_DEFAULT_PROXY               0
    152 #define WINHTTP_ACCESS_TYPE_NO_PROXY                    1
    153 #define WINHTTP_ACCESS_TYPE_NAMED_PROXY                 3
    154 #define WINHTTP_NO_PROXY_NAME     NULL
    155 #define WINHTTP_NO_PROXY_BYPASS   NULL
    156 
    157 #endif // _TRY_WINHTTP
    158 
    159 #if _TRY_JSPROXY
    160 extern "C" {
    161   typedef BOOL (STDAPICALLTYPE * pfnInternetGetProxyInfo)
    162       (
    163           LPCSTR lpszUrl,
    164           DWORD dwUrlLength,
    165           LPSTR lpszUrlHostName,
    166           DWORD dwUrlHostNameLength,
    167           LPSTR * lplpszProxyHostName,
    168           LPDWORD lpdwProxyHostNameLength
    169           );
    170 } // extern "C"
    171 #endif // _TRY_JSPROXY
    172 
    173 #if _TRY_WM_FINDPROXY
    174 #include <comutil.h>
    175 #include <wmnetsourcecreator.h>
    176 #include <wmsinternaladminnetsource.h>
    177 #endif // _TRY_WM_FINDPROXY
    178 
    179 #if _TRY_IE_LAN_SETTINGS
    180 #include <wininet.h>
    181 #include <string>
    182 #endif // _TRY_IE_LAN_SETTINGS
    183 
    184 namespace rtc {
    185 
    186 //////////////////////////////////////////////////////////////////////
    187 // Utility Functions
    188 //////////////////////////////////////////////////////////////////////
    189 
    190 #if defined(WEBRTC_WIN)
    191 #ifdef _UNICODE
    192 
    193 typedef std::wstring tstring;
    194 std::string Utf8String(const tstring& str) { return ToUtf8(str); }
    195 
    196 #else  // !_UNICODE
    197 
    198 typedef std::string tstring;
    199 std::string Utf8String(const tstring& str) { return str; }
    200 
    201 #endif  // !_UNICODE
    202 #endif  // WEBRTC_WIN
    203 
    204 bool ProxyItemMatch(const Url<char>& url, char * item, size_t len) {
    205   // hostname:443
    206   if (char * port = ::strchr(item, ':')) {
    207     *port++ = '\0';
    208     if (url.port() != atol(port)) {
    209       return false;
    210     }
    211   }
    212 
    213   // A.B.C.D or A.B.C.D/24
    214   int a, b, c, d, m;
    215   int match = sscanf(item, "%d.%d.%d.%d/%d", &a, &b, &c, &d, &m);
    216   if (match >= 4) {
    217     uint32_t ip = ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) |
    218                   (d & 0xFF);
    219     if ((match < 5) || (m > 32))
    220       m = 32;
    221     else if (m < 0)
    222       m = 0;
    223     uint32_t mask = (m == 0) ? 0 : (~0UL) << (32 - m);
    224     SocketAddress addr(url.host(), 0);
    225     // TODO: Support IPv6 proxyitems. This code block is IPv4 only anyway.
    226     return !addr.IsUnresolvedIP() &&
    227         ((addr.ipaddr().v4AddressAsHostOrderInteger() & mask) == (ip & mask));
    228   }
    229 
    230   // .foo.com
    231   if (*item == '.') {
    232     size_t hostlen = url.host().length();
    233     return (hostlen > len)
    234         && (stricmp(url.host().c_str() + (hostlen - len), item) == 0);
    235   }
    236 
    237   // localhost or www.*.com
    238   if (!string_match(url.host().c_str(), item))
    239     return false;
    240 
    241   return true;
    242 }
    243 
    244 bool ProxyListMatch(const Url<char>& url, const std::string& proxy_list,
    245                     char sep) {
    246   const size_t BUFSIZE = 256;
    247   char buffer[BUFSIZE];
    248   const char* list = proxy_list.c_str();
    249   while (*list) {
    250     // Remove leading space
    251     if (isspace(*list)) {
    252       ++list;
    253       continue;
    254     }
    255     // Break on separator
    256     size_t len;
    257     const char * start = list;
    258     if (const char * end = ::strchr(list, sep)) {
    259       len = (end - list);
    260       list += len + 1;
    261     } else {
    262       len = strlen(list);
    263       list += len;
    264     }
    265     // Remove trailing space
    266     while ((len > 0) && isspace(start[len-1]))
    267       --len;
    268     // Check for oversized entry
    269     if (len >= BUFSIZE)
    270       continue;
    271     memcpy(buffer, start, len);
    272     buffer[len] = 0;
    273     if (!ProxyItemMatch(url, buffer, len))
    274       continue;
    275     return true;
    276   }
    277   return false;
    278 }
    279 
    280 bool Better(ProxyType lhs, const ProxyType rhs) {
    281   // PROXY_NONE, PROXY_HTTPS, PROXY_SOCKS5, PROXY_UNKNOWN
    282   const int PROXY_VALUE[5] = { 0, 2, 3, 1 };
    283   return (PROXY_VALUE[lhs] > PROXY_VALUE[rhs]);
    284 }
    285 
    286 bool ParseProxy(const std::string& saddress, ProxyInfo* proxy) {
    287   const size_t kMaxAddressLength = 1024;
    288   // Allow semicolon, space, or tab as an address separator
    289   const char* const kAddressSeparator = " ;\t";
    290 
    291   ProxyType ptype;
    292   std::string host;
    293   uint16_t port;
    294 
    295   const char* address = saddress.c_str();
    296   while (*address) {
    297     size_t len;
    298     const char * start = address;
    299     if (const char * sep = strchr(address, kAddressSeparator)) {
    300       len = (sep - address);
    301       address += len + 1;
    302       while (*address != '\0' && ::strchr(kAddressSeparator, *address)) {
    303         address += 1;
    304       }
    305     } else {
    306       len = strlen(address);
    307       address += len;
    308     }
    309 
    310     if (len > kMaxAddressLength - 1) {
    311       LOG(LS_WARNING) << "Proxy address too long [" << start << "]";
    312       continue;
    313     }
    314 
    315     char buffer[kMaxAddressLength];
    316     memcpy(buffer, start, len);
    317     buffer[len] = 0;
    318 
    319     char * colon = ::strchr(buffer, ':');
    320     if (!colon) {
    321       LOG(LS_WARNING) << "Proxy address without port [" << buffer << "]";
    322       continue;
    323     }
    324 
    325     *colon = 0;
    326     char * endptr;
    327     port = static_cast<uint16_t>(strtol(colon + 1, &endptr, 0));
    328     if (*endptr != 0) {
    329       LOG(LS_WARNING) << "Proxy address with invalid port [" << buffer << "]";
    330       continue;
    331     }
    332 
    333     if (char * equals = ::strchr(buffer, '=')) {
    334       *equals = 0;
    335       host = equals + 1;
    336       if (_stricmp(buffer, "socks") == 0) {
    337         ptype = PROXY_SOCKS5;
    338       } else if (_stricmp(buffer, "https") == 0) {
    339         ptype = PROXY_HTTPS;
    340       } else {
    341         LOG(LS_WARNING) << "Proxy address with unknown protocol ["
    342                         << buffer << "]";
    343         ptype = PROXY_UNKNOWN;
    344       }
    345     } else {
    346       host = buffer;
    347       ptype = PROXY_UNKNOWN;
    348     }
    349 
    350     if (Better(ptype, proxy->type)) {
    351       proxy->type = ptype;
    352       proxy->address.SetIP(host);
    353       proxy->address.SetPort(port);
    354     }
    355   }
    356 
    357   return proxy->type != PROXY_NONE;
    358 }
    359 
    360 UserAgent GetAgent(const char* agent) {
    361   if (agent) {
    362     std::string agent_str(agent);
    363     if (agent_str.find(kFirefoxPattern) != std::string::npos) {
    364       return UA_FIREFOX;
    365     } else if (agent_str.find(kInternetExplorerPattern) != std::string::npos) {
    366       return UA_INTERNETEXPLORER;
    367     } else if (agent_str.empty()) {
    368       return UA_UNKNOWN;
    369     }
    370   }
    371   return UA_OTHER;
    372 }
    373 
    374 bool EndsWith(const std::string& a, const std::string& b) {
    375   if (b.size() > a.size()) {
    376     return false;
    377   }
    378   int result = a.compare(a.size() - b.size(), b.size(), b);
    379   return result == 0;
    380 }
    381 
    382 bool GetFirefoxProfilePath(Pathname* path) {
    383 #if defined(WEBRTC_WIN)
    384   wchar_t w_path[MAX_PATH];
    385   if (SHGetFolderPath(0, CSIDL_APPDATA, 0, SHGFP_TYPE_CURRENT, w_path) !=
    386       S_OK) {
    387     LOG(LS_ERROR) << "SHGetFolderPath failed";
    388     return false;
    389   }
    390   path->SetFolder(ToUtf8(w_path, wcslen(w_path)));
    391   path->AppendFolder("Mozilla");
    392   path->AppendFolder("Firefox");
    393 #elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
    394   FSRef fr;
    395   if (0 != FSFindFolder(kUserDomain, kApplicationSupportFolderType,
    396                         kCreateFolder, &fr)) {
    397     LOG(LS_ERROR) << "FSFindFolder failed";
    398     return false;
    399   }
    400   char buffer[NAME_MAX + 1];
    401   if (0 != FSRefMakePath(&fr, reinterpret_cast<uint8_t*>(buffer),
    402                          arraysize(buffer))) {
    403     LOG(LS_ERROR) << "FSRefMakePath failed";
    404     return false;
    405   }
    406   path->SetFolder(std::string(buffer));
    407   path->AppendFolder("Firefox");
    408 #else
    409   char* user_home = getenv("HOME");
    410   if (user_home == NULL) {
    411     return false;
    412   }
    413   path->SetFolder(std::string(user_home));
    414   path->AppendFolder(".mozilla");
    415   path->AppendFolder("firefox");
    416 #endif  // WEBRTC_WIN
    417   return true;
    418 }
    419 
    420 bool GetDefaultFirefoxProfile(Pathname* profile_path) {
    421   ASSERT(NULL != profile_path);
    422   Pathname path;
    423   if (!GetFirefoxProfilePath(&path)) {
    424     return false;
    425   }
    426 
    427 #if USE_FIREFOX_PROFILES_INI
    428   // [Profile0]
    429   // Name=default
    430   // IsRelative=1
    431   // Path=Profiles/2de53ejb.default
    432   // Default=1
    433 
    434   // Note: we are looking for the first entry with "Default=1", or the last
    435   // entry in the file
    436   path.SetFilename("profiles.ini");
    437   scoped_ptr<FileStream> fs(Filesystem::OpenFile(path, "r"));
    438   if (!fs) {
    439     return false;
    440   }
    441   Pathname candidate;
    442   bool relative = true;
    443   std::string line;
    444   while (fs->ReadLine(&line) == SR_SUCCESS) {
    445     if (line.length() == 0) {
    446       continue;
    447     }
    448     if (line.at(0) == '[') {
    449       relative = true;
    450       candidate.clear();
    451     } else if (line.find("IsRelative=") == 0 &&
    452                line.length() >= 12) {
    453       // TODO: The initial Linux public launch revealed a fairly
    454       // high number of machines where IsRelative= did not have anything after
    455       // it. Perhaps that is legal profiles.ini syntax?
    456       relative = (line.at(11) != '0');
    457     } else if (line.find("Path=") == 0 &&
    458                line.length() >= 6) {
    459       if (relative) {
    460         candidate = path;
    461       } else {
    462         candidate.clear();
    463       }
    464       candidate.AppendFolder(line.substr(5));
    465     } else if (line.find("Default=") == 0 &&
    466                line.length() >= 9) {
    467       if ((line.at(8) != '0') && !candidate.empty()) {
    468         break;
    469       }
    470     }
    471   }
    472   fs->Close();
    473   if (candidate.empty()) {
    474     return false;
    475   }
    476   profile_path->SetPathname(candidate.pathname());
    477 
    478 #else // !USE_FIREFOX_PROFILES_INI
    479   path.AppendFolder("Profiles");
    480   DirectoryIterator* it = Filesystem::IterateDirectory();
    481   it->Iterate(path);
    482   std::string extension(".default");
    483   while (!EndsWith(it->Name(), extension)) {
    484     if (!it->Next()) {
    485       return false;
    486     }
    487   }
    488 
    489   profile_path->SetPathname(path);
    490   profile->AppendFolder("Profiles");
    491   profile->AppendFolder(it->Name());
    492   delete it;
    493 
    494 #endif // !USE_FIREFOX_PROFILES_INI
    495 
    496   return true;
    497 }
    498 
    499 bool ReadFirefoxPrefs(const Pathname& filename,
    500                       const char * prefix,
    501                       StringMap* settings) {
    502   scoped_ptr<FileStream> fs(Filesystem::OpenFile(filename, "r"));
    503   if (!fs) {
    504     LOG(LS_ERROR) << "Failed to open file: " << filename.pathname();
    505     return false;
    506   }
    507 
    508   std::string line;
    509   while (fs->ReadLine(&line) == SR_SUCCESS) {
    510     size_t prefix_len = strlen(prefix);
    511 
    512     // Skip blank lines and too long lines.
    513     if ((line.length() == 0) || (line.length() > kMaxLineLength)
    514         || (line.at(0) == '#') || line.compare(0, 2, "/*") == 0
    515         || line.compare(0, 2, " *") == 0) {
    516       continue;
    517     }
    518 
    519     char buffer[kMaxLineLength];
    520     strcpyn(buffer, sizeof(buffer), line.c_str());
    521     int nstart = 0, nend = 0, vstart = 0, vend = 0;
    522     sscanf(buffer, "user_pref(\"%n%*[^\"]%n\", %n%*[^)]%n);",
    523            &nstart, &nend, &vstart, &vend);
    524     if (vend > 0) {
    525       char* name = buffer + nstart;
    526       name[nend - nstart] = 0;
    527       if ((vend - vstart >= 2) && (buffer[vstart] == '"')) {
    528         vstart += 1;
    529         vend -= 1;
    530       }
    531       char* value = buffer + vstart;
    532       value[vend - vstart] = 0;
    533       if ((strncmp(name, prefix, prefix_len) == 0) && *value) {
    534         settings->Add(name + prefix_len, value);
    535       }
    536     } else {
    537       LOG_F(LS_WARNING) << "Unparsed pref [" << buffer << "]";
    538     }
    539   }
    540   fs->Close();
    541   return true;
    542 }
    543 
    544 bool GetFirefoxProxySettings(const char* url, ProxyInfo* proxy) {
    545   Url<char> purl(url);
    546   Pathname path;
    547   bool success = false;
    548   if (GetDefaultFirefoxProfile(&path)) {
    549     StringMap settings;
    550     path.SetFilename("prefs.js");
    551     if (ReadFirefoxPrefs(path, "network.proxy.", &settings)) {
    552       success = true;
    553       proxy->bypass_list =
    554           settings.Get("no_proxies_on", "localhost, 127.0.0.1");
    555       if (settings.Get("type") == "1") {
    556         // User has manually specified a proxy, try to figure out what
    557         // type it is.
    558         if (ProxyListMatch(purl, proxy->bypass_list.c_str(), ',')) {
    559           // Our url is in the list of url's to bypass proxy.
    560         } else if (settings.Get("share_proxy_settings") == "true") {
    561           proxy->type = PROXY_UNKNOWN;
    562           proxy->address.SetIP(settings.Get("http"));
    563           proxy->address.SetPort(atoi(settings.Get("http_port").c_str()));
    564         } else if (settings.IsSet("socks")) {
    565           proxy->type = PROXY_SOCKS5;
    566           proxy->address.SetIP(settings.Get("socks"));
    567           proxy->address.SetPort(atoi(settings.Get("socks_port").c_str()));
    568         } else if (settings.IsSet("ssl")) {
    569           proxy->type = PROXY_HTTPS;
    570           proxy->address.SetIP(settings.Get("ssl"));
    571           proxy->address.SetPort(atoi(settings.Get("ssl_port").c_str()));
    572         } else if (settings.IsSet("http")) {
    573           proxy->type = PROXY_HTTPS;
    574           proxy->address.SetIP(settings.Get("http"));
    575           proxy->address.SetPort(atoi(settings.Get("http_port").c_str()));
    576         }
    577       } else if (settings.Get("type") == "2") {
    578         // Browser is configured to get proxy settings from a given url.
    579         proxy->autoconfig_url = settings.Get("autoconfig_url").c_str();
    580       } else if (settings.Get("type") == "4") {
    581         // Browser is configured to auto detect proxy config.
    582         proxy->autodetect = true;
    583       } else {
    584         // No proxy set.
    585       }
    586     }
    587   }
    588   return success;
    589 }
    590 
    591 #if defined(WEBRTC_WIN)  // Windows specific implementation for reading Internet
    592               // Explorer proxy settings.
    593 
    594 void LogGetProxyFault() {
    595   LOG_GLEM(LERROR, WINHTTP) << "WinHttpGetProxyForUrl faulted!!";
    596 }
    597 
    598 BOOL MyWinHttpGetProxyForUrl(pfnWinHttpGetProxyForUrl pWHGPFU,
    599                              HINTERNET hWinHttp, LPCWSTR url,
    600                              WINHTTP_AUTOPROXY_OPTIONS *options,
    601                              WINHTTP_PROXY_INFO *info) {
    602   // WinHttpGetProxyForUrl() can call plugins which can crash.
    603   // In the case of McAfee scriptproxy.dll, it does crash in
    604   // older versions. Try to catch crashes here and treat as an
    605   // error.
    606   BOOL success = FALSE;
    607 
    608 #if (_HAS_EXCEPTIONS == 0)
    609   __try {
    610     success = pWHGPFU(hWinHttp, url, options, info);
    611   } __except(EXCEPTION_EXECUTE_HANDLER) {
    612     // This is a separate function to avoid
    613     // Visual C++ error 2712 when compiling with C++ EH
    614     LogGetProxyFault();
    615   }
    616 #else
    617   success = pWHGPFU(hWinHttp, url, options, info);
    618 #endif  // (_HAS_EXCEPTIONS == 0)
    619 
    620   return success;
    621 }
    622 
    623 bool IsDefaultBrowserFirefox() {
    624   HKEY key;
    625   LONG result = RegOpenKeyEx(HKEY_CLASSES_ROOT, L"http\\shell\\open\\command",
    626                              0, KEY_READ, &key);
    627   if (ERROR_SUCCESS != result)
    628     return false;
    629 
    630   DWORD size, type;
    631   bool success = false;
    632   result = RegQueryValueEx(key, L"", 0, &type, NULL, &size);
    633   if (result == ERROR_SUCCESS && type == REG_SZ) {
    634     wchar_t* value = new wchar_t[size+1];
    635     BYTE* buffer = reinterpret_cast<BYTE*>(value);
    636     result = RegQueryValueEx(key, L"", 0, &type, buffer, &size);
    637     if (result == ERROR_SUCCESS) {
    638       // Size returned by RegQueryValueEx is in bytes, convert to number of
    639       // wchar_t's.
    640       size /= sizeof(value[0]);
    641       value[size] = L'\0';
    642       for (size_t i = 0; i < size; ++i) {
    643         value[i] = tolowercase(value[i]);
    644       }
    645       success = (NULL != strstr(value, L"firefox.exe"));
    646     }
    647     delete[] value;
    648   }
    649 
    650   RegCloseKey(key);
    651   return success;
    652 }
    653 
    654 bool GetWinHttpProxySettings(const char* url, ProxyInfo* proxy) {
    655   HMODULE winhttp_handle = LoadLibrary(L"winhttp.dll");
    656   if (winhttp_handle == NULL) {
    657     LOG(LS_ERROR) << "Failed to load winhttp.dll.";
    658     return false;
    659   }
    660   WINHTTP_CURRENT_USER_IE_PROXY_CONFIG iecfg;
    661   memset(&iecfg, 0, sizeof(iecfg));
    662   Url<char> purl(url);
    663   pfnWinHttpGetIEProxyConfig pWHGIEPC =
    664       reinterpret_cast<pfnWinHttpGetIEProxyConfig>(
    665           GetProcAddress(winhttp_handle,
    666                          "WinHttpGetIEProxyConfigForCurrentUser"));
    667   bool success = false;
    668   if (pWHGIEPC && pWHGIEPC(&iecfg)) {
    669     // We were read proxy config successfully.
    670     success = true;
    671     if (iecfg.fAutoDetect) {
    672       proxy->autodetect = true;
    673     }
    674     if (iecfg.lpszAutoConfigUrl) {
    675       proxy->autoconfig_url = ToUtf8(iecfg.lpszAutoConfigUrl);
    676       GlobalFree(iecfg.lpszAutoConfigUrl);
    677     }
    678     if (iecfg.lpszProxyBypass) {
    679       proxy->bypass_list = ToUtf8(iecfg.lpszProxyBypass);
    680       GlobalFree(iecfg.lpszProxyBypass);
    681     }
    682     if (iecfg.lpszProxy) {
    683       if (!ProxyListMatch(purl, proxy->bypass_list, ';')) {
    684         ParseProxy(ToUtf8(iecfg.lpszProxy), proxy);
    685       }
    686       GlobalFree(iecfg.lpszProxy);
    687     }
    688   }
    689   FreeLibrary(winhttp_handle);
    690   return success;
    691 }
    692 
    693 // Uses the WinHTTP API to auto detect proxy for the given url. Firefox and IE
    694 // have slightly different option dialogs for proxy settings. In Firefox,
    695 // either a location of a proxy configuration file can be specified or auto
    696 // detection can be selected. In IE theese two options can be independently
    697 // selected. For the case where both options are selected (only IE) we try to
    698 // fetch the config file first, and if that fails we'll perform an auto
    699 // detection.
    700 //
    701 // Returns true if we successfully performed an auto detection not depending on
    702 // whether we found a proxy or not. Returns false on error.
    703 bool WinHttpAutoDetectProxyForUrl(const char* agent, const char* url,
    704                                   ProxyInfo* proxy) {
    705   Url<char> purl(url);
    706   bool success = true;
    707   HMODULE winhttp_handle = LoadLibrary(L"winhttp.dll");
    708   if (winhttp_handle == NULL) {
    709     LOG(LS_ERROR) << "Failed to load winhttp.dll.";
    710     return false;
    711   }
    712   pfnWinHttpOpen pWHO =
    713       reinterpret_cast<pfnWinHttpOpen>(GetProcAddress(winhttp_handle,
    714                                                       "WinHttpOpen"));
    715   pfnWinHttpCloseHandle pWHCH =
    716       reinterpret_cast<pfnWinHttpCloseHandle>(
    717           GetProcAddress(winhttp_handle, "WinHttpCloseHandle"));
    718   pfnWinHttpGetProxyForUrl pWHGPFU =
    719       reinterpret_cast<pfnWinHttpGetProxyForUrl>(
    720           GetProcAddress(winhttp_handle, "WinHttpGetProxyForUrl"));
    721   if (pWHO && pWHCH && pWHGPFU) {
    722     if (HINTERNET hWinHttp = pWHO(ToUtf16(agent).c_str(),
    723                                   WINHTTP_ACCESS_TYPE_NO_PROXY,
    724                                   WINHTTP_NO_PROXY_NAME,
    725                                   WINHTTP_NO_PROXY_BYPASS,
    726                                   0)) {
    727       BOOL result = FALSE;
    728       WINHTTP_PROXY_INFO info;
    729       memset(&info, 0, sizeof(info));
    730       if (proxy->autodetect) {
    731         // Use DHCP and DNS to try to find any proxy to use.
    732         WINHTTP_AUTOPROXY_OPTIONS options;
    733         memset(&options, 0, sizeof(options));
    734         options.fAutoLogonIfChallenged = TRUE;
    735 
    736         options.dwFlags |= WINHTTP_AUTOPROXY_AUTO_DETECT;
    737         options.dwAutoDetectFlags |= WINHTTP_AUTO_DETECT_TYPE_DHCP
    738             | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
    739         result = MyWinHttpGetProxyForUrl(
    740             pWHGPFU, hWinHttp, ToUtf16(url).c_str(), &options, &info);
    741       }
    742       if (!result && !proxy->autoconfig_url.empty()) {
    743         // We have the location of a proxy config file. Download it and
    744         // execute it to find proxy settings for our url.
    745         WINHTTP_AUTOPROXY_OPTIONS options;
    746         memset(&options, 0, sizeof(options));
    747         memset(&info, 0, sizeof(info));
    748         options.fAutoLogonIfChallenged = TRUE;
    749 
    750         std::wstring autoconfig_url16((ToUtf16)(proxy->autoconfig_url));
    751         options.dwFlags |= WINHTTP_AUTOPROXY_CONFIG_URL;
    752         options.lpszAutoConfigUrl = autoconfig_url16.c_str();
    753 
    754         result = MyWinHttpGetProxyForUrl(
    755             pWHGPFU, hWinHttp, ToUtf16(url).c_str(), &options, &info);
    756       }
    757       if (result) {
    758         // Either the given auto config url was valid or auto
    759         // detection found a proxy on this network.
    760         if (info.lpszProxy) {
    761           // TODO: Does this bypass list differ from the list
    762           // retreived from GetWinHttpProxySettings earlier?
    763           if (info.lpszProxyBypass) {
    764             proxy->bypass_list = ToUtf8(info.lpszProxyBypass);
    765             GlobalFree(info.lpszProxyBypass);
    766           } else {
    767             proxy->bypass_list.clear();
    768           }
    769           if (!ProxyListMatch(purl, proxy->bypass_list, ';')) {
    770             // Found proxy for this URL. If parsing the address turns
    771             // out ok then we are successful.
    772             success = ParseProxy(ToUtf8(info.lpszProxy), proxy);
    773           }
    774           GlobalFree(info.lpszProxy);
    775         }
    776       } else {
    777         // We could not find any proxy for this url.
    778         LOG(LS_INFO) << "No proxy detected for " << url;
    779       }
    780       pWHCH(hWinHttp);
    781     }
    782   } else {
    783     LOG(LS_ERROR) << "Failed loading WinHTTP functions.";
    784     success = false;
    785   }
    786   FreeLibrary(winhttp_handle);
    787   return success;
    788 }
    789 
    790 #if 0  // Below functions currently not used.
    791 
    792 bool GetJsProxySettings(const char* url, ProxyInfo* proxy) {
    793   Url<char> purl(url);
    794   bool success = false;
    795 
    796   if (HMODULE hModJS = LoadLibrary(_T("jsproxy.dll"))) {
    797     pfnInternetGetProxyInfo pIGPI =
    798         reinterpret_cast<pfnInternetGetProxyInfo>(
    799             GetProcAddress(hModJS, "InternetGetProxyInfo"));
    800     if (pIGPI) {
    801       char proxy[256], host[256];
    802       memset(proxy, 0, sizeof(proxy));
    803       char * ptr = proxy;
    804       DWORD proxylen = sizeof(proxy);
    805       std::string surl = Utf8String(url);
    806       DWORD hostlen = _snprintf(host, sizeof(host), "http%s://%S",
    807                                 purl.secure() ? "s" : "", purl.server());
    808       if (pIGPI(surl.data(), surl.size(), host, hostlen, &ptr, &proxylen)) {
    809         LOG(INFO) << "Proxy: " << proxy;
    810       } else {
    811         LOG_GLE(INFO) << "InternetGetProxyInfo";
    812       }
    813     }
    814     FreeLibrary(hModJS);
    815   }
    816   return success;
    817 }
    818 
    819 bool GetWmProxySettings(const char* url, ProxyInfo* proxy) {
    820   Url<char> purl(url);
    821   bool success = false;
    822 
    823   INSNetSourceCreator * nsc = 0;
    824   HRESULT hr = CoCreateInstance(CLSID_ClientNetManager, 0, CLSCTX_ALL,
    825                                 IID_INSNetSourceCreator, (LPVOID *) &nsc);
    826   if (SUCCEEDED(hr)) {
    827     if (SUCCEEDED(hr = nsc->Initialize())) {
    828       VARIANT dispatch;
    829       VariantInit(&dispatch);
    830       if (SUCCEEDED(hr = nsc->GetNetSourceAdminInterface(L"http", &dispatch))) {
    831         IWMSInternalAdminNetSource * ians = 0;
    832         if (SUCCEEDED(hr = dispatch.pdispVal->QueryInterface(
    833                 IID_IWMSInternalAdminNetSource, (LPVOID *) &ians))) {
    834           _bstr_t host(purl.server());
    835           BSTR proxy = 0;
    836           BOOL bProxyEnabled = FALSE;
    837           DWORD port, context = 0;
    838           if (SUCCEEDED(hr = ians->FindProxyForURL(
    839                   L"http", host, &bProxyEnabled, &proxy, &port, &context))) {
    840             success = true;
    841             if (bProxyEnabled) {
    842               _bstr_t sproxy = proxy;
    843               proxy->ptype = PT_HTTPS;
    844               proxy->host = sproxy;
    845               proxy->port = port;
    846             }
    847           }
    848           SysFreeString(proxy);
    849           if (FAILED(hr = ians->ShutdownProxyContext(context))) {
    850             LOG(LS_INFO) << "IWMSInternalAdminNetSource::ShutdownProxyContext"
    851                          << "failed: " << hr;
    852           }
    853           ians->Release();
    854         }
    855       }
    856       VariantClear(&dispatch);
    857       if (FAILED(hr = nsc->Shutdown())) {
    858         LOG(LS_INFO) << "INSNetSourceCreator::Shutdown failed: " << hr;
    859       }
    860     }
    861     nsc->Release();
    862   }
    863   return success;
    864 }
    865 
    866 bool GetIePerConnectionProxySettings(const char* url, ProxyInfo* proxy) {
    867   Url<char> purl(url);
    868   bool success = false;
    869 
    870   INTERNET_PER_CONN_OPTION_LIST list;
    871   INTERNET_PER_CONN_OPTION options[3];
    872   memset(&list, 0, sizeof(list));
    873   memset(&options, 0, sizeof(options));
    874 
    875   list.dwSize = sizeof(list);
    876   list.dwOptionCount = 3;
    877   list.pOptions = options;
    878   options[0].dwOption = INTERNET_PER_CONN_FLAGS;
    879   options[1].dwOption = INTERNET_PER_CONN_PROXY_SERVER;
    880   options[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS;
    881   DWORD dwSize = sizeof(list);
    882 
    883   if (!InternetQueryOption(0, INTERNET_OPTION_PER_CONNECTION_OPTION, &list,
    884                            &dwSize)) {
    885     LOG(LS_INFO) << "InternetQueryOption failed: " << GetLastError();
    886   } else if ((options[0].Value.dwValue & PROXY_TYPE_PROXY) != 0) {
    887     success = true;
    888     if (!ProxyListMatch(purl, nonnull(options[2].Value.pszValue), _T(';'))) {
    889       ParseProxy(nonnull(options[1].Value.pszValue), proxy);
    890     }
    891   } else if ((options[0].Value.dwValue & PROXY_TYPE_DIRECT) != 0) {
    892     success = true;
    893   } else {
    894     LOG(LS_INFO) << "unknown internet access type: "
    895                  << options[0].Value.dwValue;
    896   }
    897   if (options[1].Value.pszValue) {
    898     GlobalFree(options[1].Value.pszValue);
    899   }
    900   if (options[2].Value.pszValue) {
    901     GlobalFree(options[2].Value.pszValue);
    902   }
    903   return success;
    904 }
    905 
    906 #endif  // 0
    907 
    908 // Uses the InternetQueryOption function to retrieve proxy settings
    909 // from the registry. This will only give us the 'static' settings,
    910 // ie, not any information about auto config etc.
    911 bool GetIeLanProxySettings(const char* url, ProxyInfo* proxy) {
    912   Url<char> purl(url);
    913   bool success = false;
    914 
    915   wchar_t buffer[1024];
    916   memset(buffer, 0, sizeof(buffer));
    917   INTERNET_PROXY_INFO * info = reinterpret_cast<INTERNET_PROXY_INFO *>(buffer);
    918   DWORD dwSize = sizeof(buffer);
    919 
    920   if (!InternetQueryOption(0, INTERNET_OPTION_PROXY, info, &dwSize)) {
    921     LOG(LS_INFO) << "InternetQueryOption failed: " << GetLastError();
    922   } else if (info->dwAccessType == INTERNET_OPEN_TYPE_DIRECT) {
    923     success = true;
    924   } else if (info->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
    925     success = true;
    926     if (!ProxyListMatch(purl, nonnull(reinterpret_cast<const char*>(
    927             info->lpszProxyBypass)), ' ')) {
    928       ParseProxy(nonnull(reinterpret_cast<const char*>(info->lpszProxy)),
    929                  proxy);
    930     }
    931   } else {
    932     LOG(LS_INFO) << "unknown internet access type: " << info->dwAccessType;
    933   }
    934   return success;
    935 }
    936 
    937 bool GetIeProxySettings(const char* agent, const char* url, ProxyInfo* proxy) {
    938   bool success = GetWinHttpProxySettings(url, proxy);
    939   if (!success) {
    940     // TODO: Should always call this if no proxy were detected by
    941     // GetWinHttpProxySettings?
    942     // WinHttp failed. Try using the InternetOptionQuery method instead.
    943     return GetIeLanProxySettings(url, proxy);
    944   }
    945   return true;
    946 }
    947 
    948 #endif  // WEBRTC_WIN
    949 
    950 #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)  // WEBRTC_MAC && !defined(WEBRTC_IOS) specific implementation for reading system wide
    951             // proxy settings.
    952 
    953 bool p_getProxyInfoForTypeFromDictWithKeys(ProxyInfo* proxy,
    954                                            ProxyType type,
    955                                            const CFDictionaryRef proxyDict,
    956                                            const CFStringRef enabledKey,
    957                                            const CFStringRef hostKey,
    958                                            const CFStringRef portKey) {
    959   // whether or not we set up the proxy info.
    960   bool result = false;
    961 
    962   // we use this as a scratch variable for determining if operations
    963   // succeeded.
    964   bool converted = false;
    965 
    966   // the data we need to construct the SocketAddress for the proxy.
    967   std::string hostname;
    968   int port;
    969 
    970   if ((proxyDict != NULL) &&
    971       (CFGetTypeID(proxyDict) == CFDictionaryGetTypeID())) {
    972     // CoreFoundation stuff that we'll have to get from
    973     // the dictionaries and interpret or convert into more usable formats.
    974     CFNumberRef enabledCFNum;
    975     CFNumberRef portCFNum;
    976     CFStringRef hostCFStr;
    977 
    978     enabledCFNum = (CFNumberRef)CFDictionaryGetValue(proxyDict, enabledKey);
    979 
    980     if (p_isCFNumberTrue(enabledCFNum)) {
    981       // let's see if we can get the address and port.
    982       hostCFStr = (CFStringRef)CFDictionaryGetValue(proxyDict, hostKey);
    983       converted = p_convertHostCFStringRefToCPPString(hostCFStr, hostname);
    984       if (converted) {
    985         portCFNum = (CFNumberRef)CFDictionaryGetValue(proxyDict, portKey);
    986         converted = p_convertCFNumberToInt(portCFNum, &port);
    987         if (converted) {
    988           // we have something enabled, with a hostname and a port.
    989           // That's sufficient to set up the proxy info.
    990           proxy->type = type;
    991           proxy->address.SetIP(hostname);
    992           proxy->address.SetPort(port);
    993           result = true;
    994         }
    995       }
    996     }
    997   }
    998 
    999   return result;
   1000 }
   1001 
   1002 // Looks for proxy information in the given dictionary,
   1003 // return true if it found sufficient information to define one,
   1004 // false otherwise.  This is guaranteed to not change the values in proxy
   1005 // unless a full-fledged proxy description was discovered in the dictionary.
   1006 // However, at the present time this does not support username or password.
   1007 // Checks first for a SOCKS proxy, then for HTTPS, then HTTP.
   1008 bool GetMacProxySettingsFromDictionary(ProxyInfo* proxy,
   1009                                        const CFDictionaryRef proxyDict) {
   1010   // the function result.
   1011   bool gotProxy = false;
   1012 
   1013 
   1014   // first we see if there's a SOCKS proxy in place.
   1015   gotProxy = p_getProxyInfoForTypeFromDictWithKeys(proxy,
   1016                                                    PROXY_SOCKS5,
   1017                                                    proxyDict,
   1018                                                    kSCPropNetProxiesSOCKSEnable,
   1019                                                    kSCPropNetProxiesSOCKSProxy,
   1020                                                    kSCPropNetProxiesSOCKSPort);
   1021 
   1022   if (!gotProxy) {
   1023     // okay, no SOCKS proxy, let's look for https.
   1024     gotProxy = p_getProxyInfoForTypeFromDictWithKeys(proxy,
   1025                                                PROXY_HTTPS,
   1026                                                proxyDict,
   1027                                                kSCPropNetProxiesHTTPSEnable,
   1028                                                kSCPropNetProxiesHTTPSProxy,
   1029                                                kSCPropNetProxiesHTTPSPort);
   1030     if (!gotProxy) {
   1031       // Finally, try HTTP proxy. Note that flute doesn't
   1032       // differentiate between HTTPS and HTTP, hence we are using the
   1033       // same flute type here, ie. PROXY_HTTPS.
   1034       gotProxy = p_getProxyInfoForTypeFromDictWithKeys(
   1035           proxy, PROXY_HTTPS, proxyDict, kSCPropNetProxiesHTTPEnable,
   1036           kSCPropNetProxiesHTTPProxy, kSCPropNetProxiesHTTPPort);
   1037     }
   1038   }
   1039   return gotProxy;
   1040 }
   1041 
   1042 // TODO(hughv) Update keychain functions. They work on 10.8, but are depricated.
   1043 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
   1044 bool p_putPasswordInProxyInfo(ProxyInfo* proxy) {
   1045   bool result = true;  // by default we assume we're good.
   1046   // for all we know there isn't any password.  We'll set to false
   1047   // if we find a problem.
   1048 
   1049   // Ask the keychain for an internet password search for the given protocol.
   1050   OSStatus oss = 0;
   1051   SecKeychainAttributeList attrList;
   1052   attrList.count = 3;
   1053   SecKeychainAttribute attributes[3];
   1054   attrList.attr = attributes;
   1055 
   1056   attributes[0].tag = kSecProtocolItemAttr;
   1057   attributes[0].length = sizeof(SecProtocolType);
   1058   SecProtocolType protocol;
   1059   switch (proxy->type) {
   1060     case PROXY_HTTPS :
   1061       protocol = kSecProtocolTypeHTTPS;
   1062       break;
   1063     case PROXY_SOCKS5 :
   1064       protocol = kSecProtocolTypeSOCKS;
   1065       break;
   1066     default :
   1067       LOG(LS_ERROR) << "asked for proxy password for unknown proxy type.";
   1068       result = false;
   1069       break;
   1070   }
   1071   attributes[0].data = &protocol;
   1072 
   1073   UInt32 port = proxy->address.port();
   1074   attributes[1].tag = kSecPortItemAttr;
   1075   attributes[1].length = sizeof(UInt32);
   1076   attributes[1].data = &port;
   1077 
   1078   std::string ip = proxy->address.ipaddr().ToString();
   1079   attributes[2].tag = kSecServerItemAttr;
   1080   attributes[2].length = ip.length();
   1081   attributes[2].data = const_cast<char*>(ip.c_str());
   1082 
   1083   if (result) {
   1084     LOG(LS_INFO) << "trying to get proxy username/password";
   1085     SecKeychainSearchRef sref;
   1086     oss = SecKeychainSearchCreateFromAttributes(NULL,
   1087                                                 kSecInternetPasswordItemClass,
   1088                                                 &attrList, &sref);
   1089     if (0 == oss) {
   1090       LOG(LS_INFO) << "SecKeychainSearchCreateFromAttributes was good";
   1091       // Get the first item, if there is one.
   1092       SecKeychainItemRef iref;
   1093       oss = SecKeychainSearchCopyNext(sref, &iref);
   1094       if (0 == oss) {
   1095         LOG(LS_INFO) << "...looks like we have the username/password data";
   1096         // If there is, get the username and the password.
   1097 
   1098         SecKeychainAttributeInfo attribsToGet;
   1099         attribsToGet.count = 1;
   1100         UInt32 tag = kSecAccountItemAttr;
   1101         UInt32 format = CSSM_DB_ATTRIBUTE_FORMAT_STRING;
   1102         void *data;
   1103         UInt32 length;
   1104         SecKeychainAttributeList *localList;
   1105 
   1106         attribsToGet.tag = &tag;
   1107         attribsToGet.format = &format;
   1108         OSStatus copyres = SecKeychainItemCopyAttributesAndData(iref,
   1109                                                                 &attribsToGet,
   1110                                                                 NULL,
   1111                                                                 &localList,
   1112                                                                 &length,
   1113                                                                 &data);
   1114         if (0 == copyres) {
   1115           LOG(LS_INFO) << "...and we can pull it out.";
   1116           // now, we know from experimentation (sadly not from docs)
   1117           // that the username is in the local attribute list,
   1118           // and the password in the data,
   1119           // both without null termination but with info on their length.
   1120           // grab the password from the data.
   1121           std::string password;
   1122           password.append(static_cast<const char*>(data), length);
   1123 
   1124           // make the password into a CryptString
   1125           // huh, at the time of writing, you can't.
   1126           // so we'll skip that for now and come back to it later.
   1127 
   1128           // now put the username in the proxy.
   1129           if (1 <= localList->attr->length) {
   1130             proxy->username.append(
   1131                 static_cast<const char*>(localList->attr->data),
   1132                 localList->attr->length);
   1133             LOG(LS_INFO) << "username is " << proxy->username;
   1134           } else {
   1135             LOG(LS_ERROR) << "got keychain entry with no username";
   1136             result = false;
   1137           }
   1138         } else {
   1139           LOG(LS_ERROR) << "couldn't copy info from keychain.";
   1140           result = false;
   1141         }
   1142         SecKeychainItemFreeAttributesAndData(localList, data);
   1143       } else if (errSecItemNotFound == oss) {
   1144         LOG(LS_INFO) << "...username/password info not found";
   1145       } else {
   1146         // oooh, neither 0 nor itemNotFound.
   1147         LOG(LS_ERROR) << "Couldn't get keychain information, error code" << oss;
   1148         result = false;
   1149       }
   1150     } else if (errSecItemNotFound == oss) {  // noop
   1151     } else {
   1152       // oooh, neither 0 nor itemNotFound.
   1153       LOG(LS_ERROR) << "Couldn't get keychain information, error code" << oss;
   1154       result = false;
   1155     }
   1156   }
   1157 
   1158   return result;
   1159 }
   1160 
   1161 bool GetMacProxySettings(ProxyInfo* proxy) {
   1162   // based on the Apple Technical Q&A QA1234
   1163   // http://developer.apple.com/qa/qa2001/qa1234.html
   1164   CFDictionaryRef proxyDict = SCDynamicStoreCopyProxies(NULL);
   1165   bool result = false;
   1166 
   1167   if (proxyDict != NULL) {
   1168     // sending it off to another function makes it easier to unit test
   1169     // since we can make our own dictionary to hand to that function.
   1170     result = GetMacProxySettingsFromDictionary(proxy, proxyDict);
   1171 
   1172     if (result) {
   1173       result = p_putPasswordInProxyInfo(proxy);
   1174     }
   1175 
   1176     // We created the dictionary with something that had the
   1177     // word 'copy' in it, so we have to release it, according
   1178     // to the Carbon memory management standards.
   1179     CFRelease(proxyDict);
   1180   } else {
   1181     LOG(LS_ERROR) << "SCDynamicStoreCopyProxies failed";
   1182   }
   1183 
   1184   return result;
   1185 }
   1186 #endif  // WEBRTC_MAC && !defined(WEBRTC_IOS)
   1187 
   1188 #ifdef WEBRTC_IOS
   1189 // iOS has only http proxy
   1190 bool GetiOSProxySettings(ProxyInfo* proxy) {
   1191 
   1192   bool result = false;
   1193 
   1194   CFDictionaryRef proxy_dict = CFNetworkCopySystemProxySettings();
   1195   if (!proxy_dict) {
   1196     LOG(LS_ERROR) << "CFNetworkCopySystemProxySettings failed";
   1197     return false;
   1198   }
   1199 
   1200   CFNumberRef proxiesHTTPEnable = (CFNumberRef)CFDictionaryGetValue(
   1201     proxy_dict, kCFNetworkProxiesHTTPEnable);
   1202   if (!p_isCFNumberTrue(proxiesHTTPEnable)) {
   1203     CFRelease(proxy_dict);
   1204     return false;
   1205   }
   1206 
   1207   CFStringRef proxy_address = (CFStringRef)CFDictionaryGetValue(
   1208     proxy_dict, kCFNetworkProxiesHTTPProxy);
   1209   CFNumberRef proxy_port = (CFNumberRef)CFDictionaryGetValue(
   1210     proxy_dict, kCFNetworkProxiesHTTPPort);
   1211 
   1212   // the data we need to construct the SocketAddress for the proxy.
   1213   std::string hostname;
   1214   int port;
   1215   if (p_convertHostCFStringRefToCPPString(proxy_address, hostname) &&
   1216       p_convertCFNumberToInt(proxy_port, &port)) {
   1217       // We have something enabled, with a hostname and a port.
   1218       // That's sufficient to set up the proxy info.
   1219       // Finally, try HTTP proxy. Note that flute doesn't
   1220       // differentiate between HTTPS and HTTP, hence we are using the
   1221       // same flute type here, ie. PROXY_HTTPS.
   1222       proxy->type = PROXY_HTTPS;
   1223 
   1224       proxy->address.SetIP(hostname);
   1225       proxy->address.SetPort(port);
   1226       result = true;
   1227   }
   1228 
   1229   // We created the dictionary with something that had the
   1230   // word 'copy' in it, so we have to release it, according
   1231   // to the Carbon memory management standards.
   1232   CFRelease(proxy_dict);
   1233 
   1234   return result;
   1235 }
   1236 #endif // WEBRTC_IOS
   1237 
   1238 bool AutoDetectProxySettings(const char* agent, const char* url,
   1239                              ProxyInfo* proxy) {
   1240 #if defined(WEBRTC_WIN)
   1241   return WinHttpAutoDetectProxyForUrl(agent, url, proxy);
   1242 #else
   1243   LOG(LS_WARNING) << "Proxy auto-detection not implemented for this platform";
   1244   return false;
   1245 #endif
   1246 }
   1247 
   1248 bool GetSystemDefaultProxySettings(const char* agent, const char* url,
   1249                                    ProxyInfo* proxy) {
   1250 #if defined(WEBRTC_WIN)
   1251   return GetIeProxySettings(agent, url, proxy);
   1252 #elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
   1253   return GetMacProxySettings(proxy);
   1254 #elif defined(WEBRTC_IOS)
   1255   return GetiOSProxySettings(proxy);
   1256 #else
   1257   // TODO: Get System settings if browser is not firefox.
   1258   return GetFirefoxProxySettings(url, proxy);
   1259 #endif
   1260 }
   1261 
   1262 bool GetProxySettingsForUrl(const char* agent, const char* url,
   1263                             ProxyInfo* proxy, bool long_operation) {
   1264   UserAgent a = GetAgent(agent);
   1265   bool result;
   1266   switch (a) {
   1267     case UA_FIREFOX: {
   1268       result = GetFirefoxProxySettings(url, proxy);
   1269       break;
   1270     }
   1271 #if defined(WEBRTC_WIN)
   1272     case UA_INTERNETEXPLORER:
   1273       result = GetIeProxySettings(agent, url, proxy);
   1274       break;
   1275     case UA_UNKNOWN:
   1276       // Agent not defined, check default browser.
   1277       if (IsDefaultBrowserFirefox()) {
   1278         result = GetFirefoxProxySettings(url, proxy);
   1279       } else {
   1280         result = GetIeProxySettings(agent, url, proxy);
   1281       }
   1282       break;
   1283 #endif  // WEBRTC_WIN
   1284     default:
   1285       result = GetSystemDefaultProxySettings(agent, url, proxy);
   1286       break;
   1287   }
   1288 
   1289   // TODO: Consider using the 'long_operation' parameter to
   1290   // decide whether to do the auto detection.
   1291   if (result && (proxy->autodetect ||
   1292                  !proxy->autoconfig_url.empty())) {
   1293     // Use WinHTTP to auto detect proxy for us.
   1294     result = AutoDetectProxySettings(agent, url, proxy);
   1295     if (!result) {
   1296       // Either auto detection is not supported or we simply didn't
   1297       // find any proxy, reset type.
   1298       proxy->type = rtc::PROXY_NONE;
   1299     }
   1300   }
   1301   return result;
   1302 }
   1303 
   1304 }  // namespace rtc
   1305