• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <comdef.h>
6 #include <iomanip>
7 #include <windows.h>
8 #include <winspool.h>
9 #include <setupapi.h>  // Must be included after windows.h
10 
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"
30 
31 #include <strsafe.h>  // Must be after base headers to avoid deprecation
32                       // warnings.
33 
34 namespace cloud_print {
35 
36 namespace {
37 
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";
42 
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",
58 };
59 
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";
65 
GetSystemPath(const base::string16 & binary)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;
71   }
72   return path.Append(binary);
73 }
74 
GetNativeSystemPath(const base::string16 & 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;
84   }
85   return path.Append(L"sysnative").Append(binary);
86 }
87 
SpoolerServiceCommand(const char * command)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");
96 
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);
102 }
103 
RegisterPortMonitor(bool install,const base::FilePath & install_path)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);
110   }
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();
118     }
119   } else if (!base::PathExists(target_path)) {
120     // Already removed.  Just "succeed" silently.
121     return S_OK;
122   }
123 
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);
128   }
129 
130   CommandLine command_line(regsvr32_path);
131   command_line.AppendArg("/s");
132   if (!install) {
133     command_line.AppendArg("/u");
134   }
135 
136   // Use system32 path here because otherwise ::AddMonitor would fail.
137   command_line.AppendArgPath(GetSystemPath(GetPortMonitorDllName()));
138 
139   base::LaunchOptions options;
140   options.wait = true;
141 
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);
147   }
148 
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();
154     }
155     if (exit_code != 0) {
156       LOG(ERROR) << "Regsvr32.exe failed with " << exit_code;
157       return HRESULT_FROM_WIN32(exit_code);
158     }
159   } else {
160     if (!base::DeleteFile(target_path, false)) {
161       SpoolerServiceCommand("stop");
162       bool deleted = base::DeleteFile(target_path, false);
163       SpoolerServiceCommand("start");
164 
165       if(!deleted) {
166         LOG(ERROR) << "Unable to delete " << target_path.value();
167         return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
168       }
169     }
170   }
171   return S_OK;
172 }
173 
GetVersionNumber()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;
185   }
186   return retval;
187 }
188 
CabinetCallback(PVOID data,UINT notification,UINT_PTR param1,UINT_PTR param2)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;
206       }
207     }
208     return FILEOP_SKIP;
209   }
210   return NO_ERROR;
211 }
212 
ReadyDriverDependencies(const base::FilePath & destination)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));
234 
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);
246       }
247     }
248   }
249 }
250 
InstallDriver(const base::FilePath & install_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());
256 
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();
265   }
266 
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);
272 
273   if (!base::PathExists(xps_path)) {
274     SetGoogleUpdateError(kGoogleUpdateProductId,
275                          LoadLocalString(IDS_ERROR_NO_XPS));
276     return HRESULT_FROM_WIN32(ERROR_BAD_DRIVER);
277   }
278 
279   DRIVER_INFO_6 driver_info = {0};
280   // Set up supported print system version.  Must be 3.
281   driver_info.cVersion = 3;
282 
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());
289 
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];
294 
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());
303 
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();
308   }
309   return S_OK;
310 }
311 
UninstallDriver()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;
323     }
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);
329   }
330   if (tries <= 0) {
331     HRESULT result = GetLastHResult();
332     LOG(ERROR) << "Unable to delete printer driver.";
333     return result;
334   }
335   return S_OK;
336 }
337 
InstallPrinter(void)338 HRESULT InstallPrinter(void) {
339   PRINTER_INFO_2 printer_info = {0};
340 
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;
357   }
358   ClosePrinter(handle);
359   return S_OK;
360 }
361 
UninstallPrinter(void)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;
373   }
374   if (!DeletePrinter(handle)) {
375     HRESULT result = GetLastHResult();
376     LOG(ERROR) << "Unable to delete printer";
377     ClosePrinter(handle);
378     return result;
379   }
380   ClosePrinter(handle);
381   return S_OK;
382 }
383 
IsOSSupported()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));
390 }
391 
RegisterVirtualDriver(const base::FilePath & install_path)392 HRESULT RegisterVirtualDriver(const base::FilePath& install_path) {
393   HRESULT result = S_OK;
394 
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);
399   }
400 
401   result = InstallDriver(install_path);
402   if (FAILED(result)) {
403     LOG(ERROR) << "Unable to install driver.";
404     return result;
405   }
406 
407   result = RegisterPortMonitor(true, install_path);
408   if (FAILED(result)) {
409     LOG(ERROR) << "Unable to register port monitor.";
410     return result;
411   }
412 
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;
418   }
419   return S_OK;
420 }
421 
TryUnregisterVirtualDriver()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;
428   }
429   result = UninstallDriver();
430   if (FAILED(result)) {
431     LOG(ERROR) << "Unable to remove driver.";
432     return result;
433   }
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;
439   }
440   return S_OK;
441 }
442 
UnregisterVirtualDriver()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;
449     }
450     // Restart spooler and try again.
451     SpoolerServiceCommand("stop");
452     SpoolerServiceCommand("start");
453   }
454   return hr;
455 }
456 
DoUninstall()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;
465 }
466 
DoUnregister()467 HRESULT DoUnregister() {
468   return UnregisterVirtualDriver();
469 }
470 
DoRegister(const base::FilePath & install_path)471 HRESULT DoRegister(const base::FilePath& install_path) {
472   HRESULT result = UnregisterVirtualDriver();
473   if (FAILED(result))
474     return result;
475   return RegisterVirtualDriver(install_path);
476 }
477 
DoDelete(const base::FilePath & 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;
485 }
486 
DoInstall(const base::FilePath & install_path)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;
492   }
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);
498   }
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;
506 }
507 
ExecuteCommands()508 HRESULT ExecuteCommands() {
509   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
510 
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);
515   }
516 
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);
527   }
528 
529   return E_INVALIDARG;
530 }
531 
532 }  // namespace
533 
534 }  // namespace cloud_print
535 
WinMain(__in HINSTANCE hInstance,__in HINSTANCE hPrevInstance,__in LPSTR lpCmdLine,__in int nCmdShow)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();
543 
544   VLOG(0) << _com_error(retval).ErrorMessage() << " HRESULT=0x" <<
545             std::setbase(16) << retval;
546 
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));
551   }
552   return retval;
553 }
554