blob: 7a960c932cb386c052eb87b538ee4d89e1cb5661 [file] [log] [blame]
license.botbf09a502008-08-24 00:55:551// Copyright (c) 2006-2008 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.
initial.commit09911bf2008-07-26 23:55:294
5#include "chrome/browser/external_protocol_handler.h"
6
[email protected]b347bcb2009-06-16 17:25:477#include "build/build_config.h"
8
initial.commit09911bf2008-07-26 23:55:299#include <set>
10
11#include "base/logging.h"
12#include "base/message_loop.h"
[email protected]319d9e6f2009-02-18 19:47:2113#include "base/string_util.h"
[email protected]505323e22009-01-24 02:47:5814#include "base/thread.h"
initial.commit09911bf2008-07-26 23:55:2915#include "chrome/browser/browser.h"
16#include "chrome/browser/browser_process_impl.h"
[email protected]59b2e322009-09-01 22:32:2617#include "chrome/common/platform_util.h"
initial.commit09911bf2008-07-26 23:55:2918#include "chrome/common/pref_service.h"
19#include "chrome/common/pref_names.h"
[email protected]46072d42008-07-28 14:49:3520#include "googleurl/src/gurl.h"
initial.commit09911bf2008-07-26 23:55:2921#include "net/base/escape.h"
22
[email protected]e7eaedde2009-09-25 20:09:4923// Whether we accept requests for launching external protocols. This is set to
24// false every time an external protocol is requested, and set back to true on
25// each user gesture. This variable should only be accessed from the UI thread.
26static bool g_accept_requests = true;
27
initial.commit09911bf2008-07-26 23:55:2928// static
29void ExternalProtocolHandler::PrepopulateDictionary(DictionaryValue* win_pref) {
30 static bool is_warm = false;
31 if (is_warm)
32 return;
33 is_warm = true;
34
35 static const wchar_t* const denied_schemes[] = {
36 L"afp",
37 L"data",
38 L"disk",
39 L"disks",
40 // ShellExecuting file:///C:/WINDOWS/system32/notepad.exe will simply
41 // execute the file specified! Hopefully we won't see any "file" schemes
42 // because we think of file:// URLs as handled URLs, but better to be safe
43 // than to let an attacker format the user's hard drive.
44 L"file",
45 L"hcp",
46 L"javascript",
47 L"ms-help",
48 L"nntp",
49 L"shell",
50 L"vbscript",
51 // view-source is a special case in chrome. When it comes through an
52 // iframe or a redirect, it looks like an external protocol, but we don't
53 // want to shellexecute it.
54 L"view-source",
55 L"vnd.ms.radio",
56 };
57
58 static const wchar_t* const allowed_schemes[] = {
59 L"mailto",
60 L"news",
61 L"snews",
62 };
63
64 bool should_block;
[email protected]057a9a92009-03-16 14:36:4165 for (size_t i = 0; i < arraysize(denied_schemes); ++i) {
initial.commit09911bf2008-07-26 23:55:2966 if (!win_pref->GetBoolean(denied_schemes[i], &should_block)) {
67 win_pref->SetBoolean(denied_schemes[i], true);
68 }
69 }
70
[email protected]057a9a92009-03-16 14:36:4171 for (size_t i = 0; i < arraysize(allowed_schemes); ++i) {
initial.commit09911bf2008-07-26 23:55:2972 if (!win_pref->GetBoolean(allowed_schemes[i], &should_block)) {
73 win_pref->SetBoolean(allowed_schemes[i], false);
74 }
75 }
76}
77
78// static
79ExternalProtocolHandler::BlockState ExternalProtocolHandler::GetBlockState(
80 const std::wstring& scheme) {
[email protected]e7eaedde2009-09-25 20:09:4981 // If we are being carpet bombed, block the request.
82 if (!g_accept_requests)
83 return BLOCK;
84
initial.commit09911bf2008-07-26 23:55:2985 if (scheme.length() == 1) {
86 // We have a URL that looks something like:
87 // C:/WINDOWS/system32/notepad.exe
88 // ShellExecuting this URL will cause the specified program to be executed.
89 return BLOCK;
90 }
91
92 // Check the stored prefs.
[email protected]98299482009-10-06 19:33:0793 // TODO(pkasting): http://b/1119651 This kind of thing should go in the
initial.commit09911bf2008-07-26 23:55:2994 // preferences on the profile, not in the local state.
95 PrefService* pref = g_browser_process->local_state();
96 if (pref) { // May be NULL during testing.
97 DictionaryValue* win_pref =
98 pref->GetMutableDictionary(prefs::kExcludedSchemes);
99 CHECK(win_pref);
100
101 // Warm up the dictionary if needed.
102 PrepopulateDictionary(win_pref);
103
104 bool should_block;
105 if (win_pref->GetBoolean(scheme, &should_block))
106 return should_block ? BLOCK : DONT_BLOCK;
107 }
108
109 return UNKNOWN;
110}
111
112// static
[email protected]98299482009-10-06 19:33:07113void ExternalProtocolHandler::SetBlockState(const std::wstring& scheme,
114 BlockState state) {
115 // Set in the stored prefs.
116 // TODO(pkasting): http://b/1119651 This kind of thing should go in the
117 // preferences on the profile, not in the local state.
118 PrefService* pref = g_browser_process->local_state();
119 if (pref) { // May be NULL during testing.
120 DictionaryValue* win_pref =
121 pref->GetMutableDictionary(prefs::kExcludedSchemes);
122 CHECK(win_pref);
123
124 if (state == UNKNOWN)
125 win_pref->Remove(scheme, NULL);
126 else
127 win_pref->SetBoolean(scheme, state == BLOCK ? true : false);
128 }
129}
130
131// static
initial.commit09911bf2008-07-26 23:55:29132void ExternalProtocolHandler::LaunchUrl(const GURL& url,
133 int render_process_host_id,
134 int tab_contents_id) {
[email protected]e7eaedde2009-09-25 20:09:49135 DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type());
136
initial.commit09911bf2008-07-26 23:55:29137 // Escape the input scheme to be sure that the command does not
138 // have parameters unexpected by the external program.
139 std::string escaped_url_string = EscapeExternalHandlerValue(url.spec());
140 GURL escaped_url(escaped_url_string);
141 BlockState block_state = GetBlockState(ASCIIToWide(escaped_url.scheme()));
142 if (block_state == BLOCK)
143 return;
144
145 if (block_state == UNKNOWN) {
[email protected]98299482009-10-06 19:33:07146#if defined(OS_WIN) || defined(TOOLKIT_GTK) || defined(OS_MACOSX)
[email protected]e7eaedde2009-09-25 20:09:49147 g_accept_requests = false;
initial.commit09911bf2008-07-26 23:55:29148 // Ask the user if they want to allow the protocol. This will call
149 // LaunchUrlWithoutSecurityCheck if the user decides to accept the protocol.
[email protected]10f57b92009-09-03 21:33:21150 RunExternalProtocolDialog(escaped_url,
151 render_process_host_id,
152 tab_contents_id);
153#endif
[email protected]98299482009-10-06 19:33:07154 // For now, allow only whitelisted protocols to fire on Linux/Views.
155 // See http://crbug.com/23853 .
initial.commit09911bf2008-07-26 23:55:29156 return;
157 }
158
[email protected]10f57b92009-09-03 21:33:21159 LaunchUrlWithoutSecurityCheck(escaped_url);
160}
161
162// static
163void ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck(const GURL& url) {
[email protected]59b2e322009-09-01 22:32:26164#if defined(OS_MACOSX)
[email protected]e7eaedde2009-09-25 20:09:49165 // This must run on the UI thread on OS X.
[email protected]10f57b92009-09-03 21:33:21166 platform_util::OpenExternal(url);
[email protected]59b2e322009-09-01 22:32:26167#else
168 // Otherwise put this work on the file thread. On Windows ShellExecute may
169 // block for a significant amount of time, and it shouldn't hurt on Linux.
[email protected]3c2a3d12009-01-16 05:16:56170 MessageLoop* loop = g_browser_process->file_thread()->message_loop();
171 if (loop == NULL) {
initial.commit09911bf2008-07-26 23:55:29172 return;
173 }
174
[email protected]3c2a3d12009-01-16 05:16:56175 loop->PostTask(FROM_HERE,
[email protected]10f57b92009-09-03 21:33:21176 NewRunnableFunction(&platform_util::OpenExternal, url));
[email protected]057a9a92009-03-16 14:36:41177#endif
initial.commit09911bf2008-07-26 23:55:29178}
179
180// static
initial.commit09911bf2008-07-26 23:55:29181void ExternalProtocolHandler::RegisterPrefs(PrefService* prefs) {
182 prefs->RegisterDictionaryPref(prefs::kExcludedSchemes);
183}
[email protected]e7eaedde2009-09-25 20:09:49184
185// static
186void ExternalProtocolHandler::OnUserGesture() {
187 DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type());
188 g_accept_requests = true;
189}