Upstream version 9.37.197.0
[platform/framework/web/crosswalk.git] / src / third_party / libjingle / source / talk / base / proxydetect.cc
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   DWORD size, type;
642   bool success = false;
643   result = RegQueryValueEx(key, L"", 0, &type, NULL, &size);
644   if (result == ERROR_SUCCESS && type == REG_SZ) {
645     wchar_t* value = new wchar_t[size+1];
646     BYTE* buffer = reinterpret_cast<BYTE*>(value);
647     result = RegQueryValueEx(key, L"", 0, &type, buffer, &size);
648     if (result == ERROR_SUCCESS) {
649       // Size returned by RegQueryValueEx is in bytes, convert to number of
650       // wchar_t's.
651       size /= sizeof(value[0]);
652       value[size] = L'\0';
653       for (size_t i = 0; i < size; ++i) {
654         value[i] = tolowercase(value[i]);
655       }
656       success = (NULL != strstr(value, L"firefox.exe"));
657     }
658     delete[] value;
659   }
660
661   RegCloseKey(key);
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