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