1 // Copyright (c) 2012 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.
5 #include "chrome/installer/util/google_update_util.h"
12 #include "base/command_line.h"
13 #include "base/environment.h"
14 #include "base/files/file_path.h"
15 #include "base/logging.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/process/kill.h"
18 #include "base/process/launch.h"
19 #include "base/strings/string16.h"
20 #include "base/strings/string_split.h"
21 #include "base/time/time.h"
22 #include "base/win/registry.h"
23 #include "base/win/scoped_handle.h"
24 #include "chrome/installer/launcher_support/chrome_launcher_support.h"
25 #include "chrome/installer/util/google_update_constants.h"
26 #include "chrome/installer/util/google_update_settings.h"
28 using base::win::RegKey
;
30 namespace google_update
{
34 const int kGoogleUpdateTimeoutMs
= 20 * 1000;
36 const char kEnvVariableUntrustedData
[] = "GoogleUpdateUntrustedData";
37 const int kEnvVariableUntrustedDataMaxLength
= 4096;
39 // Returns true if Google Update is present at the given level.
40 bool IsGoogleUpdatePresent(bool system_install
) {
41 // Using the existence of version key in the registry to decide.
42 return GoogleUpdateSettings::GetGoogleUpdateVersion(system_install
).IsValid();
45 // Returns GoogleUpdateSetup.exe's executable path at specified level.
46 // or an empty path if none is found.
47 base::FilePath
GetGoogleUpdateSetupExe(bool system_install
) {
48 const HKEY root_key
= system_install
? HKEY_LOCAL_MACHINE
: HKEY_CURRENT_USER
;
51 if (update_key
.Open(root_key
, kRegPathGoogleUpdate
, KEY_QUERY_VALUE
) ==
55 if ((update_key
.ReadValue(kRegPathField
, &path_str
) == ERROR_SUCCESS
) &&
56 (update_key
.ReadValue(kRegGoogleUpdateVersion
, &version_str
) ==
58 return base::FilePath(path_str
).DirName().Append(version_str
).
59 Append(kGoogleUpdateSetupExe
);
62 return base::FilePath();
65 // If Google Update is present at system-level, sets |cmd_string| to the command
66 // line to install Google Update at user-level and returns true.
67 // Otherwise, clears |cmd_string| and returns false.
68 bool GetUserLevelGoogleUpdateInstallCommandLine(string16
* cmd_string
) {
70 base::FilePath
google_update_setup(
71 GetGoogleUpdateSetupExe(true)); // system-level.
72 if (!google_update_setup
.empty()) {
73 CommandLine
cmd(google_update_setup
);
74 // Appends "/install runtime=true&needsadmin=false /silent /nomitag".
75 // NB: /nomitag needs to be at the end.
76 // Constants are found in code.google.com/p/omaha/common/const_cmd_line.h.
77 cmd
.AppendArg("/install");
78 // The "&" can be used in base::LaunchProcess() without quotation
79 // (this is problematic only if run from command prompt).
80 cmd
.AppendArg("runtime=true&needsadmin=false");
81 cmd
.AppendArg("/silent");
82 cmd
.AppendArg("/nomitag");
83 *cmd_string
= cmd
.GetCommandLineString();
85 return !cmd_string
->empty();
88 // Launches command |cmd_string|, and waits for |timeout| milliseconds before
89 // timing out. To wait indefinitely, one can set
90 // |timeout| to be base::TimeDelta::FromMilliseconds(INFINITE).
91 // Returns true if this executes successfully.
92 // Returns false if command execution fails to execute, or times out.
93 bool LaunchProcessAndWaitWithTimeout(const string16
& cmd_string
,
94 base::TimeDelta timeout
) {
96 base::win::ScopedHandle process
;
98 LOG(INFO
) << "Launching: " << cmd_string
;
99 if (!base::LaunchProcess(cmd_string
, base::LaunchOptions(),
100 process
.Receive())) {
101 PLOG(ERROR
) << "Failed to launch (" << cmd_string
<< ")";
102 } else if (!base::WaitForExitCodeWithTimeout(process
, &exit_code
, timeout
)) {
103 // The GetExitCodeProcess failed or timed-out.
104 LOG(ERROR
) <<"Command (" << cmd_string
<< ") is taking more than "
105 << timeout
.InMilliseconds() << " milliseconds to complete.";
106 } else if (exit_code
!= 0) {
107 LOG(ERROR
) << "Command (" << cmd_string
<< ") exited with code "
115 bool IsNotPrintable(unsigned char c
) {
116 return c
< 32 || c
>= 127;
119 // Returns whether or not |s| consists of printable characters.
120 bool IsStringPrintable(const std::string
& s
) {
121 return std::find_if(s
.begin(), s
.end(), IsNotPrintable
) == s
.end();
124 bool IsIllegalUntrustedDataKeyChar(unsigned char c
) {
125 return !(c
>= 'A' && c
<= 'Z' || c
>= 'a' && c
<= 'z' ||
126 c
>= '0' && c
<= '9' || c
== '-' || c
== '_' || c
== '$');
129 // Returns true if |key| from untrusted data is valid.
130 bool IsUntrustedDataKeyValid(const std::string
& key
) {
131 return std::find_if(key
.begin(), key
.end(), IsIllegalUntrustedDataKeyChar
)
135 // Reads and parses untrusted data passed from Google Update as key-value
136 // pairs, then overwrites |untrusted_data_map| with the result.
137 // Returns true if data are successfully read.
138 bool GetGoogleUpdateUntrustedData(
139 std::map
<std::string
, std::string
>* untrusted_data
) {
140 DCHECK(untrusted_data
);
141 scoped_ptr
<base::Environment
> env(base::Environment::Create());
142 std::string data_string
;
143 if (env
== NULL
|| !env
->GetVar(kEnvVariableUntrustedData
, &data_string
))
146 if (data_string
.length() > kEnvVariableUntrustedDataMaxLength
||
147 !IsStringPrintable(data_string
)) {
148 LOG(ERROR
) << "Invalid value in " << kEnvVariableUntrustedData
;
152 VLOG(1) << kEnvVariableUntrustedData
<< ": " << data_string
;
154 std::vector
<std::pair
<std::string
, std::string
> > kv_pairs
;
155 if (!base::SplitStringIntoKeyValuePairs(data_string
, '=', '&', &kv_pairs
)) {
156 LOG(ERROR
) << "Failed to parse untrusted data: " << data_string
;
160 untrusted_data
->clear();
161 std::vector
<std::pair
<std::string
, std::string
> >::const_iterator it
;
162 for (it
= kv_pairs
.begin(); it
!= kv_pairs
.end(); ++it
) {
163 const std::string
& key(it
->first
);
164 // TODO(huangs): URL unescape |value|.
165 const std::string
& value(it
->second
);
166 if (IsUntrustedDataKeyValid(key
) && IsStringPrintable(value
))
167 (*untrusted_data
)[key
] = value
;
169 LOG(ERROR
) << "Illegal character found in untrusted data.";
176 bool EnsureUserLevelGoogleUpdatePresent() {
177 LOG(INFO
) << "Ensuring Google Update is present at user-level.";
179 bool success
= false;
180 if (IsGoogleUpdatePresent(false)) {
184 if (!GetUserLevelGoogleUpdateInstallCommandLine(&cmd_string
)) {
185 LOG(ERROR
) << "Cannot find Google Update at system-level.";
186 // Ideally we should return false. However, this case should not be
187 // encountered by regular users, and developers (who often installs
188 // Chrome without Google Update) may be unduly impeded by this case.
189 // Therefore we return true.
192 success
= LaunchProcessAndWaitWithTimeout(cmd_string
,
193 base::TimeDelta::FromMilliseconds(INFINITE
));
199 bool UninstallGoogleUpdate(bool system_install
) {
200 bool success
= false;
202 GoogleUpdateSettings::GetUninstallCommandLine(system_install
));
203 if (cmd_string
.empty()) {
204 success
= true; // Nothing to; vacuous success.
206 success
= LaunchProcessAndWaitWithTimeout(cmd_string
,
207 base::TimeDelta::FromMilliseconds(kGoogleUpdateTimeoutMs
));
212 std::string
GetUntrustedDataValue(const std::string
& key
) {
213 std::map
<std::string
, std::string
> untrusted_data
;
214 if (GetGoogleUpdateUntrustedData(&untrusted_data
)) {
215 std::map
<std::string
, std::string
>::const_iterator
data_it(
216 untrusted_data
.find(key
));
217 if (data_it
!= untrusted_data
.end())
218 return data_it
->second
;
221 return std::string();
224 } // namespace google_update