Remove old about scheme URL constants.
[chromium-blink-merge.git] / cloud_print / virtual_driver / win / install / setup.cc
blob2ac07edbcb85e5b7c3acf7b6f8213035f48add31
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 <comdef.h>
6 #include <iomanip>
7 #include <windows.h>
8 #include <winspool.h>
9 #include <setupapi.h> // Must be included after windows.h
11 #include "base/at_exit.h"
12 #include "base/command_line.h"
13 #include "base/file_util.h"
14 #include "base/file_version_info_win.h"
15 #include "base/files/scoped_temp_dir.h"
16 #include "base/logging.h"
17 #include "base/path_service.h"
18 #include "base/process/process.h"
19 #include "base/process/launch.h"
20 #include "base/strings/string16.h"
21 #include "base/strings/string_util.h"
22 #include "base/win/registry.h"
23 #include "base/win/scoped_handle.h"
24 #include "base/win/windows_version.h"
25 #include "cloud_print/common/win/cloud_print_utils.h"
26 #include "cloud_print/common/win/install_utils.h"
27 #include "cloud_print/virtual_driver/win/virtual_driver_consts.h"
28 #include "cloud_print/virtual_driver/win/virtual_driver_helpers.h"
29 #include "grit/virtual_driver_setup_resources.h"
31 #include <strsafe.h> // Must be after base headers to avoid deprecation
32 // warnings.
34 namespace cloud_print {
36 namespace {
38 const wchar_t kNameValue[] = L"GCP Virtual Driver";
39 const wchar_t kUninstallId[] = L"{74AA24E0-AC50-4B28-BA46-9CF05467C9B7}";
40 const wchar_t kInstallerName[] = L"virtual_driver_setup.exe";
41 const wchar_t kGcpUrl[] = L"http://www.google.com/cloudprint";
43 const wchar_t kDataFileName[] = L"gcp_driver.gpd";
44 const wchar_t kDriverName[] = L"MXDWDRV.DLL";
45 const wchar_t kUiDriverName[] = L"UNIDRVUI.DLL";
46 const wchar_t kHelpName[] = L"UNIDRV.HLP";
47 const wchar_t* kDependencyList[] = {
48 kDriverName,
49 kHelpName,
50 kUiDriverName,
51 L"STDDTYPE.GDL",
52 L"STDNAMES.GPD",
53 L"STDSCHEM.GDL",
54 L"STDSCHMX.GDL",
55 L"UNIDRV.DLL",
56 L"UNIRES.DLL",
57 L"XPSSVCS.DLL",
60 const char kDelete[] = "delete";
61 const char kInstallSwitch[] = "install";
62 const char kRegisterSwitch[] = "register";
63 const char kUninstallSwitch[] = "uninstall";
64 const char kUnregisterSwitch[] = "unregister";
66 base::FilePath GetSystemPath(const base::string16& binary) {
67 base::FilePath path;
68 if (!PathService::Get(base::DIR_SYSTEM, &path)) {
69 LOG(ERROR) << "Unable to get system path.";
70 return path;
72 return path.Append(binary);
75 base::FilePath GetNativeSystemPath(const base::string16& binary) {
76 if (!IsSystem64Bit())
77 return GetSystemPath(binary);
78 base::FilePath path;
79 // Sysnative will bypass filesystem redirection and give us
80 // the location of the 64bit system32 from a 32 bit process.
81 if (!PathService::Get(base::DIR_WINDOWS, &path)) {
82 LOG(ERROR) << "Unable to get windows path.";
83 return path;
85 return path.Append(L"sysnative").Append(binary);
88 void SpoolerServiceCommand(const char* command) {
89 base::FilePath net_path = GetNativeSystemPath(L"net");
90 if (net_path.empty())
91 return;
92 CommandLine command_line(net_path);
93 command_line.AppendArg(command);
94 command_line.AppendArg("spooler");
95 command_line.AppendArg("/y");
97 base::LaunchOptions options;
98 options.wait = true;
99 options.start_hidden = true;
100 VLOG(0) << command_line.GetCommandLineString();
101 base::LaunchProcess(command_line, options, NULL);
104 HRESULT RegisterPortMonitor(bool install, const base::FilePath& install_path) {
105 DCHECK(install || install_path.empty());
106 base::FilePath target_path = GetNativeSystemPath(GetPortMonitorDllName());
107 if (target_path.empty()) {
108 LOG(ERROR) << "Unable to get port monitor target path.";
109 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
111 if (install) {
112 base::FilePath source_path =
113 install_path.Append(GetPortMonitorDllName());
114 if (!base::CopyFile(source_path, target_path)) {
115 LOG(ERROR) << "Unable copy port monitor dll from " <<
116 source_path.value() << " to " << target_path.value();
117 return GetLastHResult();
119 } else if (!base::PathExists(target_path)) {
120 // Already removed. Just "succeed" silently.
121 return S_OK;
124 base::FilePath regsvr32_path = GetNativeSystemPath(L"regsvr32.exe");
125 if (regsvr32_path.empty()) {
126 LOG(ERROR) << "Can't find regsvr32.exe.";
127 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
130 CommandLine command_line(regsvr32_path);
131 command_line.AppendArg("/s");
132 if (!install) {
133 command_line.AppendArg("/u");
136 // Use system32 path here because otherwise ::AddMonitor would fail.
137 command_line.AppendArgPath(GetSystemPath(GetPortMonitorDllName()));
139 base::LaunchOptions options;
140 options.wait = true;
142 base::win::ScopedHandle regsvr32_handle;
143 if (!base::LaunchProcess(command_line.GetCommandLineString(), options,
144 &regsvr32_handle)) {
145 LOG(ERROR) << "Unable to launch regsvr32.exe.";
146 return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
149 DWORD exit_code = S_OK;
150 if (install) {
151 if (!GetExitCodeProcess(regsvr32_handle, &exit_code)) {
152 LOG(ERROR) << "Unable to get regsvr32.exe exit code.";
153 return GetLastHResult();
155 if (exit_code != 0) {
156 LOG(ERROR) << "Regsvr32.exe failed with " << exit_code;
157 return HRESULT_FROM_WIN32(exit_code);
159 } else {
160 if (!base::DeleteFile(target_path, false)) {
161 SpoolerServiceCommand("stop");
162 bool deleted = base::DeleteFile(target_path, false);
163 SpoolerServiceCommand("start");
165 if(!deleted) {
166 LOG(ERROR) << "Unable to delete " << target_path.value();
167 return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
171 return S_OK;
174 DWORDLONG GetVersionNumber() {
175 DWORDLONG retval = 0;
176 scoped_ptr<FileVersionInfo> version_info(
177 FileVersionInfo::CreateFileVersionInfoForCurrentModule());
178 if (version_info.get()) {
179 FileVersionInfoWin* version_info_win =
180 static_cast<FileVersionInfoWin*>(version_info.get());
181 VS_FIXEDFILEINFO* fixed_file_info = version_info_win->fixed_file_info();
182 retval = fixed_file_info->dwFileVersionMS;
183 retval <<= 32;
184 retval |= fixed_file_info->dwFileVersionLS;
186 return retval;
189 UINT CALLBACK CabinetCallback(PVOID data,
190 UINT notification,
191 UINT_PTR param1,
192 UINT_PTR param2) {
193 const base::FilePath* temp_path(
194 reinterpret_cast<const base::FilePath*>(data));
195 if (notification == SPFILENOTIFY_FILEINCABINET) {
196 FILE_IN_CABINET_INFO* info =
197 reinterpret_cast<FILE_IN_CABINET_INFO*>(param1);
198 for (int i = 0; i < arraysize(kDependencyList); i++) {
199 base::FilePath base_name(info->NameInCabinet);
200 base_name = base_name.BaseName();
201 if (base::FilePath::CompareEqualIgnoreCase(base_name.value().c_str(),
202 kDependencyList[i])) {
203 StringCchCopy(info->FullTargetName, MAX_PATH,
204 temp_path->Append(kDependencyList[i]).value().c_str());
205 return FILEOP_DOIT;
208 return FILEOP_SKIP;
210 return NO_ERROR;
213 void ReadyDriverDependencies(const base::FilePath& destination) {
214 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
215 // GetCorePrinterDrivers and GetPrinterDriverPackagePath only exist on
216 // Vista and later. Winspool.drv must be delayloaded so these calls don't
217 // create problems on XP.
218 DWORD size = MAX_PATH;
219 wchar_t package_path[MAX_PATH] = {0};
220 CORE_PRINTER_DRIVER driver;
221 GetCorePrinterDrivers(NULL, NULL, L"{D20EA372-DD35-4950-9ED8-A6335AFE79F5}",
222 1, &driver);
223 GetPrinterDriverPackagePath(NULL, NULL, NULL, driver.szPackageID,
224 package_path, MAX_PATH, &size);
225 SetupIterateCabinet(package_path, 0, &CabinetCallback,
226 &base::FilePath(destination));
227 } else {
228 // Driver files are in the sp3 cab.
229 base::FilePath package_path;
230 PathService::Get(base::DIR_WINDOWS, &package_path);
231 package_path = package_path.Append(L"Driver Cache\\i386\\sp3.cab");
232 SetupIterateCabinet(package_path.value().c_str(), 0, &CabinetCallback,
233 &base::FilePath(destination));
235 // Copy the rest from the driver cache or system dir.
236 base::FilePath driver_cache_path;
237 PathService::Get(base::DIR_WINDOWS, &driver_cache_path);
238 driver_cache_path = driver_cache_path.Append(L"Driver Cache\\i386");
239 for (size_t i = 0; i < arraysize(kDependencyList); ++i) {
240 base::FilePath dst_path = destination.Append(kDependencyList[i]);
241 if (!base::PathExists(dst_path)) {
242 base::FilePath src_path = driver_cache_path.Append(kDependencyList[i]);
243 if (!base::PathExists(src_path))
244 src_path = GetSystemPath(kDependencyList[i]);
245 base::CopyFile(src_path, dst_path);
251 HRESULT InstallDriver(const base::FilePath& install_path) {
252 base::ScopedTempDir temp_path;
253 if (!temp_path.CreateUniqueTempDir())
254 return HRESULT_FROM_WIN32(ERROR_CANNOT_MAKE);
255 ReadyDriverDependencies(temp_path.path());
257 std::vector<base::string16> dependent_array;
258 // Add all files. AddPrinterDriverEx will removes unnecessary.
259 for (size_t i = 0; i < arraysize(kDependencyList); ++i) {
260 base::FilePath file_path = temp_path.path().Append(kDependencyList[i]);
261 if (base::PathExists(file_path))
262 dependent_array.push_back(file_path.value());
263 else
264 LOG(WARNING) << "File is missing: " << file_path.BaseName().value();
267 // Set up paths for the files we depend on.
268 base::FilePath data_file = install_path.Append(kDataFileName);
269 base::FilePath xps_path = temp_path.path().Append(kDriverName);
270 base::FilePath ui_path = temp_path.path().Append(kUiDriverName);
271 base::FilePath ui_help_path = temp_path.path().Append(kHelpName);
273 if (!base::PathExists(xps_path)) {
274 SetGoogleUpdateError(kGoogleUpdateProductId,
275 LoadLocalString(IDS_ERROR_NO_XPS));
276 return HRESULT_FROM_WIN32(ERROR_BAD_DRIVER);
279 DRIVER_INFO_6 driver_info = {0};
280 // Set up supported print system version. Must be 3.
281 driver_info.cVersion = 3;
283 // None of the print API structures likes constant strings even though they
284 // don't modify the string. const_casting is the cleanest option.
285 driver_info.pDataFile = const_cast<LPWSTR>(data_file.value().c_str());
286 driver_info.pHelpFile = const_cast<LPWSTR>(ui_help_path.value().c_str());
287 driver_info.pDriverPath = const_cast<LPWSTR>(xps_path.value().c_str());
288 driver_info.pConfigFile = const_cast<LPWSTR>(ui_path.value().c_str());
290 base::string16 dependent_files(JoinString(dependent_array, L'\n'));
291 dependent_files.push_back(L'\n');
292 std::replace(dependent_files.begin(), dependent_files.end(), L'\n', L'\0');
293 driver_info.pDependentFiles = &dependent_files[0];
295 // Set up user visible strings.
296 base::string16 manufacturer = LoadLocalString(IDS_GOOGLE);
297 driver_info.pszMfgName = const_cast<LPWSTR>(manufacturer.c_str());
298 driver_info.pszProvider = const_cast<LPWSTR>(manufacturer.c_str());
299 driver_info.pszOEMUrl = const_cast<LPWSTR>(kGcpUrl);
300 driver_info.dwlDriverVersion = GetVersionNumber();
301 base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
302 driver_info.pName = const_cast<LPWSTR>(driver_name.c_str());
304 if (!::AddPrinterDriverEx(NULL, 6, reinterpret_cast<BYTE*>(&driver_info),
305 APD_COPY_NEW_FILES | APD_COPY_FROM_DIRECTORY)) {
306 LOG(ERROR) << "Unable to add printer driver";
307 return GetLastHResult();
309 return S_OK;
312 HRESULT UninstallDriver() {
313 int tries = 3;
314 base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
315 while (!DeletePrinterDriverEx(NULL,
316 NULL,
317 const_cast<LPWSTR>(driver_name.c_str()),
318 DPD_DELETE_UNUSED_FILES,
319 0) && tries > 0) {
320 if (GetLastError() == ERROR_UNKNOWN_PRINTER_DRIVER) {
321 LOG(WARNING) << "Print driver is already uninstalled.";
322 return S_OK;
324 // After deleting the printer it can take a few seconds before
325 // the driver is free for deletion. Retry a few times before giving up.
326 LOG(WARNING) << "Attempt to delete printer driver failed. Retrying.";
327 tries--;
328 Sleep(2000);
330 if (tries <= 0) {
331 HRESULT result = GetLastHResult();
332 LOG(ERROR) << "Unable to delete printer driver.";
333 return result;
335 return S_OK;
338 HRESULT InstallPrinter(void) {
339 PRINTER_INFO_2 printer_info = {0};
341 // None of the print API structures likes constant strings even though they
342 // don't modify the string. const_casting is the cleanest option.
343 base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
344 printer_info.pDriverName = const_cast<LPWSTR>(driver_name.c_str());
345 printer_info.pPrinterName = const_cast<LPWSTR>(driver_name.c_str());
346 printer_info.pComment = const_cast<LPWSTR>(driver_name.c_str());
347 printer_info.pLocation = const_cast<LPWSTR>(kGcpUrl);
348 base::string16 port_name;
349 printer_info.pPortName = const_cast<LPWSTR>(kPortName);
350 printer_info.Attributes = PRINTER_ATTRIBUTE_DIRECT|PRINTER_ATTRIBUTE_LOCAL;
351 printer_info.pPrintProcessor = L"winprint";
352 HANDLE handle = AddPrinter(NULL, 2, reinterpret_cast<BYTE*>(&printer_info));
353 if (handle == NULL) {
354 HRESULT result = GetLastHResult();
355 LOG(ERROR) << "Unable to add printer";
356 return result;
358 ClosePrinter(handle);
359 return S_OK;
362 HRESULT UninstallPrinter(void) {
363 HANDLE handle = NULL;
364 PRINTER_DEFAULTS printer_defaults = {0};
365 printer_defaults.DesiredAccess = PRINTER_ALL_ACCESS;
366 base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME);
367 if (!OpenPrinter(const_cast<LPWSTR>(driver_name.c_str()),
368 &handle,
369 &printer_defaults)) {
370 // If we can't open the printer, it was probably already removed.
371 LOG(WARNING) << "Unable to open printer";
372 return S_OK;
374 if (!DeletePrinter(handle)) {
375 HRESULT result = GetLastHResult();
376 LOG(ERROR) << "Unable to delete printer";
377 ClosePrinter(handle);
378 return result;
380 ClosePrinter(handle);
381 return S_OK;
384 bool IsOSSupported() {
385 // We don't support XP service pack 2 or older.
386 base::win::Version version = base::win::GetVersion();
387 return (version > base::win::VERSION_XP) ||
388 ((version == base::win::VERSION_XP) &&
389 (base::win::OSInfo::GetInstance()->service_pack().major >= 3));
392 HRESULT RegisterVirtualDriver(const base::FilePath& install_path) {
393 HRESULT result = S_OK;
395 DCHECK(base::DirectoryExists(install_path));
396 if (!IsOSSupported()) {
397 LOG(ERROR) << "Requires XP SP3 or later.";
398 return HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION);
401 result = InstallDriver(install_path);
402 if (FAILED(result)) {
403 LOG(ERROR) << "Unable to install driver.";
404 return result;
407 result = RegisterPortMonitor(true, install_path);
408 if (FAILED(result)) {
409 LOG(ERROR) << "Unable to register port monitor.";
410 return result;
413 result = InstallPrinter();
414 if (FAILED(result) &&
415 result != HRESULT_FROM_WIN32(ERROR_PRINTER_ALREADY_EXISTS)) {
416 LOG(ERROR) << "Unable to install printer.";
417 return result;
419 return S_OK;
422 HRESULT TryUnregisterVirtualDriver() {
423 HRESULT result = S_OK;
424 result = UninstallPrinter();
425 if (FAILED(result)) {
426 LOG(ERROR) << "Unable to delete printer.";
427 return result;
429 result = UninstallDriver();
430 if (FAILED(result)) {
431 LOG(ERROR) << "Unable to remove driver.";
432 return result;
434 // The second argument is ignored if the first is false.
435 result = RegisterPortMonitor(false, base::FilePath());
436 if (FAILED(result)) {
437 LOG(ERROR) << "Unable to remove port monitor.";
438 return result;
440 return S_OK;
443 HRESULT UnregisterVirtualDriver() {
444 HRESULT hr = S_FALSE;
445 for (int i = 0; i < 2; ++i) {
446 hr = TryUnregisterVirtualDriver();
447 if (SUCCEEDED(hr)) {
448 break;
450 // Restart spooler and try again.
451 SpoolerServiceCommand("stop");
452 SpoolerServiceCommand("start");
454 return hr;
457 HRESULT DoUninstall() {
458 DeleteGoogleUpdateKeys(kGoogleUpdateProductId);
459 HRESULT result = UnregisterVirtualDriver();
460 if (FAILED(result))
461 return result;
462 DeleteUninstallKey(kUninstallId);
463 DeleteProgramDir(kDelete);
464 return S_OK;
467 HRESULT DoUnregister() {
468 return UnregisterVirtualDriver();
471 HRESULT DoRegister(const base::FilePath& install_path) {
472 HRESULT result = UnregisterVirtualDriver();
473 if (FAILED(result))
474 return result;
475 return RegisterVirtualDriver(install_path);
478 HRESULT DoDelete(const base::FilePath& install_path) {
479 if (install_path.value().empty())
480 return E_INVALIDARG;
481 if (!base::DirectoryExists(install_path))
482 return S_FALSE;
483 Sleep(5000); // Give parent some time to exit.
484 return base::DeleteFile(install_path, true) ? S_OK : E_FAIL;
487 HRESULT DoInstall(const base::FilePath& install_path) {
488 HRESULT result = UnregisterVirtualDriver();
489 if (FAILED(result)) {
490 LOG(ERROR) << "Unable to unregister.";
491 return result;
493 base::FilePath old_install_path = GetInstallLocation(kUninstallId);
494 if (!old_install_path.value().empty() &&
495 install_path != old_install_path) {
496 if (base::DirectoryExists(old_install_path))
497 base::DeleteFile(old_install_path, true);
499 CreateUninstallKey(kUninstallId, LoadLocalString(IDS_DRIVER_NAME),
500 kUninstallSwitch);
501 result = RegisterVirtualDriver(install_path);
502 if (FAILED(result))
503 return result;
504 SetGoogleUpdateKeys(kGoogleUpdateProductId, kNameValue);
505 return result;
508 HRESULT ExecuteCommands() {
509 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
511 base::FilePath exe_path;
512 if (FAILED(PathService::Get(base::DIR_EXE, &exe_path)) ||
513 !base::DirectoryExists(exe_path)) {
514 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
517 if (command_line.HasSwitch(kDelete)) {
518 return DoDelete(command_line.GetSwitchValuePath(kDelete));
519 } else if (command_line.HasSwitch(kUninstallSwitch)) {
520 return DoUninstall();
521 } else if (command_line.HasSwitch(kInstallSwitch)) {
522 return DoInstall(exe_path);
523 } else if (command_line.HasSwitch(kUnregisterSwitch)) {
524 return DoUnregister();
525 } else if (command_line.HasSwitch(kRegisterSwitch)) {
526 return DoRegister(exe_path);
529 return E_INVALIDARG;
532 } // namespace
534 } // namespace cloud_print
536 int WINAPI WinMain(__in HINSTANCE hInstance,
537 __in HINSTANCE hPrevInstance,
538 __in LPSTR lpCmdLine,
539 __in int nCmdShow) {
540 base::AtExitManager at_exit_manager;
541 CommandLine::Init(0, NULL);
542 HRESULT retval = cloud_print::ExecuteCommands();
544 VLOG(0) << _com_error(retval).ErrorMessage() << " HRESULT=0x" <<
545 std::setbase(16) << retval;
547 // Installer is silent by default as required by Google Update.
548 if (CommandLine::ForCurrentProcess()->HasSwitch("verbose")) {
549 cloud_print::DisplayWindowsMessage(NULL, retval,
550 cloud_print::LoadLocalString(IDS_DRIVER_NAME));
552 return retval;