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