diff options
author | abeera@google.com <abeera@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-08-19 04:14:58 +0000 |
---|---|---|
committer | abeera@google.com <abeera@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-08-19 04:14:58 +0000 |
commit | 23a96d057f6f59497b27050e95d2d01699cf8981 (patch) | |
tree | 448c8d3e765e5cff7b9b93a919dbc981e3472dc8 /cloud_print/virtual_driver | |
parent | 64cecfd77eaa9c61a1d9039fe4b1f2387aaab3cb (diff) | |
download | chromium_src-23a96d057f6f59497b27050e95d2d01699cf8981.zip chromium_src-23a96d057f6f59497b27050e95d2d01699cf8981.tar.gz chromium_src-23a96d057f6f59497b27050e95d2d01699cf8981.tar.bz2 |
Simplify the Posix Cloud Print virtual driver, add print ticket support for Mac and Linux.
BUG=NONE
TEST= NONE
Review URL: http://codereview.chromium.org/7612004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@97423 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'cloud_print/virtual_driver')
9 files changed, 253 insertions, 84 deletions
diff --git a/cloud_print/virtual_driver/posix/backend.gyp b/cloud_print/virtual_driver/posix/backend.gyp index df6e49b..ae65801 100644 --- a/cloud_print/virtual_driver/posix/backend.gyp +++ b/cloud_print/virtual_driver/posix/backend.gyp @@ -15,10 +15,12 @@ ], 'sources': [ 'printer_driver_util_linux.cc', + 'printer_driver_util_posix.cc', 'printer_driver_util_posix.h', 'printer_driver_util_mac.mm', 'virtual_driver_posix.cc', '../virtual_driver_switches.cc', + '../virtual_driver_switches.h', ], 'conditions': [ ['OS=="mac"', { @@ -26,6 +28,20 @@ 'libraries': ['ScriptingBridge.framework'], }], ], + }, + { + 'target_name': 'virtual_driver_posix_unittests', + 'type': 'executable', + 'dependencies': [ + '../../../base/base.gyp:base', + '../../../base/base.gyp:test_support_base', + '../../../testing/gmock.gyp:gmock', + '../../../testing/gtest.gyp:gtest', + ], + 'sources': [ + 'virtual_driver_posix_tests.cc', + 'printer_driver_util_posix.cc', + ], }], 'conditions': [ ['OS=="mac"', { diff --git a/cloud_print/virtual_driver/posix/printer_driver_util_linux.cc b/cloud_print/virtual_driver/posix/printer_driver_util_linux.cc index 81014f8..b7e70cf 100644 --- a/cloud_print/virtual_driver/posix/printer_driver_util_linux.cc +++ b/cloud_print/virtual_driver/posix/printer_driver_util_linux.cc @@ -13,9 +13,12 @@ #include "cloud_print/virtual_driver/posix/printer_driver_util_posix.h" #include "cloud_print/virtual_driver/virtual_driver_switches.h" +namespace printer_driver_util { + void LaunchPrintDialog(const std::string& output_path, const std::string& job_title, - const std::string& current_user) { + const std::string& current_user, + const std::string& print_ticket) { std::string set_var; // Set Environment variable to control display. @@ -28,15 +31,15 @@ void LaunchPrintDialog(const std::string& output_path, } // Construct the call to Chrome - FilePath chrome_path("google-chrome"); FilePath job_path(output_path); CommandLine command_line(chrome_path); command_line.AppendSwitchPath(switches::kCloudPrintFile, job_path); command_line.AppendSwitchNative(switches::kCloudPrintJobTitle, job_title); command_line.AppendSwitch(switches::kCloudPrintDeleteFile); + command_line.AppendSwitchNative(switches::kCloudPrintPrintTicket, + print_ticket); LOG(INFO) << "Call to chrome is " << command_line.GetCommandLineString(); - if (system(command_line.GetCommandLineString().c_str()) == -1) { LOG(ERROR) << "Unable to call Chrome"; exit(CUPS_BACKEND_CANCEL); @@ -44,3 +47,5 @@ void LaunchPrintDialog(const std::string& output_path, LOG(INFO) << "Call to Chrome succeeded"; } +} // namespace printer_driver_util + diff --git a/cloud_print/virtual_driver/posix/printer_driver_util_mac.mm b/cloud_print/virtual_driver/posix/printer_driver_util_mac.mm index d85e3ff..b17788d 100644 --- a/cloud_print/virtual_driver/posix/printer_driver_util_mac.mm +++ b/cloud_print/virtual_driver/posix/printer_driver_util_mac.mm @@ -34,9 +34,11 @@ bool IsBrowserRunning(std::string bundleID) { } } // namespace cloud_print +namespace printer_driver_util { void LaunchPrintDialog(const std::string& outputPath, const std::string& jobTitle, - const std::string& user) { + const std::string& user, + const std::string& print_ticket) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; OSStatus status = noErr; FSRef ref; @@ -84,6 +86,9 @@ void LaunchPrintDialog(const std::string& outputPath, [NSString stringWithUTF8String:jobTitle.c_str()]]; NSAppleEventDescriptor* mime = [NSAppleEventDescriptor descriptorWithString:@"application/pdf"]; + NSAppleEventDescriptor* ticket = + [NSAppleEventDescriptor descriptorWithString: + [NSString stringWithUTF8String:print_ticket.c_str()]]; // Create and populate the list of parameters. // Note that the array starts at index 1. @@ -97,6 +102,7 @@ void LaunchPrintDialog(const std::string& outputPath, [parameters insertDescriptor:mime atIndex:1]; [parameters insertDescriptor:printPath atIndex:2]; [parameters insertDescriptor:title atIndex:3]; + [parameters insertDescriptor:ticket atIndex:4]; [event setParamDescriptor:parameters forKeyword:kAECloudPrintClass]; // Set the application launch parameters. @@ -118,3 +124,5 @@ void LaunchPrintDialog(const std::string& outputPath, [pool release]; return; } + +} // namespace printer_driver_util diff --git a/cloud_print/virtual_driver/posix/printer_driver_util_posix.cc b/cloud_print/virtual_driver/posix/printer_driver_util_posix.cc new file mode 100644 index 0000000..d78f632 --- /dev/null +++ b/cloud_print/virtual_driver/posix/printer_driver_util_posix.cc @@ -0,0 +1,99 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <pwd.h> +#include <stdio.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include <cups/backend.h> + +#include "base/json/json_writer.h" +#include "base/logging.h" +#include "base/string_tokenizer.h" +#include "base/values.h" + +#include "cloud_print/virtual_driver/posix/printer_driver_util_posix.h" + +namespace printer_driver_util { + +void WriteToTemp(FILE* input_pdf, FilePath output_path) { + FILE* output_pdf; + char buffer[128]; + output_pdf = fopen(output_path.value().c_str(), "w"); + if (output_pdf == NULL) { + LOG(ERROR) << "Unable to open file handle for writing output file"; + exit(CUPS_BACKEND_CANCEL); + } + // Read from input file or stdin and write to output file. + while (fgets(buffer, sizeof(buffer), input_pdf) != NULL) { + fputs(buffer, output_pdf); + } + + LOG(INFO) << "Successfully wrote output file"; + // ensure everything is written, then close the files. + fflush(output_pdf); + fclose(input_pdf); + fclose(output_pdf); +} + +// Sets the UID of the process to that of the username. +void SetUser(const char* user) { + struct passwd* calling_user = NULL; + calling_user = getpwnam(user); + if (calling_user == NULL) { + LOG(ERROR) << "Unable to get calling user"; + exit(CUPS_BACKEND_CANCEL); + } + if (!setuid(calling_user->pw_uid) == -1) { + LOG(ERROR) << "Unable to set UID"; + exit(CUPS_BACKEND_CANCEL); + } + + LOG(INFO) << "Successfully set user and group ID"; +} + +// Parses the options passed in on the command line to key value +// JSON pairs. Assumes that the input options string is of the +// format KEY=VALUE, with expressions being separated by spaces. +// Fails if print_ticket cannot be written to. +void GetOptions(const char* options, std::string* print_ticket) { + if (options == NULL) { + *(print_ticket) = "{}"; + return; + } + CStringTokenizer t(options, options + strlen(options), " "); + DictionaryValue* json_options = new DictionaryValue; + + while (t.GetNext()) { + std::string token = t.token(); + // If the token ends with a slash, that indicates + // that the next space is actually escaped + // So we append the next token onto this token + // if possible. We also replace the slash with a space + // since the JSON will expect an unescaped string. + while (token.at(token.length()-1) == '\\') { + if (t.GetNext()) { + token.replace(token.length()-1, 1, " "); + token.append(t.token()); + } else { + break; + } + } + size_t pos = token.find("="); + if (pos == std::string::npos) { + continue; + } + std::string option_name = token.substr(0, pos); + std::string option_value = token.substr(pos+1); + base::StringValue* val= base::Value::CreateStringValue(option_value); + json_options->SetWithoutPathExpansion(option_name, val); + } + base::JSONWriter::Write(json_options, /*pretty_print=*/false, print_ticket); + delete json_options; +} + +} // namespace printer_driver_util + diff --git a/cloud_print/virtual_driver/posix/printer_driver_util_posix.h b/cloud_print/virtual_driver/posix/printer_driver_util_posix.h index 821802b..8631f07 100644 --- a/cloud_print/virtual_driver/posix/printer_driver_util_posix.h +++ b/cloud_print/virtual_driver/posix/printer_driver_util_posix.h @@ -7,8 +7,18 @@ #include <string> +#include "base/file_path.h" + +namespace printer_driver_util { + void LaunchPrintDialog(const std::string& output_path, const std::string& job_title, - const std::string& current_user); + const std::string& current_user, + const std::string& print_ticket); +void WriteToTemp(FILE* input_pdf, FilePath output_path); +void SetUser(const char* user); +void GetOptions(const char* options, std::string* print_ticket); + +} // namespace printer_driver_util #endif // CLOUD_PRINT_VIRTUAL_DRIVER_POSIX_PRINTER_DRIVER_UTIL_POSIX_H_ diff --git a/cloud_print/virtual_driver/posix/virtual_driver_posix.cc b/cloud_print/virtual_driver/posix/virtual_driver_posix.cc index ec0a73d..aac6299 100644 --- a/cloud_print/virtual_driver/posix/virtual_driver_posix.cc +++ b/cloud_print/virtual_driver/posix/virtual_driver_posix.cc @@ -2,12 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <pwd.h> -#include <stdio.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/wait.h> - #include <cups/backend.h> #include "base/at_exit.h" @@ -19,63 +13,28 @@ #include "cloud_print/virtual_driver/posix/printer_driver_util_posix.h" -void WriteToTemp(FILE* input_pdf, FilePath output_path) { - FILE* output_pdf; - char buffer[128]; - output_pdf = fopen(output_path.value().c_str(), "w"); - if (output_pdf == NULL) { - LOG(ERROR) << "Unable to open file handle for writing output file"; - exit(CUPS_BACKEND_CANCEL); - } - // Read from input file or stdin and write to output file. - while (fgets(buffer, sizeof(buffer), input_pdf) != NULL) { - fputs(buffer, output_pdf); - } - - LOG(INFO) << "Successfully wrote output file"; - // ensure everything is written, then close the files. - fflush(output_pdf); - fclose(input_pdf); - fclose(output_pdf); -} - -void SetUser(const char* user) { - struct passwd* calling_user = NULL; - calling_user = getpwnam(user); - if (calling_user == NULL) { - LOG(ERROR) << "Unable to get calling user"; - exit(CUPS_BACKEND_CANCEL); - } - if (setgid(calling_user->pw_gid) == -1) { - LOG(ERROR) << "Unable to set group ID"; - exit(CUPS_BACKEND_CANCEL); - } - if (!setuid(calling_user->pw_uid) == -1) { - LOG(ERROR) << "Unable to set UID"; - exit(CUPS_BACKEND_CANCEL); - } - - LOG(INFO) << "Successfully set user and group ID"; -} - - - // Main function for backend. int main(int argc, const char* argv[]) { // With no arguments, send identification string as required by CUPS. if (argc == 1) { printf("file GCP-driver:/ \"GCP Virtual Driver\" \"GCP Virtual Driver\" " - "\"MFG:Google Inc.;MDL:GCP Virtual Driver;DES:GCP Virtual Driver;" - "CLS:PRINTER;CMD:POSTSCRIPT;\"\n"); + "\"MFG:Google Inc.;MDL:GCP Virtual Driver;DES:GCP Virtual Driver;" + "CLS:PRINTER;CMD:POSTSCRIPT;\"\n"); return 0; } if (argc < 6 || argc > 7) { - fprintf(stderr, "Usage: GCP-Driver job-id user" - "title copies options [file]\n"); + fprintf(stderr, "Usage: GCP-Driver job-id user " + "title copies options [file]\n"); return 0; } + // We can run the backend as root or unpriveliged user lp + // Since we want to launch the printer dialog as the user + // that initiated the print job, we run the backend as root + // and then setuid to the user that started the print job + printer_driver_util::SetUser(argv[2]); + // AtExitManager is necessary to use the path service. base::AtExitManager aemanager; // CommandLine is only setup to enable logging, never used subseqeuently. @@ -85,10 +44,10 @@ int main(int argc, const char* argv[]) { // Set up logging. logging::InitLogging(log_location.c_str(), - logging::LOG_ONLY_TO_FILE, - logging::LOCK_LOG_FILE, - logging::APPEND_TO_OLD_LOG_FILE, - logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS); + logging::LOG_ONLY_TO_FILE, + logging::LOCK_LOG_FILE, + logging::APPEND_TO_OLD_LOG_FILE, + logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS); // Temporary directory to hold the output file. FilePath temp_dir; @@ -98,6 +57,7 @@ int main(int argc, const char* argv[]) { std::string job_title; std::string job_id; std::string file_name; + std::string print_ticket; // Get temp directory to hold spool file. if (!PathService::Get(base::DIR_TEMP, &temp_dir)) { @@ -108,9 +68,15 @@ int main(int argc, const char* argv[]) { current_user = argv[2]; job_title = argv[3]; job_id = argv[1]; + printer_driver_util::GetOptions(argv[5], &print_ticket); file_name = current_user + "-" + job_title + "-" + job_id; FilePath output_path = temp_dir.Append(file_name); + // However, the input file can only be read as root. + if (!setuid(0)) { + PLOG(ERROR) << "Unable to setuid back to 0"; + } + if (argc == 7) { // Read from file if specified. FILE* input_pdf = fopen(argv[6], "r"); @@ -119,34 +85,18 @@ int main(int argc, const char* argv[]) { return CUPS_BACKEND_CANCEL; } // File is opened. - WriteToTemp(input_pdf, output_path); + printer_driver_util::WriteToTemp(input_pdf, output_path); } else { // Otherwise, read from stdin. - WriteToTemp(stdin, output_path); - } - - // Set File permissions to allow non-sudo user to read spool file. - if (chmod(output_path.value().c_str(), - S_IRUSR|S_IWUSR|S_IROTH|S_IWOTH) != 0) { - LOG(ERROR) << "Unable to change file permissions"; - return CUPS_BACKEND_CANCEL; + printer_driver_util::WriteToTemp(stdin, output_path); } - pid_t pid = fork(); - - if (!pid) { - // In child process. - - // Set the user to the one that initiated print job. - SetUser(argv[2]); + // Change back to user to launch Chrome. + printer_driver_util::SetUser(argv[2]); + PLOG(ERROR) << print_ticket; + // Launch Chrome and pass the print job onto Cloud Print. + printer_driver_util::LaunchPrintDialog(output_path.value(), job_title, + current_user, print_ticket); - // Launch Chrome and pass the print job onto Cloud Print. - LaunchPrintDialog(output_path.value(), job_title, current_user); - - return 0; - } - // back in parent process. - // wait for child, then terminate. - waitpid(pid, NULL, 0); return 0; } diff --git a/cloud_print/virtual_driver/posix/virtual_driver_posix_tests.cc b/cloud_print/virtual_driver/posix/virtual_driver_posix_tests.cc new file mode 100644 index 0000000..34869af --- /dev/null +++ b/cloud_print/virtual_driver/posix/virtual_driver_posix_tests.cc @@ -0,0 +1,75 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cloud_print/virtual_driver/posix/printer_driver_util_posix.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace printer_driver_util { + +void test_helper(const char* input, std::string expected_output) { + std::string test; + GetOptions(input, &test); + EXPECT_EQ(expected_output, test); +} + +TEST(PrintTicketTest, HandlesEmpty) { + test_helper("", "{}"); +} + +TEST(PrintTicketTest, HandlesNull) { + test_helper(NULL, "{}"); +} + +TEST(PrintTicketTest, HandlesOneOption) { + test_helper("Resolution=500", "{\"Resolution\":\"500\"}"); +} + +TEST(PrintTicketTest, HandlesMutipleOptions) { + test_helper("Resolution=500 DPI=1100", + "{\"DPI\":\"1100\",\"Resolution\":\"500\"}"); +} + +TEST(PrintTicketTest, HandlesErrorInOptions) { + test_helper("Resolution=500 Foo DPI=1100", + "{\"DPI\":\"1100\",\"Resolution\":\"500\"}"); +} + +TEST(PrintTicketTest, HandlesMutipleSpaces) { + test_helper("Resolution=500 DPI=1100", + "{\"DPI\":\"1100\",\"Resolution\":\"500\"}"); +} + +TEST(PrintTicketTest, HandlesEscapedSpace) { + test_helper("Job\\ Owner=First\\ Last", + "{\"Job Owner\":\"First Last\"}"); +} + +TEST(PrintTicketTest, HandlesMultipleEscapedWords) { + test_helper("Job\\ Owner\\ Name=First\\ Last", + "{\"Job Owner Name\":\"First Last\"}"); +} + +TEST(PrintTicketTest, HandlesMultipleEscapedSpaces) { + test_helper("Job\\ Owner\\ \\ Name=First\\ Last", + "{\"Job Owner Name\":\"First Last\"}"); +} + +TEST(PrintTicketTest, HandlesKeyEndsInEscapedSpace) { + test_helper("Job\\ Owner\\ =First\\ Last", + "{\"Job Owner \":\"First Last\"}"); +} + +TEST(PrintTicketTest, HandlesSlashAtEnd) { + test_helper("Job\\ Owner=First\\ Last\\", + "{\"Job Owner\":\"First Last\\\\\"}"); +} + + +} // namespace printer_driver_util + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + diff --git a/cloud_print/virtual_driver/virtual_driver_switches.cc b/cloud_print/virtual_driver/virtual_driver_switches.cc index 72114bf..e8bd347 100644 --- a/cloud_print/virtual_driver/virtual_driver_switches.cc +++ b/cloud_print/virtual_driver/virtual_driver_switches.cc @@ -9,5 +9,6 @@ const char kCloudPrintDeleteFile[] = "cloud-print-delete-file"; const char kCloudPrintFile[] = "cloud-print-file"; const char kCloudPrintJobTitle[] = "cloud-print-job-title"; const char kCloudPrintFileType[] = "cloud-print-file-type"; +const char kCloudPrintPrintTicket[] = "cloud-print-print-ticket"; } // namespace switches diff --git a/cloud_print/virtual_driver/virtual_driver_switches.h b/cloud_print/virtual_driver/virtual_driver_switches.h index f30d9d1..54db54c 100644 --- a/cloud_print/virtual_driver/virtual_driver_switches.h +++ b/cloud_print/virtual_driver/virtual_driver_switches.h @@ -28,6 +28,11 @@ extern const char kCloudPrintJobTitle[]; // file referenced by cloud-print-file. // Defaults to "application/pdf" if unspecified. extern const char kCloudPrintFileType[]; + +// Used with kCloudPrintFile to specify a JSON print ticket for the resulting +// print job. +// Defaults to null if unspecified. +extern const char kCloudPrintPrintTicket[]; } // namespace switches #endif // CLOUD_PRINT_VIRTUAL_DRIVER_VIRTUAL_DRIVER_SWITCHES_H_ |