1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#if !defined(OS_CHROMEOS)
6
7#include "chrome/browser/ui/webui/options/advanced_options_utils.h"
8
9#include "base/environment.h"
10#include "base/file_util.h"
11#include "base/nix/xdg_util.h"
12#include "base/process_util.h"
13#include "base/string_tokenizer.h"
14#include "chrome/browser/ui/browser_list.h"
15#include "content/browser/browser_thread.h"
16#include "content/browser/tab_contents/tab_contents.h"
17#include "content/common/process_watcher.h"
18#include "ui/base/gtk/gtk_signal.h"
19
20// Command used to configure GNOME proxy settings. The command was renamed
21// in January 2009, so both are used to work on both old and new systems.
22const char* kOldGNOMEProxyConfigCommand[] = {"gnome-network-preferences", NULL};
23const char* kGNOMEProxyConfigCommand[] = {"gnome-network-properties", NULL};
24// KDE3 and KDE4 are only slightly different, but incompatible. Go figure.
25const char* kKDE3ProxyConfigCommand[] = {"kcmshell", "proxy", NULL};
26const char* kKDE4ProxyConfigCommand[] = {"kcmshell4", "proxy", NULL};
27
28// The URL for Linux proxy configuration help when not running under a
29// supported desktop environment.
30const char kLinuxProxyConfigUrl[] = "about:linux-proxy-config";
31
32struct ProxyConfigCommand {
33  std::string binary;
34  const char** argv;
35};
36
37namespace {
38
39// Search $PATH to find one of the commands. Store the full path to
40// it in the |binary| field and the command array index in in |index|.
41bool SearchPATH(ProxyConfigCommand* commands, size_t ncommands, size_t* index) {
42  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
43  const char* path = getenv("PATH");
44  if (!path)
45    return false;
46  FilePath bin_path;
47  CStringTokenizer tk(path, path + strlen(path), ":");
48  // Search $PATH looking for the commands in order.
49  while (tk.GetNext()) {
50    for (size_t i = 0; i < ncommands; i++) {
51      bin_path = FilePath(tk.token()).Append(commands[i].argv[0]);
52      if (file_util::PathExists(bin_path)) {
53        commands[i].binary = bin_path.value();
54        if (index)
55          *index = i;
56        return true;
57      }
58    }
59  }
60  // Did not find any of the binaries in $PATH.
61  return false;
62}
63
64// Show the proxy config URL in the given tab.
65void ShowLinuxProxyConfigUrl(TabContents* tab_contents) {
66  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
67  scoped_ptr<base::Environment> env(base::Environment::Create());
68  const char* name = base::nix::GetDesktopEnvironmentName(env.get());
69  if (name)
70    LOG(ERROR) << "Could not find " << name << " network settings in $PATH";
71  tab_contents->OpenURL(GURL(kLinuxProxyConfigUrl), GURL(),
72                        NEW_FOREGROUND_TAB, PageTransition::LINK);
73}
74
75// Start the given proxy configuration utility.
76void StartProxyConfigUtil(TabContents* tab_contents,
77                          const ProxyConfigCommand& command) {
78  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
79  std::vector<std::string> argv;
80  argv.push_back(command.binary);
81  for (size_t i = 1; command.argv[i]; i++)
82    argv.push_back(command.argv[i]);
83  base::file_handle_mapping_vector no_files;
84  base::ProcessHandle handle;
85  if (!base::LaunchApp(argv, no_files, false, &handle)) {
86    LOG(ERROR) << "StartProxyConfigUtil failed to start " << command.binary;
87    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
88        NewRunnableFunction(&ShowLinuxProxyConfigUrl, tab_contents));
89    return;
90  }
91  ProcessWatcher::EnsureProcessGetsReaped(handle);
92}
93
94// Detect, and if possible, start the appropriate proxy config utility. On
95// failure to do so, show the Linux proxy config URL in a new tab instead.
96void DetectAndStartProxyConfigUtil(TabContents* tab_contents) {
97  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
98  scoped_ptr<base::Environment> env(base::Environment::Create());
99
100  ProxyConfigCommand command;
101  bool found_command = false;
102  switch (base::nix::GetDesktopEnvironment(env.get())) {
103    case base::nix::DESKTOP_ENVIRONMENT_GNOME: {
104      size_t index;
105      ProxyConfigCommand commands[2];
106      commands[0].argv = kGNOMEProxyConfigCommand;
107      commands[1].argv = kOldGNOMEProxyConfigCommand;
108      found_command = SearchPATH(commands, 2, &index);
109      if (found_command)
110        command = commands[index];
111      break;
112    }
113
114    case base::nix::DESKTOP_ENVIRONMENT_KDE3:
115      command.argv = kKDE3ProxyConfigCommand;
116      found_command = SearchPATH(&command, 1, NULL);
117      break;
118
119    case base::nix::DESKTOP_ENVIRONMENT_KDE4:
120      command.argv = kKDE4ProxyConfigCommand;
121      found_command = SearchPATH(&command, 1, NULL);
122      break;
123
124    case base::nix::DESKTOP_ENVIRONMENT_XFCE:
125    case base::nix::DESKTOP_ENVIRONMENT_OTHER:
126      break;
127  }
128
129  if (found_command) {
130    StartProxyConfigUtil(tab_contents, command);
131  } else {
132    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
133        NewRunnableFunction(&ShowLinuxProxyConfigUrl, tab_contents));
134  }
135}
136
137}  // anonymous namespace
138
139void AdvancedOptionsUtilities::ShowNetworkProxySettings(
140    TabContents* tab_contents) {
141  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
142      NewRunnableFunction(&DetectAndStartProxyConfigUtil, tab_contents));
143}
144
145#endif  // !defined(OS_CHROMEOS)
146