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