This source file includes following definitions.
- GetAppDataDir
- DeleteLeakedFiles
- GetJobTitle
- HandlePortUi
- GetUserToken
- LaunchPrintDialog
- LaunchChromeDownloadPage
- ValidateCurrentUser
- ReadPathFromRegistry
- ReadPathFromAnyRegistry
- GetChromeExePath
- GetChromeProfilePath
- Monitor2EnumPorts
- Monitor2OpenPort
- Monitor2StartDocPort
- Monitor2WritePort
- Monitor2ReadPort
- Monitor2EndDocPort
- Monitor2ClosePort
- Monitor2Shutdown
- Monitor2XcvOpenPort
- Monitor2XcvDataPort
- Monitor2XcvClosePort
- MonitorUiAddPortUi
- MonitorUiConfigureOrDeletePortUI
- InitializePrintMonitor2
- InitializePrintMonitorUI
#include "cloud_print/virtual_driver/win/port_monitor/port_monitor.h"
#include <lmcons.h>
#include <shellapi.h>
#include <shlobj.h>
#include <strsafe.h>
#include <userenv.h>
#include <windows.h>
#include <winspool.h>
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/files/file_enumerator.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/process/process.h"
#include "base/process/launch.h"
#include "base/strings/string16.h"
#include "base/win/registry.h"
#include "base/win/scoped_handle.h"
#include "base/win/windows_version.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/installer/launcher_support/chrome_launcher_support.h"
#include "cloud_print/common/win/cloud_print_utils.h"
#include "cloud_print/virtual_driver/win/port_monitor/spooler_win.h"
#include "cloud_print/virtual_driver/win/virtual_driver_consts.h"
#include "cloud_print/virtual_driver/win/virtual_driver_helpers.h"
namespace cloud_print {
namespace {
const wchar_t kIePath[] = L"Internet Explorer\\iexplore.exe";
const char kChromeInstallUrl[] =
"http://google.com/cloudprint/learn/chrome.html";
const wchar_t kCloudPrintRegKey[] = L"Software\\Google\\CloudPrint";
const wchar_t kXpsMimeType[] = L"application/vnd.ms-xpsdocument";
const wchar_t kAppDataDir[] = L"Google\\Cloud Printer";
struct MonitorData {
scoped_ptr<base::AtExitManager> at_exit_manager;
};
struct PortData {
PortData() : job_id(0), printer_handle(NULL), file(0) {
}
~PortData() {
Close();
}
void Close() {
if (printer_handle) {
ClosePrinter(printer_handle);
printer_handle = NULL;
}
if (file) {
base::CloseFile(file);
file = NULL;
}
}
DWORD job_id;
HANDLE printer_handle;
FILE* file;
base::FilePath file_path;
};
typedef struct {
ACCESS_MASK granted_access;
} XcvUiData;
MONITORUI g_monitor_ui = {
sizeof(MONITORUI),
MonitorUiAddPortUi,
MonitorUiConfigureOrDeletePortUI,
MonitorUiConfigureOrDeletePortUI
};
MONITOR2 g_monitor_2 = {
sizeof(MONITOR2),
Monitor2EnumPorts,
Monitor2OpenPort,
NULL,
Monitor2StartDocPort,
Monitor2WritePort,
Monitor2ReadPort,
Monitor2EndDocPort,
Monitor2ClosePort,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
Monitor2XcvOpenPort,
Monitor2XcvDataPort,
Monitor2XcvClosePort,
Monitor2Shutdown
};
base::FilePath GetAppDataDir() {
base::FilePath file_path;
base::win::Version version = base::win::GetVersion();
int path_id = (version >= base::win::VERSION_VISTA) ?
base::DIR_LOCAL_APP_DATA_LOW : base::DIR_LOCAL_APP_DATA;
if (!PathService::Get(path_id, &file_path)) {
LOG(ERROR) << "Can't get DIR_LOCAL_APP_DATA";
return base::FilePath();
}
return file_path.Append(kAppDataDir);
}
void DeleteLeakedFiles(const base::FilePath& dir) {
base::Time delete_before = base::Time::Now() - base::TimeDelta::FromDays(1);
base::FileEnumerator enumerator(dir, false, base::FileEnumerator::FILES);
for (base::FilePath file_path = enumerator.Next(); !file_path.empty();
file_path = enumerator.Next()) {
if (enumerator.GetInfo().GetLastModifiedTime() < delete_before)
base::DeleteFile(file_path, false);
}
}
bool GetJobTitle(HANDLE printer_handle,
DWORD job_id,
base::string16 *title) {
DCHECK(printer_handle != NULL);
DCHECK(title != NULL);
DWORD bytes_needed = 0;
GetJob(printer_handle, job_id, 1, NULL, 0, &bytes_needed);
if (bytes_needed == 0) {
LOG(ERROR) << "Unable to get bytes needed for job info.";
return false;
}
scoped_ptr<BYTE[]> buffer(new BYTE[bytes_needed]);
if (!GetJob(printer_handle,
job_id,
1,
buffer.get(),
bytes_needed,
&bytes_needed)) {
LOG(ERROR) << "Unable to get job info.";
return false;
}
JOB_INFO_1* job_info = reinterpret_cast<JOB_INFO_1*>(buffer.get());
*title = job_info->pDocument;
return true;
}
void HandlePortUi(HWND hwnd, const base::string16& caption) {
if (hwnd != NULL && IsWindow(hwnd)) {
DisplayWindowsMessage(hwnd, CO_E_NOT_SUPPORTED, cloud_print::kPortName);
}
}
bool GetUserToken(HANDLE* primary_token) {
HANDLE token = NULL;
if (!OpenThreadToken(GetCurrentThread(),
TOKEN_QUERY|TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY,
FALSE,
&token)) {
LOG(ERROR) << "Unable to get thread token.";
return false;
}
base::win::ScopedHandle token_scoped(token);
if (!DuplicateTokenEx(token,
TOKEN_QUERY|TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY,
NULL,
SecurityImpersonation,
TokenPrimary,
primary_token)) {
LOG(ERROR) << "Unable to get primary thread token.";
return false;
}
return true;
}
bool LaunchPrintDialog(const base::FilePath& xps_path,
const base::string16& job_title) {
HANDLE token = NULL;
if (!GetUserToken(&token)) {
LOG(ERROR) << "Unable to get user token.";
return false;
}
base::win::ScopedHandle primary_token_scoped(token);
base::FilePath chrome_path = GetChromeExePath();
if (chrome_path.empty()) {
LOG(ERROR) << "Unable to get chrome exe path.";
return false;
}
CommandLine command_line(chrome_path);
base::FilePath chrome_profile = GetChromeProfilePath();
if (!chrome_profile.empty())
command_line.AppendSwitchPath(switches::kUserDataDir, chrome_profile);
command_line.AppendSwitchPath(switches::kCloudPrintFile, xps_path);
command_line.AppendSwitchNative(switches::kCloudPrintFileType, kXpsMimeType);
command_line.AppendSwitchNative(switches::kCloudPrintJobTitle, job_title);
base::LaunchOptions options;
options.as_user = primary_token_scoped;
base::LaunchProcess(command_line, options, NULL);
return true;
}
void LaunchChromeDownloadPage() {
if (kIsUnittest)
return;
HANDLE token = NULL;
if (!GetUserToken(&token)) {
LOG(ERROR) << "Unable to get user token.";
return;
}
base::win::ScopedHandle token_scoped(token);
base::FilePath ie_path;
PathService::Get(base::DIR_PROGRAM_FILESX86, &ie_path);
ie_path = ie_path.Append(kIePath);
CommandLine command_line(ie_path);
command_line.AppendArg(kChromeInstallUrl);
base::LaunchOptions options;
options.as_user = token_scoped;
base::LaunchProcess(command_line, options, NULL);
}
bool ValidateCurrentUser() {
HANDLE token = NULL;
if (!GetUserToken(&token)) {
return false;
}
base::win::ScopedHandle token_scoped(token);
if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
DWORD session_id = 0;
DWORD dummy;
if (!GetTokenInformation(token_scoped,
TokenSessionId,
reinterpret_cast<void *>(&session_id),
sizeof(DWORD),
&dummy)) {
return false;
}
if (session_id == 0) {
return false;
}
}
return true;
}
}
base::FilePath ReadPathFromRegistry(HKEY root, const wchar_t* path_name) {
base::win::RegKey gcp_key(HKEY_CURRENT_USER, kCloudPrintRegKey, KEY_READ);
base::string16 data;
if (SUCCEEDED(gcp_key.ReadValue(path_name, &data)) &&
base::PathExists(base::FilePath(data))) {
return base::FilePath(data);
}
return base::FilePath();
}
base::FilePath ReadPathFromAnyRegistry(const wchar_t* path_name) {
base::FilePath result = ReadPathFromRegistry(HKEY_CURRENT_USER, path_name);
if (!result.empty())
return result;
return ReadPathFromRegistry(HKEY_LOCAL_MACHINE, path_name);
}
base::FilePath GetChromeExePath() {
base::FilePath path = ReadPathFromAnyRegistry(kChromeExePathRegValue);
if (!path.empty())
return path;
return chrome_launcher_support::GetAnyChromePath();
}
base::FilePath GetChromeProfilePath() {
base::FilePath path = ReadPathFromAnyRegistry(kChromeProfilePathRegValue);
if (!path.empty() && base::DirectoryExists(path))
return path;
return base::FilePath();
}
BOOL WINAPI Monitor2EnumPorts(HANDLE,
wchar_t*,
DWORD level,
BYTE* ports,
DWORD ports_size,
DWORD* needed_bytes,
DWORD* returned) {
if (needed_bytes == NULL) {
LOG(ERROR) << "needed_bytes should not be NULL.";
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (level == 1) {
*needed_bytes = sizeof(PORT_INFO_1);
} else if (level == 2) {
*needed_bytes = sizeof(PORT_INFO_2);
} else {
LOG(ERROR) << "Level " << level << "is not supported.";
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
*needed_bytes += static_cast<DWORD>(cloud_print::kPortNameSize);
if (ports_size < *needed_bytes) {
LOG(WARNING) << *needed_bytes << " bytes are required. Only "
<< ports_size << " were allocated.";
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (ports == NULL) {
LOG(ERROR) << "ports should not be NULL.";
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (returned == NULL) {
LOG(ERROR) << "returned should not be NULL.";
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
wchar_t* string_target =
reinterpret_cast<wchar_t*>(ports + ports_size -
cloud_print::kPortNameSize);
if (level == 1) {
PORT_INFO_1* port_info = reinterpret_cast<PORT_INFO_1*>(ports);
port_info->pName = string_target;
StringCbCopy(port_info->pName,
cloud_print::kPortNameSize,
cloud_print::kPortName);
} else {
PORT_INFO_2* port_info = reinterpret_cast<PORT_INFO_2*>(ports);
port_info->pPortName = string_target;
StringCbCopy(port_info->pPortName,
cloud_print::kPortNameSize,
cloud_print::kPortName);
port_info->pMonitorName = NULL;
port_info->pDescription = NULL;
port_info->fPortType = PORT_TYPE_WRITE;
port_info->Reserved = 0;
}
*returned = 1;
return TRUE;
}
BOOL WINAPI Monitor2OpenPort(HANDLE, wchar_t*, HANDLE* handle) {
if (handle == NULL) {
LOG(ERROR) << "handle should not be NULL.";
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
*handle = new PortData();
return TRUE;
}
BOOL WINAPI Monitor2StartDocPort(HANDLE port_handle,
wchar_t* printer_name,
DWORD job_id,
DWORD,
BYTE*) {
SetGoogleUpdateUsage(kGoogleUpdateProductId);
if (port_handle == NULL) {
LOG(ERROR) << "port_handle should not be NULL.";
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (printer_name == NULL) {
LOG(ERROR) << "printer_name should not be NULL.";
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (!ValidateCurrentUser()) {
return FALSE;
}
PortData* port_data = reinterpret_cast<PortData*>(port_handle);
port_data->job_id = job_id;
if (!OpenPrinter(printer_name, &(port_data->printer_handle), NULL)) {
LOG(WARNING) << "Unable to open printer " << printer_name << ".";
port_data->printer_handle = NULL;
}
base::FilePath& file_path = port_data->file_path;
base::FilePath app_data_dir = GetAppDataDir();
if (app_data_dir.empty())
return FALSE;
DeleteLeakedFiles(app_data_dir);
if (!base::CreateDirectory(app_data_dir) ||
!base::CreateTemporaryFileInDir(app_data_dir, &file_path)) {
LOG(ERROR) << "Can't create temporary file in " << app_data_dir.value();
return FALSE;
}
port_data->file = base::OpenFile(file_path, "wb+");
if (port_data->file == NULL) {
LOG(ERROR) << "Error opening file " << file_path.value() << ".";
return FALSE;
}
return TRUE;
}
BOOL WINAPI Monitor2WritePort(HANDLE port_handle,
BYTE* buffer,
DWORD buffer_size,
DWORD* bytes_written) {
PortData* port_data = reinterpret_cast<PortData*>(port_handle);
if (!ValidateCurrentUser()) {
return FALSE;
}
*bytes_written =
static_cast<DWORD>(fwrite(buffer, 1, buffer_size, port_data->file));
if (*bytes_written > 0) {
return TRUE;
} else {
return FALSE;
}
}
BOOL WINAPI Monitor2ReadPort(HANDLE, BYTE*, DWORD, DWORD* read_bytes) {
LOG(ERROR) << "Read is not supported.";
*read_bytes = 0;
SetLastError(ERROR_NOT_SUPPORTED);
return FALSE;
}
BOOL WINAPI Monitor2EndDocPort(HANDLE port_handle) {
if (!ValidateCurrentUser()) {
return FALSE;
}
PortData* port_data = reinterpret_cast<PortData*>(port_handle);
if (port_data == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (port_data->file != NULL) {
base::CloseFile(port_data->file);
port_data->file = NULL;
bool delete_file = true;
int64 file_size = 0;
base::GetFileSize(port_data->file_path, &file_size);
if (file_size > 0) {
base::string16 job_title;
if (port_data->printer_handle != NULL) {
GetJobTitle(port_data->printer_handle,
port_data->job_id,
&job_title);
}
if (!LaunchPrintDialog(port_data->file_path, job_title)) {
LaunchChromeDownloadPage();
} else {
delete_file = false;
}
}
if (delete_file)
base::DeleteFile(port_data->file_path, false);
}
if (port_data->printer_handle != NULL) {
SetJob(port_data->printer_handle,
port_data->job_id,
0,
NULL,
JOB_CONTROL_SENT_TO_PRINTER);
}
port_data->Close();
return TRUE;
}
BOOL WINAPI Monitor2ClosePort(HANDLE port_handle) {
if (port_handle == NULL) {
LOG(ERROR) << "port_handle should not be NULL.";
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
PortData* port_data = reinterpret_cast<PortData*>(port_handle);
delete port_data;
return TRUE;
}
VOID WINAPI Monitor2Shutdown(HANDLE monitor_handle) {
if (monitor_handle != NULL) {
MonitorData* monitor_data =
reinterpret_cast<MonitorData*>(monitor_handle);
delete monitor_handle;
}
}
BOOL WINAPI Monitor2XcvOpenPort(HANDLE,
const wchar_t*,
ACCESS_MASK granted_access,
HANDLE* handle) {
if (handle == NULL) {
LOG(ERROR) << "handle should not be NULL.";
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
XcvUiData* xcv_data = new XcvUiData();
xcv_data->granted_access = granted_access;
*handle = xcv_data;
return TRUE;
}
DWORD WINAPI Monitor2XcvDataPort(HANDLE xcv_handle,
const wchar_t* data_name,
BYTE*,
DWORD,
BYTE* output_data,
DWORD output_data_bytes,
DWORD* output_data_bytes_needed) {
XcvUiData* xcv_data = reinterpret_cast<XcvUiData*>(xcv_handle);
DWORD ret_val = ERROR_SUCCESS;
if ((xcv_data->granted_access & SERVER_ACCESS_ADMINISTER) == 0) {
return ERROR_ACCESS_DENIED;
}
if (output_data == NULL || output_data_bytes == 0) {
return ERROR_INVALID_PARAMETER;
}
if (lstrcmp(L"MonitorUI", data_name) == 0) {
DWORD dll_path_len = 0;
base::FilePath dll_path(GetPortMonitorDllName());
dll_path_len = static_cast<DWORD>(dll_path.value().length());
if (output_data_bytes_needed != NULL) {
*output_data_bytes_needed = dll_path_len;
}
if (output_data_bytes < dll_path_len) {
return ERROR_INSUFFICIENT_BUFFER;
} else {
ret_val = StringCbCopy(reinterpret_cast<wchar_t*>(output_data),
output_data_bytes,
dll_path.value().c_str());
}
} else {
return ERROR_INVALID_PARAMETER;
}
return ret_val;
}
BOOL WINAPI Monitor2XcvClosePort(HANDLE handle) {
XcvUiData* xcv_data = reinterpret_cast<XcvUiData*>(handle);
delete xcv_data;
return TRUE;
}
BOOL WINAPI MonitorUiAddPortUi(const wchar_t*,
HWND hwnd,
const wchar_t* monitor_name,
wchar_t**) {
HandlePortUi(hwnd, monitor_name);
return TRUE;
}
BOOL WINAPI MonitorUiConfigureOrDeletePortUI(const wchar_t*,
HWND hwnd,
const wchar_t* port_name) {
HandlePortUi(hwnd, port_name);
return TRUE;
}
}
MONITOR2* WINAPI InitializePrintMonitor2(MONITORINIT*,
HANDLE* handle) {
if (handle == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
cloud_print::MonitorData* monitor_data = new cloud_print::MonitorData;
*handle = monitor_data;
if (!cloud_print::kIsUnittest) {
monitor_data->at_exit_manager.reset(new base::AtExitManager());
PathService::DisableCache();
}
return &cloud_print::g_monitor_2;
}
MONITORUI* WINAPI InitializePrintMonitorUI(void) {
return &cloud_print::g_monitor_ui;
}