1 /* $Id: proxyguess.cc,v 1.14 2005/07/02 17:21:35 atterer Exp $ -*- C++ -*-
3 |_) /| Copyright (C) 2003 | richard@
4 | \/¯| Richard Atterer | atterer.net
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License, version 2. See
8 the file COPYING for details.
10 Find proxy URLs by reading config files of various browsers.
12 Warning, this code probably tries to be too clever: It compares the "last
13 modified" timestamps of the various config files and chooses the
14 configuration with the most recent timestamp.
18 // This is what libcurl says about env vars (url.c:2326):
19 /* If proxy was not specified, we check for default proxy environment
20 * variables, to enable i.e Lynx compliance:
22 * http_proxy=http://some.server.dom:port/
23 * https_proxy=http://some.server.dom:port/
24 * ftp_proxy=http://some.server.dom:port/
25 * gopher_proxy=http://some.server.dom:port/
26 * no_proxy=domain1.dom,host.domain2.dom
27 * (a comma-separated list of hosts which should
28 * not be proxied, or an asterisk to override
29 * all proxy variables)
30 * all_proxy=http://some.server.dom:port/
31 * (seems to exist for the CERN www lib. Probably
32 * the first to check for.)
34 * For compatibility, the all-uppercase versions of these variables are
35 * checked if the lowercase versions don't exist.
44 #include <sys/types.h>
46 #include <unistd-jigdo.h>
51 #include <proxyguess.hh>
52 //______________________________________________________________________
54 DEBUG_UNIT("proxyguess")
56 #ifndef TESTING_PROXYGUESS
57 #warning TODO glibcurl_add_proxy
58 void glibcurl_add_proxy(const char*, const char*) { }
59 void glibcurl_add_noproxy(const char*) { }
66 /* Windows: Read Internet Explorer's proxy settings. Doesn't work with .pac
67 files, just with user-supplied servers. */
70 void proxyGuess_MSIE(HKEY internetSettings) {
71 const unsigned BUFLEN = 256;
76 if (RegQueryValueEx(internetSettings, "ProxyEnable", NULL, &type,
77 buf, &len) == ERROR_SUCCESS
78 && type == REG_BINARY && buf[0] == 0) {
79 // User deselected the option "Use a proxy server", so don't continue
80 debug("No proxies set up");
85 // List of servers not to use the proxy for
86 if (RegQueryValueEx(internetSettings, "ProxyOverride", NULL, &type,
87 buf, &len) == ERROR_SUCCESS
88 && type == REG_SZ && buf[0] != 0) {
89 // String has the form "lan;<local>". Split at ; and ignore <local>
91 const char* list = reinterpret_cast<const char*>(buf);
92 while (*list != '\0') {
93 if (!(isalnum(*list) || *list == '.' || *list == '-')) {
97 while (isalnum(*list) || *list == '.' || *list == '-') {
101 if (host != "local") {
102 debug("No proxy for %1", host);
103 glibcurl_add_noproxy(host.c_str());
110 if (RegQueryValueEx(internetSettings, "ProxyServer", NULL, &type,
111 buf, &len) == ERROR_SUCCESS
112 && type == REG_SZ && len >= 2 && buf[0] != 0) {
113 /* String has one of two formats. Either simple format, one proxy for
114 all: "ox:8080", or per-protocol format:
115 "ftp=ox:8081;gopher=ox:8080;http=ox:8080;https=ox:8080" */
117 const char* list = reinterpret_cast<const char*>(buf);
119 while (*list != '\0' && *list != ';') {
123 string::size_type equals = entry.find('=');
124 if (equals == string::npos) {
125 // Simple proxy setting, assume it's both for HTTP and FTP
126 string proxy = "http://";
127 proxy.append(reinterpret_cast<const char*>(buf));
128 debug("General proxy: %1", proxy);
129 glibcurl_add_proxy("http", proxy.c_str());
130 glibcurl_add_proxy("ftp", proxy.c_str());
132 // Per-protocol proxy settings
133 string proto(entry, 0, equals);
134 if (proto == "http" || proto == "ftp") {
135 string proxy = "http://";
136 proxy.append(entry, equals + 1, string::npos);
137 debug("%1 proxy: %2", proto, proxy);
138 glibcurl_add_proxy(proto.c_str(), proxy.c_str());
142 if (*list == '\0') break;
145 } // endif (RegQueryValueEx("ProxyServer") == ERROR_SUCCESS)
152 if (RegOpenKeyEx(HKEY_CURRENT_USER, "Software", 0, KEY_READ, &software)
155 if (RegOpenKeyEx(software, "Microsoft", 0, KEY_READ, µsoft)
158 if (RegOpenKeyEx(microsoft, "Windows", 0, KEY_READ, &windows)
161 if (RegOpenKeyEx(windows, "CurrentVersion", 0, KEY_READ,
162 ¤tVersion) == ERROR_SUCCESS) {
163 HKEY internetSettings;
164 if (RegOpenKeyEx(currentVersion, "Internet Settings", 0,
165 KEY_READ, &internetSettings) == ERROR_SUCCESS) {
166 proxyGuess_MSIE(internetSettings);
167 RegCloseKey(internetSettings);
169 RegCloseKey(currentVersion);
171 RegCloseKey(windows);
173 RegCloseKey(microsoft);
175 RegCloseKey(software);
178 //______________________________________________________________________
182 // Proxy guess code for Unix
186 /** Local struct: Info about a browser configuration file */
187 struct BrowserConfig {
188 /* Init filename and timestamp */
189 BrowserConfig(const string& name, time_t timestamp);
190 void init(time_t timestamp);
191 virtual ~BrowserConfig();
192 /* Reads config file, returns true if proxies could be set. */
193 virtual bool read() = 0;
195 /* In order for things to work with time_t wraparound, record one recent
196 timestamp "now" and always work with the "age" of files in seconds. */
199 signed age; // Allow future dates, e.g. happens with NFS + clock skew
202 BrowserConfig::BrowserConfig(const string& name, time_t timestamp) {
203 if (now == 0) time(&now);
205 age = now - timestamp;
208 BrowserConfig::~BrowserConfig() { }
210 time_t BrowserConfig::now = 0;
212 // Compare pointers to BrowserConfigs by comparing the objects' age
213 struct ConfFilesCompare {
214 bool operator()(const BrowserConfig* a, const BrowserConfig* b) {
215 return a->age < b->age;
220 //______________________________________________________________________
224 # ifndef TESTING_PROXYGUESS
225 // Return the last modification date of the file in question, 0 on error
226 inline time_t fileModTime(const char* path) {
227 struct stat fileInfo;
228 if (stat(path, &fileInfo) != 0) return 0;
229 return fileInfo.st_mtime;
231 typedef ifstream MyIfstream;
234 // Add file info to set of candidate config files
235 template<class Browser>
236 void add(set<BrowserConfig*, ConfFilesCompare>* c, const char* name) {
237 if (name == 0) return;
238 time_t timestamp = fileModTime(name);
239 if (timestamp == 0) return;
240 c->insert(new Browser(string(name), timestamp));
243 template<class Browser>
244 void add(set<BrowserConfig*, ConfFilesCompare>* c, const string& name) {
245 time_t timestamp = fileModTime(name.c_str());
246 if (timestamp == 0) return;
247 c->insert(new Browser(name, timestamp));
250 // E.g. protocol == "http", proxy == "http://localhost:8080/"
251 // NB proxy must start with a scheme like "http:"
252 inline bool addProxy(const char* protocol, const char* proxy) {
253 while (*proxy == ' ' || *proxy == '\t' || *proxy == '=') ++proxy;
254 if (*proxy == '#') return false; // Assuming comment, not setting proxy
255 if (*proxy == '\0') return false;
256 debug("%1 proxy: %2", protocol, proxy);
257 glibcurl_add_proxy(protocol, proxy);
261 void addNoProxy(const char* list) {
263 while (*list != '\0') {
264 if (*list == '#') return; // Assuming start of comment
265 if (!(isalnum(*list) || *list == '.' || *list == '-')) {
269 while (isalnum(*list) || *list == '.' || *list == '-') {
273 debug("No proxy for %1", host);
274 glibcurl_add_noproxy(host.c_str());
280 //______________________________________________________________________
284 struct Lynx : BrowserConfig {
285 Lynx(const string& n, time_t t) : BrowserConfig(n, t) { }
291 static const string whitespace = " \t";
292 MyIfstream s(filename.c_str());
296 string::size_type pos = line.find_first_not_of(whitespace);
297 if (pos == string::npos) continue;
298 const char* l = line.c_str();
299 if (strncmp(l + pos, "http_proxy:", 11) == 0) {
300 if (addProxy("http", l + 11)) result = true;
301 } else if (strncmp(l + pos, "ftp_proxy:", 10) == 0) {
302 if (addProxy("ftp", l + 10)) result = true;
303 } else if (strncmp(l + pos, "no_proxy:", 9) == 0) {
311 //______________________________________________________________________
315 struct Wget : BrowserConfig {
316 Wget(const string& n, time_t t) : BrowserConfig(n, t) { }
321 // Maybe this should react on "use_proxy = off", but it doesn't
323 static const string whitespace = " \t";
324 MyIfstream s(filename.c_str());
328 string::size_type pos = line.find_first_not_of(whitespace);
329 if (pos == string::npos) continue;
330 const char* l = line.c_str();
331 if (strncmp(l + pos, "http_proxy", 10) == 0) {
332 if (addProxy("http", l + 10)) result = true;
333 } else if (strncmp(l + pos, "ftp_proxy", 9) == 0) {
334 if (addProxy("ftp", l + 9)) result = true;
335 } else if (strncmp(l + pos, "no_proxy", 8) == 0) {
343 //______________________________________________________________________
347 struct Mozilla : BrowserConfig {
348 Mozilla(const string& n, time_t t) : BrowserConfig(n, t) { }
350 void setString(string* dest, const char* src);
353 void Mozilla::setString(string* dest, const char* src) {
355 while (*src == '"' || *src == ' ' || *src == '\t' || *src == ',') ++src;
356 while (*src != '\0' && *src != ')' && *src != '"') {
362 // Works for Netscape 4.7, Galeon, Mozilla 5/6
363 bool Mozilla::read() {
364 // Doesn't understand .pac proxy definitions
365 MyIfstream s(filename.c_str());
366 string line, ftphost, ftpport, httphost, httpport, noproxy;
369 const char USERPREF[] = "user_pref(\"network.proxy.";
370 const char PREF[] = "pref(\"network.proxy.";
371 const char* l = line.c_str();
372 if (strncmp(l, USERPREF, sizeof(USERPREF)-1) == 0)
373 l += sizeof(USERPREF)-1;
374 else if (strncmp(l, PREF, sizeof(PREF)-1) == 0)
378 if (strncmp(l, "ftp\"", 4) == 0)
379 setString(&ftphost, l + 4);
380 else if (strncmp(l, "ftp_port\"", 9) == 0)
381 setString(&ftpport, l + 9);
382 else if (strncmp(l, "http\"", 5) == 0)
383 setString(&httphost, l + 5);
384 else if (strncmp(l, "http_port\"", 10) == 0)
385 setString(&httpport, l + 10);
386 else if (strncmp(l, "no_proxies_on\"", 14) == 0)
387 setString(&noproxy, l + 14);
391 if (!httphost.empty() && !httpport.empty()) {
392 httphost.insert(0, "http://");
394 httphost += httpport;
395 if (addProxy("http", httphost.c_str())) result = true;
397 if (!ftphost.empty() && !ftpport.empty()) {
398 ftphost.insert(0, "http://");
401 if (addProxy("ftp", ftphost.c_str())) result = true;
403 if (!noproxy.empty()) addNoProxy(noproxy.c_str());
408 //______________________________________________________________________
412 struct KDE : BrowserConfig {
413 KDE(const string& n, time_t t) : BrowserConfig(n, t) { }
419 // Maybe this should react on "use_proxy = off", but it doesn't
421 static const string whitespace = " \t";
422 MyIfstream s(filename.c_str());
426 string::size_type pos = line.find_first_not_of(whitespace);
427 if (pos == string::npos) continue;
428 const char* l = line.c_str();
429 if (strncmp(l + pos, "httpProxy", 9) == 0) {
430 if (addProxy("http", l + 9)) result = true;
431 } else if (strncmp(l + pos, "ftpProxy", 8) == 0) {
432 if (addProxy("ftp", l + 8)) result = true;
433 } else if (strncmp(l + pos, "NoProxyFor", 10) == 0) {
441 //______________________________________________________________________
444 string home = g_get_home_dir();
445 if (home[home.size() - 1] != DIRSEP) home += DIRSEP;
446 //____________________
448 typedef set<BrowserConfig*, ConfFilesCompare> ConfFiles;
451 add<Lynx>(&c, "/etc/lynx.cfg");
452 add<Lynx>(&c, home + ".lynxrc");
453 add<Lynx>(&c, getenv("LYNX_CFG"));
455 add<Wget>(&c, "/etc/wgetrc");
456 add<Wget>(&c, home + ".wgetrc");
457 add<Wget>(&c, getenv("WGETRC"));
459 add<Mozilla>(&c, "/etc/netscape4/defaults/preferences.js");
460 add<Mozilla>(&c, "/etc/mozilla/prefs.js");
461 add<Mozilla>(&c, home + ".netscape/preferences.js");
462 add<Mozilla>(&c, home + ".galeon/mozilla/galeon/prefs.js");
463 add<Mozilla>(&c, home + ".mozilla/default/prefs.js");
464 add<Mozilla>(&c, home + ".netscape6/default/prefs.js"); // Is this correct?
466 add<KDE>(&c, home + ".kde/share/config/kioslaverc");
468 bool finished = false;
469 while (!finished && !c.empty()) {
470 ConfFiles::iterator first = c.begin();
471 ConfFiles::iterator second = first;
473 BrowserConfig* bc = *first;
474 debug("Scan: %1 %2", bc->age, bc->filename);
475 finished = bc->read();
477 c.erase(first, second);
479 // Most recent browser config was found, just delete rest
481 ConfFiles::iterator first = c.begin();
482 ConfFiles::iterator second = first;
484 BrowserConfig* bc = *first;
485 debug("Ignr: %1 %2", bc->age, bc->filename);
487 c.erase(first, second);