1 // Copyright 2014 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 #include "chrome/install_static/install_util.h"
6
7 #include <assert.h>
8 #include <stdlib.h>
9 #include <string.h>
10
11 #include <algorithm>
12 #include <iterator>
13 #include <limits>
14 #include <memory>
15 #include <sstream>
16
17 #include "build/branding_buildflags.h"
18 #include "chrome/chrome_elf/nt_registry/nt_registry.h"
19 #include "chrome/install_static/buildflags.h"
20 #include "chrome/install_static/install_details.h"
21 #include "chrome/install_static/install_modes.h"
22 #include "chrome/install_static/policy_path_parser.h"
23 #include "chrome/install_static/user_data_dir.h"
24 #include "components/nacl/common/buildflags.h"
25 #include "components/version_info/channel.h"
26
27 namespace install_static {
28
29 enum class ProcessType {
30 UNINITIALIZED,
31 OTHER_PROCESS,
32 BROWSER_PROCESS,
33 CLOUD_PRINT_SERVICE_PROCESS,
34 #if BUILDFLAG(ENABLE_NACL)
35 NACL_BROKER_PROCESS,
36 NACL_LOADER_PROCESS,
37 #endif
38 CRASHPAD_HANDLER_PROCESS,
39 };
40
41 // Caches the |ProcessType| of the current process.
42 ProcessType g_process_type = ProcessType::UNINITIALIZED;
43
44 const wchar_t kRegValueChromeStatsSample[] = L"UsageStatsInSample";
45
46 // TODO(ananta)
47 // http://crbug.com/604923
48 // The constants defined in this file are also defined in chrome/installer and
49 // other places. we need to unify them.
50 const wchar_t kHeadless[] = L"CHROME_HEADLESS";
51 const wchar_t kShowRestart[] = L"CHROME_CRASHED";
52 const wchar_t kRestartInfo[] = L"CHROME_RESTART";
53 const wchar_t kRtlLocale[] = L"RIGHT_TO_LEFT";
54
55 const wchar_t kCrashpadHandler[] = L"crashpad-handler";
56 const wchar_t kFallbackHandler[] = L"fallback-handler";
57
58 const wchar_t kProcessType[] = L"type";
59 const wchar_t kUserDataDirSwitch[] = L"user-data-dir";
60 const wchar_t kUtilityProcess[] = L"utility";
61
62 namespace {
63
64 #if BUILDFLAG(USE_GOOGLE_UPDATE_INTEGRATION)
65 // TODO(ananta)
66 // http://crbug.com/604923
67 // The constants defined in this file are also defined in chrome/installer and
68 // other places. we need to unify them.
69 // Chrome channel display names.
70 constexpr wchar_t kChromeChannelDev[] = L"dev";
71 constexpr wchar_t kChromeChannelBeta[] = L"beta";
72 constexpr wchar_t kChromeChannelStableExplicit[] = L"stable";
73 #endif
74
75 // TODO(ananta)
76 // http://crbug.com/604923
77 // These constants are defined in the chrome/installer directory as well. We
78 // need to unify them.
79 #if BUILDFLAG(USE_GOOGLE_UPDATE_INTEGRATION)
80 constexpr wchar_t kRegValueAp[] = L"ap";
81 constexpr wchar_t kRegValueName[] = L"name";
82 #endif
83 constexpr wchar_t kRegValueUsageStats[] = L"usagestats";
84 constexpr wchar_t kMetricsReportingEnabled[] = L"MetricsReportingEnabled";
85
86 constexpr wchar_t kCloudPrintServiceProcess[] = L"service";
87 #if BUILDFLAG(ENABLE_NACL)
88 constexpr wchar_t kNaClBrokerProcess[] = L"nacl-broker";
89 constexpr wchar_t kNaClLoaderProcess[] = L"nacl-loader";
90 #endif
91
Trace(const wchar_t * format_string,...)92 void Trace(const wchar_t* format_string, ...) {
93 static const int kMaxLogBufferSize = 1024;
94 static wchar_t buffer[kMaxLogBufferSize] = {};
95
96 va_list args = {};
97
98 va_start(args, format_string);
99 vswprintf(buffer, kMaxLogBufferSize, format_string, args);
100 OutputDebugStringW(buffer);
101 va_end(args);
102 }
103
GetLanguageAndCodePageFromVersionResource(const char * version_resource,WORD * language,WORD * code_page)104 bool GetLanguageAndCodePageFromVersionResource(const char* version_resource,
105 WORD* language,
106 WORD* code_page) {
107 if (!version_resource)
108 return false;
109
110 struct LanguageAndCodePage {
111 WORD language;
112 WORD code_page;
113 };
114
115 LanguageAndCodePage* translation_info = nullptr;
116 uint32_t data_size_in_bytes = 0;
117 BOOL query_result = VerQueryValueW(
118 version_resource, L"\\VarFileInfo\\Translation",
119 reinterpret_cast<void**>(&translation_info), &data_size_in_bytes);
120 if (!query_result)
121 return false;
122
123 *language = translation_info->language;
124 *code_page = translation_info->code_page;
125 return true;
126 }
127
GetValueFromVersionResource(const char * version_resource,const std::wstring & name,std::wstring * value_str)128 bool GetValueFromVersionResource(const char* version_resource,
129 const std::wstring& name,
130 std::wstring* value_str) {
131 assert(value_str);
132 value_str->clear();
133
134 // TODO(ananta)
135 // It may be better in the long run to enumerate the languages and code pages
136 // in the version resource and return the value from the first match.
137 WORD language = 0;
138 WORD code_page = 0;
139 if (!GetLanguageAndCodePageFromVersionResource(version_resource, &language,
140 &code_page)) {
141 return false;
142 }
143
144 const size_t array_size = 8;
145 WORD lang_codepage[array_size] = {};
146 size_t i = 0;
147 // Use the language and codepage
148 lang_codepage[i++] = language;
149 lang_codepage[i++] = code_page;
150 // Use the default language and codepage from the resource.
151 lang_codepage[i++] = ::GetUserDefaultLangID();
152 lang_codepage[i++] = code_page;
153 // Use the language from the resource and Latin codepage (most common).
154 lang_codepage[i++] = language;
155 lang_codepage[i++] = 1252;
156 // Use the default language and Latin codepage (most common).
157 lang_codepage[i++] = ::GetUserDefaultLangID();
158 lang_codepage[i++] = 1252;
159
160 static_assert((array_size % 2) == 0,
161 "Language code page size should be a multiple of 2");
162 assert(array_size == i);
163
164 for (i = 0; i < array_size;) {
165 wchar_t sub_block[MAX_PATH];
166 WORD language = lang_codepage[i++];
167 WORD code_page = lang_codepage[i++];
168 _snwprintf_s(sub_block, MAX_PATH, MAX_PATH,
169 L"\\StringFileInfo\\%04hx%04hx\\%ls", language, code_page,
170 name.c_str());
171 void* value = nullptr;
172 uint32_t size = 0;
173 BOOL r = ::VerQueryValueW(version_resource, sub_block, &value, &size);
174 if (r && value) {
175 value_str->assign(static_cast<wchar_t*>(value));
176 return true;
177 }
178 }
179 return false;
180 }
181
DirectoryExists(const std::wstring & path)182 bool DirectoryExists(const std::wstring& path) {
183 DWORD file_attributes = ::GetFileAttributes(path.c_str());
184 if (file_attributes == INVALID_FILE_ATTRIBUTES)
185 return false;
186 return (file_attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
187 }
188
189 // Returns true if the |source| string matches the |pattern|. The pattern
190 // may contain wildcards like '?' which matches one character or a '*'
191 // which matches 0 or more characters.
192 // Please note that pattern matches the whole string. If you want to find
193 // something in the middle of the string then you need to specify the pattern
194 // as '*xyz*'.
195 // |source_index| is the index of the current character being matched in
196 // |source|.
197 // |pattern_index| is the index of the current pattern character in |pattern|
198 // which is matched with source.
MatchPatternImpl(const std::wstring & source,const std::wstring & pattern,size_t source_index,size_t pattern_index)199 bool MatchPatternImpl(const std::wstring& source,
200 const std::wstring& pattern,
201 size_t source_index,
202 size_t pattern_index) {
203 if (source.empty() && pattern.empty())
204 return true;
205
206 if (source_index > source.length() || pattern_index > pattern.length())
207 return false;
208
209 // If we reached the end of both strings, then we are done.
210 if ((source_index == source.length()) &&
211 (pattern_index == pattern.length())) {
212 return true;
213 }
214
215 // If the current character in the pattern is a '*' then make sure that
216 // characters after the pattern are present in the source string. This
217 // assumes that you won't have two consecutive '*' characters in the pattern.
218 if ((pattern[pattern_index] == L'*') &&
219 (pattern_index + 1 < pattern.length()) &&
220 (source_index >= source.length())) {
221 return false;
222 }
223
224 // If the pattern contains wildcard characters '?' or '.' or there is a match
225 // then move ahead in both strings.
226 if ((pattern[pattern_index] == L'?') ||
227 (pattern[pattern_index] == source[source_index])) {
228 return MatchPatternImpl(source, pattern, source_index + 1,
229 pattern_index + 1);
230 }
231
232 // If we have a '*' then there are two possibilities
233 // 1. We consider current character of source.
234 // 2. We ignore current character of source.
235 if (pattern[pattern_index] == L'*') {
236 return MatchPatternImpl(source, pattern, source_index + 1, pattern_index) ||
237 MatchPatternImpl(source, pattern, source_index, pattern_index + 1);
238 }
239 return false;
240 }
241
242 // Defines the type of whitespace characters typically found in strings.
243 constexpr char kWhiteSpaces[] = " \t\n\r\f\v";
244 constexpr wchar_t kWhiteSpaces16[] = L" \t\n\r\f\v";
245
246 // Define specializations for white spaces based on the type of the string.
247 template <class StringType>
248 StringType GetWhiteSpacesForType();
249 template <>
GetWhiteSpacesForType()250 std::wstring GetWhiteSpacesForType() {
251 return kWhiteSpaces16;
252 }
253 template <>
GetWhiteSpacesForType()254 std::string GetWhiteSpacesForType() {
255 return kWhiteSpaces;
256 }
257
258 // Trim whitespaces from left & right
259 template <class StringType>
TrimT(StringType * str)260 void TrimT(StringType* str) {
261 str->erase(str->find_last_not_of(GetWhiteSpacesForType<StringType>()) + 1);
262 str->erase(0, str->find_first_not_of(GetWhiteSpacesForType<StringType>()));
263 }
264
265 // Tokenizes a string based on a single character delimiter.
266 template <class StringType>
TokenizeStringT(const StringType & str,typename StringType::value_type delimiter,bool trim_spaces)267 std::vector<StringType> TokenizeStringT(
268 const StringType& str,
269 typename StringType::value_type delimiter,
270 bool trim_spaces) {
271 std::vector<StringType> tokens;
272 std::basic_istringstream<typename StringType::value_type> buffer(str);
273 for (StringType token; std::getline(buffer, token, delimiter);) {
274 if (trim_spaces)
275 TrimT<StringType>(&token);
276 tokens.push_back(token);
277 }
278 return tokens;
279 }
280
281 #if BUILDFLAG(USE_GOOGLE_UPDATE_INTEGRATION)
282 // Returns Chrome's update channel name based on the contents of the given "ap"
283 // value from Chrome's ClientState key.
ChannelFromAdditionalParameters(const InstallConstants & mode,const std::wstring & ap_value)284 std::wstring ChannelFromAdditionalParameters(const InstallConstants& mode,
285 const std::wstring& ap_value) {
286 static constexpr wchar_t kChromeChannelBetaPattern[] = L"1?1-*";
287 static constexpr wchar_t kChromeChannelBetaX64Pattern[] = L"*x64-beta*";
288 static constexpr wchar_t kChromeChannelDevPattern[] = L"2?0-d*";
289 static constexpr wchar_t kChromeChannelDevX64Pattern[] = L"*x64-dev*";
290
291 std::wstring value;
292 value.reserve(ap_value.size());
293 std::transform(ap_value.begin(), ap_value.end(), std::back_inserter(value),
294 ::tolower);
295
296 // Empty channel names or those containing "stable" should be reported as
297 // an empty string.
298 if (value.empty() ||
299 (value.find(kChromeChannelStableExplicit) != std::wstring::npos)) {
300 return std::wstring();
301 }
302 if (MatchPattern(value, kChromeChannelDevPattern) ||
303 MatchPattern(value, kChromeChannelDevX64Pattern)) {
304 return kChromeChannelDev;
305 }
306 if (MatchPattern(value, kChromeChannelBetaPattern) ||
307 MatchPattern(value, kChromeChannelBetaX64Pattern)) {
308 return kChromeChannelBeta;
309 }
310 // Else report values with garbage as stable since they will match the stable
311 // rules in the update configs.
312 return std::wstring();
313 }
314
GetChromeChannelNameFromString(const wchar_t * channel_test,std::wstring & channel)315 bool GetChromeChannelNameFromString(const wchar_t* channel_test,
316 std::wstring& channel) {
317 if (!channel_test)
318 return false;
319 if (!*channel_test || !lstrcmpiW(channel_test, kChromeChannelStableExplicit))
320 channel = std::wstring();
321 else if (!lstrcmpiW(channel_test, kChromeChannelBeta))
322 channel = kChromeChannelBeta;
323 else if (!lstrcmpiW(channel_test, kChromeChannelDev))
324 channel = kChromeChannelDev;
325 else
326 return false;
327 return true;
328 }
329
330 #endif // BUILDFLAG(USE_GOOGLE_UPDATE_INTEGRATION)
331
332 // Converts a process type specified as a string to the ProcessType enum.
GetProcessType(const std::wstring & process_type)333 ProcessType GetProcessType(const std::wstring& process_type) {
334 if (process_type.empty())
335 return ProcessType::BROWSER_PROCESS;
336 if (process_type == kCloudPrintServiceProcess)
337 return ProcessType::CLOUD_PRINT_SERVICE_PROCESS;
338 #if BUILDFLAG(ENABLE_NACL)
339 if (process_type == kNaClBrokerProcess)
340 return ProcessType::NACL_BROKER_PROCESS;
341 if (process_type == kNaClLoaderProcess)
342 return ProcessType::NACL_LOADER_PROCESS;
343 #endif
344 if (process_type == kCrashpadHandler)
345 return ProcessType::CRASHPAD_HANDLER_PROCESS;
346 return ProcessType::OTHER_PROCESS;
347 }
348
349 // Returns whether |process_type| needs the profile directory.
ProcessNeedsProfileDir(ProcessType process_type)350 bool ProcessNeedsProfileDir(ProcessType process_type) {
351 // On Windows we don't want subprocesses other than the browser process and
352 // service processes to be able to use the profile directory because if it
353 // lies on a network share the sandbox will prevent us from accessing it.
354 switch (process_type) {
355 case ProcessType::BROWSER_PROCESS:
356 case ProcessType::CLOUD_PRINT_SERVICE_PROCESS:
357 #if BUILDFLAG(ENABLE_NACL)
358 case ProcessType::NACL_BROKER_PROCESS:
359 case ProcessType::NACL_LOADER_PROCESS:
360 #endif
361 return true;
362 case ProcessType::OTHER_PROCESS:
363 return false;
364 case ProcessType::CRASHPAD_HANDLER_PROCESS:
365 return false;
366 case ProcessType::UNINITIALIZED:
367 assert(false);
368 return false;
369 }
370 assert(false);
371 return false;
372 }
373
374 } // namespace
375
IsSystemInstall()376 bool IsSystemInstall() {
377 return InstallDetails::Get().system_level();
378 }
379
GetChromeInstallSubDirectory()380 std::wstring GetChromeInstallSubDirectory() {
381 std::wstring result;
382 AppendChromeInstallSubDirectory(InstallDetails::Get().mode(),
383 true /* include_suffix */, &result);
384 return result;
385 }
386
GetRegistryPath()387 std::wstring GetRegistryPath() {
388 std::wstring result(L"Software\\");
389 AppendChromeInstallSubDirectory(InstallDetails::Get().mode(),
390 true /* include_suffix */, &result);
391 return result;
392 }
393
GetClientsKeyPath()394 std::wstring GetClientsKeyPath() {
395 return GetClientsKeyPath(GetAppGuid());
396 }
397
GetClientStateKeyPath()398 std::wstring GetClientStateKeyPath() {
399 return GetClientStateKeyPath(GetAppGuid());
400 }
401
GetClientStateMediumKeyPath()402 std::wstring GetClientStateMediumKeyPath() {
403 return GetClientStateMediumKeyPath(GetAppGuid());
404 }
405
GetUninstallRegistryPath()406 std::wstring GetUninstallRegistryPath() {
407 std::wstring result(
408 L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\");
409 if (*kCompanyPathName)
410 result.append(kCompanyPathName).append(1, L' ');
411 result.append(kProductPathName, kProductPathNameLength);
412 return result.append(InstallDetails::Get().mode().install_suffix);
413 }
414
GetAppGuid()415 const wchar_t* GetAppGuid() {
416 return InstallDetails::Get().app_guid();
417 }
418
GetToastActivatorClsid()419 const CLSID& GetToastActivatorClsid() {
420 return InstallDetails::Get().toast_activator_clsid();
421 }
422
GetElevatorClsid()423 const CLSID& GetElevatorClsid() {
424 return InstallDetails::Get().elevator_clsid();
425 }
426
GetElevatorIid()427 const CLSID& GetElevatorIid() {
428 return InstallDetails::Get().elevator_iid();
429 }
430
GetElevationServiceName()431 std::wstring GetElevationServiceName() {
432 std::wstring name = GetElevationServiceDisplayName();
433 name.erase(std::remove_if(name.begin(), name.end(), isspace), name.end());
434 return name;
435 }
436
GetElevationServiceDisplayName()437 std::wstring GetElevationServiceDisplayName() {
438 static constexpr wchar_t kElevationServiceDisplayName[] =
439 L" Elevation Service";
440 return GetBaseAppName() + kElevationServiceDisplayName;
441 }
442
GetBaseAppName()443 std::wstring GetBaseAppName() {
444 return InstallDetails::Get().mode().base_app_name;
445 }
446
GetBaseAppId()447 const wchar_t* GetBaseAppId() {
448 return InstallDetails::Get().base_app_id();
449 }
450
GetProgIdPrefix()451 const wchar_t* GetProgIdPrefix() {
452 return InstallDetails::Get().mode().prog_id_prefix;
453 }
454
GetProgIdDescription()455 const wchar_t* GetProgIdDescription() {
456 return InstallDetails::Get().mode().prog_id_description;
457 }
458
GetActiveSetupPath()459 std::wstring GetActiveSetupPath() {
460 return std::wstring(
461 L"Software\\Microsoft\\Active Setup\\Installed Components\\")
462 .append(InstallDetails::Get().mode().active_setup_guid);
463 }
464
GetLegacyCommandExecuteImplClsid()465 std::wstring GetLegacyCommandExecuteImplClsid() {
466 return InstallDetails::Get().mode().legacy_command_execute_clsid;
467 }
468
SupportsSetAsDefaultBrowser()469 bool SupportsSetAsDefaultBrowser() {
470 return InstallDetails::Get().mode().supports_set_as_default_browser;
471 }
472
SupportsRetentionExperiments()473 bool SupportsRetentionExperiments() {
474 return InstallDetails::Get().mode().supports_retention_experiments;
475 }
476
GetIconResourceIndex()477 int GetIconResourceIndex() {
478 return InstallDetails::Get().mode().app_icon_resource_index;
479 }
480
GetSandboxSidPrefix()481 const wchar_t* GetSandboxSidPrefix() {
482 return InstallDetails::Get().mode().sandbox_sid_prefix;
483 }
484
GetSafeBrowsingName()485 std::string GetSafeBrowsingName() {
486 return kSafeBrowsingName;
487 }
488
GetCollectStatsConsent()489 bool GetCollectStatsConsent() {
490 bool enabled = true;
491
492 if (ReportingIsEnforcedByPolicy(&enabled))
493 return enabled;
494
495 const bool system_install = IsSystemInstall();
496
497 DWORD out_value = 0;
498
499 // If system_install, first try ClientStateMedium in HKLM.
500 if (system_install &&
501 nt::QueryRegValueDWORD(
502 nt::HKLM, nt::WOW6432,
503 InstallDetails::Get().GetClientStateMediumKeyPath().c_str(),
504 kRegValueUsageStats, &out_value)) {
505 return (out_value == 1);
506 }
507
508 // Second, try ClientState.
509 return (nt::QueryRegValueDWORD(
510 system_install ? nt::HKLM : nt::HKCU, nt::WOW6432,
511 InstallDetails::Get().GetClientStateKeyPath().c_str(),
512 kRegValueUsageStats, &out_value) &&
513 out_value == 1);
514 }
515
GetCollectStatsInSample()516 bool GetCollectStatsInSample() {
517 std::wstring registry_path = GetRegistryPath();
518
519 DWORD out_value = 0;
520 if (!nt::QueryRegValueDWORD(nt::HKCU, nt::WOW6432, registry_path.c_str(),
521 kRegValueChromeStatsSample, &out_value)) {
522 // If reading the value failed, treat it as though sampling isn't in effect,
523 // implicitly meaning this install is in the sample.
524 return true;
525 }
526 return out_value == 1;
527 }
528
SetCollectStatsInSample(bool in_sample)529 bool SetCollectStatsInSample(bool in_sample) {
530 std::wstring registry_path = GetRegistryPath();
531
532 HANDLE key_handle = INVALID_HANDLE_VALUE;
533 if (!nt::CreateRegKey(nt::HKCU, registry_path.c_str(),
534 KEY_SET_VALUE | KEY_WOW64_32KEY, &key_handle)) {
535 return false;
536 }
537
538 bool success = nt::SetRegValueDWORD(key_handle, kRegValueChromeStatsSample,
539 in_sample ? 1 : 0);
540 nt::CloseRegKey(key_handle);
541 return success;
542 }
543
544 // Appends "[kCompanyPathName\]kProductPathName[install_suffix]" to |path|,
545 // returning a reference to |path|.
AppendChromeInstallSubDirectory(const InstallConstants & mode,bool include_suffix,std::wstring * path)546 std::wstring& AppendChromeInstallSubDirectory(const InstallConstants& mode,
547 bool include_suffix,
548 std::wstring* path) {
549 if (*kCompanyPathName) {
550 path->append(kCompanyPathName);
551 path->push_back(L'\\');
552 }
553 path->append(kProductPathName, kProductPathNameLength);
554 if (!include_suffix)
555 return *path;
556 return path->append(mode.install_suffix);
557 }
558
ReportingIsEnforcedByPolicy(bool * crash_reporting_enabled)559 bool ReportingIsEnforcedByPolicy(bool* crash_reporting_enabled) {
560 std::wstring policies_path = L"SOFTWARE\\Policies\\";
561 AppendChromeInstallSubDirectory(InstallDetails::Get().mode(),
562 false /* !include_suffix */, &policies_path);
563 DWORD value = 0;
564
565 // First, try HKLM.
566 if (nt::QueryRegValueDWORD(nt::HKLM, nt::NONE, policies_path.c_str(),
567 kMetricsReportingEnabled, &value)) {
568 *crash_reporting_enabled = (value != 0);
569 return true;
570 }
571
572 // Second, try HKCU.
573 if (nt::QueryRegValueDWORD(nt::HKCU, nt::NONE, policies_path.c_str(),
574 kMetricsReportingEnabled, &value)) {
575 *crash_reporting_enabled = (value != 0);
576 return true;
577 }
578
579 return false;
580 }
581
InitializeProcessType()582 void InitializeProcessType() {
583 assert(g_process_type == ProcessType::UNINITIALIZED);
584 std::wstring process_type =
585 GetSwitchValueFromCommandLine(::GetCommandLine(), kProcessType);
586 g_process_type = GetProcessType(process_type);
587 }
588
IsProcessTypeInitialized()589 bool IsProcessTypeInitialized() {
590 return g_process_type != ProcessType::UNINITIALIZED;
591 }
592
IsNonBrowserProcess()593 bool IsNonBrowserProcess() {
594 assert(g_process_type != ProcessType::UNINITIALIZED);
595 return g_process_type != ProcessType::BROWSER_PROCESS;
596 }
597
IsCrashpadHandlerProcess()598 bool IsCrashpadHandlerProcess() {
599 assert(g_process_type != ProcessType::UNINITIALIZED);
600 return g_process_type == ProcessType::CRASHPAD_HANDLER_PROCESS;
601 }
602
ProcessNeedsProfileDir(const std::string & process_type)603 bool ProcessNeedsProfileDir(const std::string& process_type) {
604 return ProcessNeedsProfileDir(GetProcessType(UTF8ToUTF16(process_type)));
605 }
606
GetCrashDumpLocation()607 std::wstring GetCrashDumpLocation() {
608 // In order to be able to start crash handling very early and in chrome_elf,
609 // we cannot rely on chrome's PathService entries (for DIR_CRASH_DUMPS) being
610 // available on Windows. See https://crbug.com/564398.
611 std::wstring user_data_dir;
612 bool ret = GetUserDataDirectory(&user_data_dir, nullptr);
613 assert(ret);
614 IgnoreUnused(ret);
615 return user_data_dir.append(L"\\Crashpad");
616 }
617
GetEnvironmentString(const std::string & variable_name)618 std::string GetEnvironmentString(const std::string& variable_name) {
619 return UTF16ToUTF8(
620 GetEnvironmentString16(UTF8ToUTF16(variable_name).c_str()));
621 }
622
GetEnvironmentString16(const wchar_t * variable_name)623 std::wstring GetEnvironmentString16(const wchar_t* variable_name) {
624 DWORD value_length = ::GetEnvironmentVariableW(variable_name, nullptr, 0);
625 if (!value_length)
626 return std::wstring();
627 std::wstring value(value_length, L'\0');
628 value_length =
629 ::GetEnvironmentVariableW(variable_name, &value[0], value_length);
630 if (!value_length || value_length >= value.size())
631 return std::wstring();
632 value.resize(value_length);
633 return value;
634 }
635
SetEnvironmentString(const std::string & variable_name,const std::string & new_value)636 bool SetEnvironmentString(const std::string& variable_name,
637 const std::string& new_value) {
638 return SetEnvironmentString16(UTF8ToUTF16(variable_name),
639 UTF8ToUTF16(new_value));
640 }
641
SetEnvironmentString16(const std::wstring & variable_name,const std::wstring & new_value)642 bool SetEnvironmentString16(const std::wstring& variable_name,
643 const std::wstring& new_value) {
644 return !!SetEnvironmentVariable(variable_name.c_str(), new_value.c_str());
645 }
646
HasEnvironmentVariable(const std::string & variable_name)647 bool HasEnvironmentVariable(const std::string& variable_name) {
648 return HasEnvironmentVariable16(UTF8ToUTF16(variable_name));
649 }
650
HasEnvironmentVariable16(const std::wstring & variable_name)651 bool HasEnvironmentVariable16(const std::wstring& variable_name) {
652 return !!::GetEnvironmentVariable(variable_name.c_str(), nullptr, 0);
653 }
654
GetExecutableVersionDetails(const std::wstring & exe_path,std::wstring * product_name,std::wstring * version,std::wstring * special_build,std::wstring * channel_name)655 void GetExecutableVersionDetails(const std::wstring& exe_path,
656 std::wstring* product_name,
657 std::wstring* version,
658 std::wstring* special_build,
659 std::wstring* channel_name) {
660 assert(product_name);
661 assert(version);
662 assert(special_build);
663 assert(channel_name);
664
665 // Default values in case we don't find a version resource.
666 *product_name = L"Chrome";
667 *version = L"0.0.0.0-devel";
668 special_build->clear();
669
670 DWORD dummy = 0;
671 DWORD length = ::GetFileVersionInfoSize(exe_path.c_str(), &dummy);
672 if (length) {
673 std::unique_ptr<char[]> data(new char[length]);
674 if (::GetFileVersionInfo(exe_path.c_str(), dummy, length, data.get())) {
675 GetValueFromVersionResource(data.get(), L"ProductVersion", version);
676
677 std::wstring official_build;
678 GetValueFromVersionResource(data.get(), L"Official Build",
679 &official_build);
680 if (official_build != L"1")
681 version->append(L"-devel");
682 GetValueFromVersionResource(data.get(), L"ProductShortName",
683 product_name);
684 GetValueFromVersionResource(data.get(), L"SpecialBuild", special_build);
685 }
686 }
687 *channel_name = GetChromeChannelName();
688 }
689
GetChromeChannel()690 version_info::Channel GetChromeChannel() {
691 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
692 std::wstring channel_name(GetChromeChannelName());
693 if (channel_name.empty()) {
694 return version_info::Channel::STABLE;
695 }
696 if (channel_name == L"beta") {
697 return version_info::Channel::BETA;
698 }
699 if (channel_name == L"dev") {
700 return version_info::Channel::DEV;
701 }
702 if (channel_name == L"canary") {
703 return version_info::Channel::CANARY;
704 }
705 #endif
706
707 return version_info::Channel::UNKNOWN;
708 }
709
GetChromeChannelName()710 std::wstring GetChromeChannelName() {
711 return InstallDetails::Get().channel();
712 }
713
MatchPattern(const std::wstring & source,const std::wstring & pattern)714 bool MatchPattern(const std::wstring& source, const std::wstring& pattern) {
715 assert(pattern.find(L"**") == std::wstring::npos);
716 return MatchPatternImpl(source, pattern, 0, 0);
717 }
718
UTF16ToUTF8(const std::wstring & source)719 std::string UTF16ToUTF8(const std::wstring& source) {
720 if (source.empty() ||
721 static_cast<int>(source.size()) > std::numeric_limits<int>::max()) {
722 return std::string();
723 }
724 int size = ::WideCharToMultiByte(CP_UTF8, 0, &source[0],
725 static_cast<int>(source.size()), nullptr, 0,
726 nullptr, nullptr);
727 std::string result(size, '\0');
728 if (::WideCharToMultiByte(CP_UTF8, 0, &source[0],
729 static_cast<int>(source.size()), &result[0], size,
730 nullptr, nullptr) != size) {
731 assert(false);
732 return std::string();
733 }
734 return result;
735 }
736
UTF8ToUTF16(const std::string & source)737 std::wstring UTF8ToUTF16(const std::string& source) {
738 if (source.empty() ||
739 static_cast<int>(source.size()) > std::numeric_limits<int>::max()) {
740 return std::wstring();
741 }
742 int size = ::MultiByteToWideChar(CP_UTF8, 0, &source[0],
743 static_cast<int>(source.size()), nullptr, 0);
744 std::wstring result(size, L'\0');
745 if (::MultiByteToWideChar(CP_UTF8, 0, &source[0],
746 static_cast<int>(source.size()), &result[0],
747 size) != size) {
748 assert(false);
749 return std::wstring();
750 }
751 return result;
752 }
753
TokenizeString(const std::string & str,char delimiter,bool trim_spaces)754 std::vector<std::string> TokenizeString(const std::string& str,
755 char delimiter,
756 bool trim_spaces) {
757 return TokenizeStringT<std::string>(str, delimiter, trim_spaces);
758 }
759
TokenizeString16(const std::wstring & str,wchar_t delimiter,bool trim_spaces)760 std::vector<std::wstring> TokenizeString16(const std::wstring& str,
761 wchar_t delimiter,
762 bool trim_spaces) {
763 return TokenizeStringT<std::wstring>(str, delimiter, trim_spaces);
764 }
765
TokenizeCommandLineToArray(const std::wstring & command_line)766 std::vector<std::wstring> TokenizeCommandLineToArray(
767 const std::wstring& command_line) {
768 // This is baroquely complex to do properly, see e.g.
769 // https://blogs.msdn.microsoft.com/oldnewthing/20100917-00/?p=12833
770 // http://www.windowsinspired.com/how-a-windows-programs-splits-its-command-line-into-individual-arguments/
771 // and many others. We cannot use CommandLineToArgvW() in chrome_elf, because
772 // it's in shell32.dll. Previously, __wgetmainargs() in the CRT was available,
773 // and it's still documented for VS 2015 at
774 // https://msdn.microsoft.com/en-us/library/ff770599.aspx but unfortunately,
775 // isn't actually available.
776 //
777 // This parsing matches CommandLineToArgvW()s for arguments, rather than the
778 // CRTs. These are different only in the most obscure of cases and will not
779 // matter in any practical situation. See the windowsinspired.com post above
780 // for details.
781 //
782 // Indicates whether or not space and tab are interpreted as token separators.
783 enum class SpecialChars {
784 // Space or tab, if encountered, delimit tokens.
785 kInterpret,
786
787 // Space or tab, if encountered, are part of the current token.
788 kIgnore,
789 } state;
790
791 static constexpr wchar_t kSpaceTab[] = L" \t";
792
793 std::vector<std::wstring> result;
794 const wchar_t* p = command_line.c_str();
795
796 // The first argument (the program) is delimited by whitespace or quotes based
797 // on its first character.
798 size_t argv0_length = 0;
799 if (p[0] == L'"') {
800 const wchar_t* closing = wcschr(++p, L'"');
801 if (!closing)
802 argv0_length = command_line.size() - 1; // Skip the opening quote.
803 else
804 argv0_length = closing - (command_line.c_str() + 1);
805 } else {
806 argv0_length = wcscspn(p, kSpaceTab);
807 }
808 result.emplace_back(p, argv0_length);
809 if (p[argv0_length] == 0)
810 return result;
811 p += argv0_length + 1;
812
813 std::wstring token;
814 // This loops the entire string, with a subloop for each argument.
815 for (;;) {
816 // Advance past leading whitespace (only space and tab are handled).
817 p += wcsspn(p, kSpaceTab);
818
819 // End of arguments.
820 if (p[0] == 0)
821 break;
822
823 state = SpecialChars::kInterpret;
824
825 // Scan an argument.
826 for (;;) {
827 // Count and advance past collections of backslashes, which have special
828 // meaning when followed by a double quote.
829 int num_backslashes = wcsspn(p, L"\\");
830 p += num_backslashes;
831
832 if (p[0] == L'"') {
833 // Emit a backslash for each pair of backslashes found. A non-paired
834 // "extra" backslash is handled below.
835 token.append(num_backslashes / 2, L'\\');
836
837 if (num_backslashes % 2 == 1) {
838 // An odd number of backslashes followed by a quote is treated as
839 // pairs of protected backslashes, followed by the protected quote.
840 token += L'"';
841 } else if (p[1] == L'"' && state == SpecialChars::kIgnore) {
842 // Special case for consecutive double quotes within a quoted string:
843 // emit one for the pair, and switch back to interpreting special
844 // characters.
845 ++p;
846 token += L'"';
847 state = SpecialChars::kInterpret;
848 } else {
849 state = state == SpecialChars::kInterpret ? SpecialChars::kIgnore
850 : SpecialChars::kInterpret;
851 }
852 } else {
853 // Emit backslashes that do not precede a quote verbatim.
854 token.append(num_backslashes, L'\\');
855 if (p[0] == 0 ||
856 (state == SpecialChars::kInterpret && wcschr(kSpaceTab, p[0]))) {
857 result.push_back(token);
858 token.clear();
859 break;
860 }
861
862 token += *p;
863 }
864
865 ++p;
866 }
867 }
868
869 return result;
870 }
871
GetSwitchValueFromCommandLine(const std::wstring & command_line,const std::wstring & switch_name)872 std::wstring GetSwitchValueFromCommandLine(const std::wstring& command_line,
873 const std::wstring& switch_name) {
874 static constexpr wchar_t kSwitchTerminator[] = L"--";
875 assert(!command_line.empty());
876 assert(!switch_name.empty());
877
878 std::vector<std::wstring> as_array = TokenizeCommandLineToArray(command_line);
879 std::wstring switch_with_equal = L"--" + switch_name + L"=";
880 auto end = std::find(as_array.cbegin(), as_array.cend(), kSwitchTerminator);
881 for (auto scan = as_array.cbegin(); scan != end; ++scan) {
882 const std::wstring& arg = *scan;
883 if (arg.compare(0, switch_with_equal.size(), switch_with_equal) == 0)
884 return arg.substr(switch_with_equal.size());
885 }
886
887 return std::wstring();
888 }
889
RecursiveDirectoryCreate(const std::wstring & full_path)890 bool RecursiveDirectoryCreate(const std::wstring& full_path) {
891 // If the path exists, we've succeeded if it's a directory, failed otherwise.
892 const wchar_t* full_path_str = full_path.c_str();
893 DWORD file_attributes = ::GetFileAttributes(full_path_str);
894 if (file_attributes != INVALID_FILE_ATTRIBUTES) {
895 if ((file_attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
896 Trace(L"%hs( %ls directory exists )\n", __func__, full_path_str);
897 return true;
898 }
899 Trace(L"%hs( %ls directory conflicts with an existing file. )\n", __func__,
900 full_path_str);
901 return false;
902 }
903
904 // Invariant: Path does not exist as file or directory.
905
906 // Attempt to create the parent recursively. This will immediately return
907 // true if it already exists, otherwise will create all required parent
908 // directories starting with the highest-level missing parent.
909 std::wstring parent_path;
910 std::size_t pos = full_path.find_last_of(L"/\\");
911 if (pos != std::wstring::npos) {
912 parent_path = full_path.substr(0, pos);
913 if (!RecursiveDirectoryCreate(parent_path)) {
914 Trace(L"Failed to create one of the parent directories");
915 return false;
916 }
917 }
918 if (!::CreateDirectory(full_path_str, nullptr)) {
919 DWORD error_code = ::GetLastError();
920 if (error_code == ERROR_ALREADY_EXISTS && DirectoryExists(full_path_str)) {
921 // This error code ERROR_ALREADY_EXISTS doesn't indicate whether we
922 // were racing with someone creating the same directory, or a file
923 // with the same path. If the directory exists, we lost the
924 // race to create the same directory.
925 return true;
926 } else {
927 Trace(L"Failed to create directory %ls, last error is %d\n",
928 full_path_str, error_code);
929 return false;
930 }
931 }
932 return true;
933 }
934
935 // This function takes these inputs rather than accessing the module's
936 // InstallDetails instance since it is used to bootstrap InstallDetails.
DetermineChannel(const InstallConstants & mode,bool system_level,const wchar_t * channel_override,std::wstring * update_ap,std::wstring * update_cohort_name)937 DetermineChannelResult DetermineChannel(const InstallConstants& mode,
938 bool system_level,
939 const wchar_t* channel_override,
940 std::wstring* update_ap,
941 std::wstring* update_cohort_name) {
942 #if !BUILDFLAG(USE_GOOGLE_UPDATE_INTEGRATION)
943 return {std::wstring(), ChannelOrigin::kInstallMode};
944 #else
945 // Read the "ap" value and cache it if requested.
946 std::wstring client_state(GetClientStateKeyPath(mode.app_guid));
947 std::wstring ap_value;
948 // An empty |ap_value| is used in case of error.
949 nt::QueryRegValueSZ(system_level ? nt::HKLM : nt::HKCU, nt::WOW6432,
950 client_state.c_str(), kRegValueAp, &ap_value);
951 if (update_ap)
952 *update_ap = ap_value;
953
954 // Cache the cohort name if requested.
955 if (update_cohort_name) {
956 nt::QueryRegValueSZ(system_level ? nt::HKLM : nt::HKCU, nt::WOW6432,
957 client_state.append(L"\\cohort").c_str(), kRegValueName,
958 update_cohort_name);
959 }
960
961 switch (mode.channel_strategy) {
962 case ChannelStrategy::UNSUPPORTED:
963 assert(false);
964 break;
965 case ChannelStrategy::ADDITIONAL_PARAMETERS: {
966 std::wstring channel_override_value;
967 if (channel_override && GetChromeChannelNameFromString(
968 channel_override, channel_override_value)) {
969 return {std::move(channel_override_value), ChannelOrigin::kPolicy};
970 }
971 return {ChannelFromAdditionalParameters(mode, ap_value),
972 ChannelOrigin::kAdditionalParameters};
973 }
974 case ChannelStrategy::FIXED:
975 return {mode.default_channel_name, ChannelOrigin::kInstallMode};
976 }
977 return {std::wstring(), ChannelOrigin::kInstallMode};
978 #endif
979 }
980
981 } // namespace install_static
982