Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(764)

Side by Side Diff: chrome/service/cloud_print/print_system_cups.cc

Issue 2519002: Redesign cloud printing subsystem layer. (Closed) Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: '' Created 10 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/service/cloud_print/printer_info.h" 5 #include "chrome/service/cloud_print/print_system.h"
6 6
7 #include <cups/cups.h> 7 #include <cups/cups.h>
8 #include <list> 8 #include <list>
9 #include <map> 9 #include <map>
10 10
11 #include "base/json/json_reader.h" 11 #include "base/json/json_reader.h"
12 #include "base/file_path.h" 12 #include "base/file_path.h"
13 #include "base/file_util.h" 13 #include "base/file_util.h"
14 #include "base/logging.h" 14 #include "base/logging.h"
15 #include "base/lock.h" 15 #include "base/lock.h"
16 #include "base/message_loop.h" 16 #include "base/message_loop.h"
17 #include "base/rand_util.h" 17 #include "base/rand_util.h"
18 #include "base/string_util.h" 18 #include "base/string_util.h"
19 #include "base/task.h" 19 #include "base/task.h"
20 #include "base/values.h" 20 #include "base/values.h"
21 #include "base/utf_string_conversions.h" 21 #include "base/utf_string_conversions.h"
22 22
23 namespace cloud_print { 23 namespace cloud_print {
24 24
25 static const char kCUPSPrinterInfoOpt[] = "printer-info"; 25 static const char kCUPSPrinterInfoOpt[] = "printer-info";
26 static const char kCUPSPrinterStateOpt[] = "printer-state"; 26 static const char kCUPSPrinterStateOpt[] = "printer-state";
27 27
28 void EnumeratePrinters(PrinterList* printer_list) { 28 class PrintSystemCUPS : public PrintSystem {
29 public:
30 virtual void EnumeratePrinters(PrinterList* printer_list);
31
32 virtual bool GetPrinterCapsAndDefaults(const std::string& printer_name,
33 PrinterCapsAndDefaults* printer_info);
34
35 virtual bool ValidatePrintTicket(const std::string& printer_name,
36 const std::string& print_ticket_data);
37
38 virtual bool SpoolPrintJob(const std::string& print_ticket,
39 const FilePath& print_data_file_path,
40 const std::string& print_data_mime_type,
41 const std::string& printer_name,
42 const std::string& job_title,
43 PlatformJobId* job_id_ret);
44
45 virtual bool GetJobDetails(const std::string& printer_name,
46 PlatformJobId job_id,
47 PrintJobDetails *job_details);
48
49 virtual bool IsValidPrinter(const std::string& printer_name);
50
51 // TODO(gene): Add implementation for CUPS print server watcher.
52 class PrintServerWatcherCUPS
53 : public PrintSystem::PrintServerWatcher {
54 public:
55 PrintServerWatcherCUPS() {}
56
57 // PrintSystem::PrintServerWatcher interface
58 virtual bool StartWatching(
59 PrintSystem::PrintServerWatcher::Delegate* delegate) {
60 NOTIMPLEMENTED();
61 return true;
62 }
63 virtual bool StopWatching() {
64 NOTIMPLEMENTED();
65 return true;
66 }
67 };
68
69 class PrinterWatcherCUPS
70 : public PrintSystem::PrinterWatcher {
71 public:
72 explicit PrinterWatcherCUPS(PrintSystemCUPS* print_system,
73 const std::string& printer_name)
74 : printer_name_(printer_name), print_system_(print_system) {
75 }
76
77 // PrintSystem::PrinterWatcher interface
78 virtual bool StartWatching(
79 PrintSystem::PrinterWatcher::Delegate* delegate) {
80 if (delegate_ != NULL)
81 StopWatching();
82 delegate_ = delegate;
83 MessageLoop::current()->PostDelayedTask(FROM_HERE,
84 NewRunnableMethod(this,
85 &PrintSystemCUPS::PrinterWatcherCUPS::Update), 5000);
86 return true;
87 }
88 virtual bool StopWatching() {
89 delegate_ = NULL;
90 return true;
91 }
92 bool GetCurrentPrinterInfo(PrinterBasicInfo* printer_info) {
93 DCHECK(printer_info);
94 return print_system_->GetPrinterInfo(printer_name_, printer_info);
95 }
96
97 void Update() {
98 if (delegate_ == NULL)
99 return; // Orphan call. We have been stopped already.
100 // For CUPS proxy, we are going to fire OnJobChanged notification
101 // periodically. Higher level will check if there are any outstanding
102 // jobs for this printer and check their status. If printer has no
103 // outstanding jobs, OnJobChanged() will do nothing.
104 delegate_->OnJobChanged();
105 MessageLoop::current()->PostDelayedTask(FROM_HERE,
106 NewRunnableMethod(this,
107 &PrintSystemCUPS::PrinterWatcherCUPS::Update),
108 kNotificationTimeout);
109 }
110 private:
111 static const int kNotificationTimeout = 5000; // in ms
112 std::string printer_name_;
113 PrintSystem::PrinterWatcher::Delegate* delegate_;
114 scoped_refptr<PrintSystemCUPS> print_system_;
115 DISALLOW_COPY_AND_ASSIGN(PrinterWatcherCUPS);
116 };
117
118 virtual PrintSystem::PrintServerWatcher* CreatePrintServerWatcher();
119 virtual PrintSystem::PrinterWatcher* CreatePrinterWatcher(
120 const std::string& printer_name);
121
122 // Helper functions.
123 bool GetPrinterInfo(const std::string& printer_name, PrinterBasicInfo* info);
124 bool ParsePrintTicket(const std::string& print_ticket,
125 std::map<std::string, std::string>* options);
126 };
127
128 void PrintSystemCUPS::EnumeratePrinters(PrinterList* printer_list) {
29 DCHECK(printer_list); 129 DCHECK(printer_list);
30 printer_list->clear(); 130 printer_list->clear();
31 131
32 cups_dest_t* destinations = NULL; 132 cups_dest_t* destinations = NULL;
33 int num_dests = cupsGetDests(&destinations); 133 int num_dests = cupsGetDests(&destinations);
34 134
35 for (int printer_index = 0; printer_index < num_dests; printer_index++) { 135 for (int printer_index = 0; printer_index < num_dests; printer_index++) {
36 const cups_dest_t& printer = destinations[printer_index]; 136 const cups_dest_t& printer = destinations[printer_index];
37 137
38 PrinterBasicInfo printer_info; 138 PrinterBasicInfo printer_info;
(...skipping 13 matching lines...) Expand all
52 for (int opt_index = 0; opt_index < printer.num_options; opt_index++) { 152 for (int opt_index = 0; opt_index < printer.num_options; opt_index++) {
53 printer_info.options[printer.options[opt_index].name] = 153 printer_info.options[printer.options[opt_index].name] =
54 printer.options[opt_index].value; 154 printer.options[opt_index].value;
55 } 155 }
56 156
57 printer_list->push_back(printer_info); 157 printer_list->push_back(printer_info);
58 } 158 }
59 159
60 cupsFreeDests(num_dests, destinations); 160 cupsFreeDests(num_dests, destinations);
61 161
62 DLOG(INFO) << "Enumerated " << printer_list->size() << " printers."; 162 DLOG(INFO) << "CP_CUPS: Enumerated " << printer_list->size() << " printers.";
63 } 163 }
64 164
65 bool GetPrinterCapsAndDefaults(const std::string& printer_name, 165 bool PrintSystemCUPS::GetPrinterCapsAndDefaults(const std::string& printer_name,
66 PrinterCapsAndDefaults* printer_info) { 166 PrinterCapsAndDefaults* printer_info) {
67 DCHECK(printer_info); 167 DCHECK(printer_info);
68 168
69 DLOG(INFO) << "Getting Caps and Defaults for: " << printer_name; 169 DLOG(INFO) << "CP_CUPS: Getting Caps and Defaults for: " << printer_name;
70 170
71 static Lock ppd_lock; 171 static Lock ppd_lock;
72 // cupsGetPPD returns a filename stored in a static buffer in CUPS. 172 // cupsGetPPD returns a filename stored in a static buffer in CUPS.
73 // Protect this code with lock. 173 // Protect this code with lock.
74 ppd_lock.Acquire(); 174 ppd_lock.Acquire();
75 FilePath ppd_path(cupsGetPPD(printer_name.c_str())); 175 FilePath ppd_path;
176 const char* ppd_file_path = cupsGetPPD(printer_name.c_str());
177 if (ppd_file_path)
178 ppd_path = FilePath(ppd_file_path);
76 ppd_lock.Release(); 179 ppd_lock.Release();
77 180
181 // In some cases CUPS failed to get ppd file.
182 if (ppd_path.empty()) {
183 DLOG(ERROR) << "CP_CUPS: Failed to get PPD for: " << printer_name;
184 return false;
185 }
186
78 std::string content; 187 std::string content;
79 bool res = file_util::ReadFileToString(ppd_path, &content); 188 bool res = file_util::ReadFileToString(ppd_path, &content);
80 189
81 file_util::Delete(ppd_path, false); 190 file_util::Delete(ppd_path, false);
82 191
83 if (res) { 192 if (res) {
84 printer_info->printer_capabilities.swap(content); 193 printer_info->printer_capabilities.swap(content);
85 printer_info->caps_mime_type = "application/pagemaker"; 194 printer_info->caps_mime_type = "application/pagemaker";
86 // In CUPS, printer defaults is a part of PPD file. Nothing to upload here. 195 // In CUPS, printer defaults is a part of PPD file. Nothing to upload here.
87 printer_info->printer_defaults.clear(); 196 printer_info->printer_defaults.clear();
88 printer_info->defaults_mime_type.clear(); 197 printer_info->defaults_mime_type.clear();
89 } 198 }
90 199
91 return res; 200 return res;
92 } 201 }
93 202
94 bool ValidatePrintTicket(const std::string& printer_name, 203 bool PrintSystemCUPS::ValidatePrintTicket(const std::string& printer_name,
95 const std::string& print_ticket_data) { 204 const std::string& print_ticket_data) {
96 scoped_ptr<Value> ticket_value(base::JSONReader::Read(print_ticket_data, 205 scoped_ptr<Value> ticket_value(base::JSONReader::Read(print_ticket_data,
97 false)); 206 false));
98 return ticket_value != NULL && ticket_value->IsType(Value::TYPE_DICTIONARY); 207 return ticket_value != NULL && ticket_value->IsType(Value::TYPE_DICTIONARY);
99 } 208 }
100 209
101 std::string GenerateProxyId() {
102 // TODO(gene): This code should generate a unique id for proxy. ID should be
103 // unique for this user. Rand may return the same number. We'll need to change
104 // this in the future.
105 std::string id("CP_PROXY_");
106 id += Uint64ToString(base::RandUint64());
107 return id;
108 }
109
110 // Print ticket on linux is a JSON string containing only one dictionary. 210 // Print ticket on linux is a JSON string containing only one dictionary.
111 bool ParsePrintTicket(const std::string& print_ticket, 211 bool PrintSystemCUPS::ParsePrintTicket(const std::string& print_ticket,
112 std::map<std::string, std::string>* options) { 212 std::map<std::string, std::string>* options) {
113 DCHECK(options); 213 DCHECK(options);
114 scoped_ptr<Value> ticket_value(base::JSONReader::Read(print_ticket, false)); 214 scoped_ptr<Value> ticket_value(base::JSONReader::Read(print_ticket, false));
115 if (ticket_value == NULL || !ticket_value->IsType(Value::TYPE_DICTIONARY)) 215 if (ticket_value == NULL || !ticket_value->IsType(Value::TYPE_DICTIONARY))
116 return false; 216 return false;
117 217
118 options->clear(); 218 options->clear();
119 DictionaryValue* ticket_dict = 219 DictionaryValue* ticket_dict =
120 static_cast<DictionaryValue*>(ticket_value.get()); 220 static_cast<DictionaryValue*>(ticket_value.get());
121 DictionaryValue::key_iterator it(ticket_dict->begin_keys()); 221 DictionaryValue::key_iterator it(ticket_dict->begin_keys());
122 for (; it != ticket_dict->end_keys(); ++it) { 222 for (; it != ticket_dict->end_keys(); ++it) {
123 std::wstring key = *it; 223 std::wstring key = *it;
124 std::string value; 224 std::string value;
125 if (ticket_dict->GetString(key, &value)) { 225 if (ticket_dict->GetString(key, &value)) {
126 (*options)[WideToUTF8(key.c_str())] = value; 226 (*options)[WideToUTF8(key.c_str())] = value;
127 } 227 }
128 } 228 }
129 229
130 return true; 230 return true;
131 } 231 }
132 232
133 bool SpoolPrintJob(const std::string& print_ticket, 233 bool PrintSystemCUPS::SpoolPrintJob(const std::string& print_ticket,
134 const FilePath& print_data_file_path, 234 const FilePath& print_data_file_path,
135 const std::string& print_data_mime_type, 235 const std::string& print_data_mime_type,
136 const std::string& printer_name, 236 const std::string& printer_name,
137 const std::string& job_title, 237 const std::string& job_title,
138 PlatformJobId* job_id_ret) { 238 PlatformJobId* job_id_ret) {
139 DCHECK(job_id_ret); 239 DCHECK(job_id_ret);
140 240
141 DLOG(INFO) << "Spooling print job for: " << printer_name; 241 DLOG(INFO) << "CP_CUPS: Spooling print job for: " << printer_name;
142 242
143 // We need to store options as char* string for the duration of the 243 // We need to store options as char* string for the duration of the
144 // cupsPrintFile call. We'll use map here to store options, since 244 // cupsPrintFile call. We'll use map here to store options, since
145 // Dictionary value from JSON parser returns wchat_t. 245 // Dictionary value from JSON parser returns wchat_t.
146 std::map<std::string, std::string> options; 246 std::map<std::string, std::string> options;
147 bool res = ParsePrintTicket(print_ticket, &options); 247 bool res = ParsePrintTicket(print_ticket, &options);
148 DCHECK(res); // If print ticket is invalid we still print using defaults. 248 DCHECK(res); // If print ticket is invalid we still print using defaults.
149 249
150 std::vector<cups_option_t> cups_options; 250 std::vector<cups_option_t> cups_options;
151 std::map<std::string, std::string>::iterator it; 251 std::map<std::string, std::string>::iterator it;
152 for (it = options.begin(); it != options.end(); ++it) { 252 for (it = options.begin(); it != options.end(); ++it) {
153 cups_option_t opt; 253 cups_option_t opt;
154 opt.name = const_cast<char*>(it->first.c_str()); 254 opt.name = const_cast<char*>(it->first.c_str());
155 opt.value = const_cast<char*>(it->second.c_str()); 255 opt.value = const_cast<char*>(it->second.c_str());
156 cups_options.push_back(opt); 256 cups_options.push_back(opt);
157 } 257 }
158 258
159 int job_id = cupsPrintFile(printer_name.c_str(), 259 int job_id = cupsPrintFile(printer_name.c_str(),
160 print_data_file_path.value().c_str(), 260 print_data_file_path.value().c_str(),
161 job_title.c_str(), 261 job_title.c_str(),
162 cups_options.size(), 262 cups_options.size(),
163 &(cups_options[0])); 263 &(cups_options[0]));
164 264
265 DLOG(INFO) << "CP_CUPS: Job spooled, id: " << job_id;
266
165 if (job_id == 0) 267 if (job_id == 0)
166 return false; 268 return false;
167 269
168 *job_id_ret = job_id; 270 *job_id_ret = job_id;
169 return true; 271 return true;
170 } 272 }
171 273
172 bool GetJobDetails(const std::string& printer_name, 274 bool PrintSystemCUPS::GetJobDetails(const std::string& printer_name,
173 PlatformJobId job_id, 275 PlatformJobId job_id,
174 PrintJobDetails *job_details) { 276 PrintJobDetails *job_details) {
175 DCHECK(job_details); 277 DCHECK(job_details);
176 278
177 DLOG(INFO) << "Getting job details for: " << printer_name << 279 DLOG(INFO) << "CP_CUPS: Getting job details for: " << printer_name <<
178 " job_id: " << job_id; 280 " job_id: " << job_id;
179 281
180 cups_job_t* jobs = NULL; 282 cups_job_t* jobs = NULL;
181 int num_jobs = cupsGetJobs(&jobs, printer_name.c_str(), 1, -1); 283 int num_jobs = cupsGetJobs(&jobs, printer_name.c_str(), 1, -1);
182 284
183 bool found = false; 285 bool found = false;
184 for (int i = 0; i < num_jobs; i++) { 286 for (int i = 0; i < num_jobs; i++) {
185 if (jobs[i].id == job_id) { 287 if (jobs[i].id == job_id) {
186 found = true; 288 found = true;
187 switch (jobs[i].state) { 289 switch (jobs[i].state) {
(...skipping 17 matching lines...) Expand all
205 307
206 // We don't have any details on the number of processed pages here. 308 // We don't have any details on the number of processed pages here.
207 break; 309 break;
208 } 310 }
209 } 311 }
210 312
211 cupsFreeJobs(num_jobs, jobs); 313 cupsFreeJobs(num_jobs, jobs);
212 return found; 314 return found;
213 } 315 }
214 316
215 bool GetPrinterInfo(const std::string& printer_name, PrinterBasicInfo* info) { 317 bool PrintSystemCUPS::GetPrinterInfo(const std::string& printer_name,
318 PrinterBasicInfo* info) {
216 DCHECK(info); 319 DCHECK(info);
217 320
218 DLOG(INFO) << "Getting printer info for: " << printer_name; 321 DLOG(INFO) << "CP_CUPS: Getting printer info for: " << printer_name;
219 322
220 // This is not very efficient way to get specific printer info. CUPS 1.4 323 // This is not very efficient way to get specific printer info. CUPS 1.4
221 // supports cupsGetNamedDest() function. However, CUPS 1.4 is not available 324 // supports cupsGetNamedDest() function. However, CUPS 1.4 is not available
222 // everywhere (for example, it supported from Mac OS 10.6 only). 325 // everywhere (for example, it supported from Mac OS 10.6 only).
223 PrinterList printer_list; 326 PrinterList printer_list;
224 EnumeratePrinters(&printer_list); 327 EnumeratePrinters(&printer_list);
225 328
226 PrinterList::iterator it; 329 PrinterList::iterator it;
227 for (it = printer_list.begin(); it != printer_list.end(); ++it) { 330 for (it = printer_list.begin(); it != printer_list.end(); ++it) {
228 if (it->printer_name == printer_name) { 331 if (it->printer_name == printer_name) {
229 *info = *it; 332 *info = *it;
230 return true; 333 return true;
231 } 334 }
232 } 335 }
233 return false; 336 return false;
234 } 337 }
235 338
236 bool IsValidPrinter(const std::string& printer_name) { 339 bool PrintSystemCUPS::IsValidPrinter(const std::string& printer_name) {
237 PrinterBasicInfo info; 340 PrinterBasicInfo info;
238 return GetPrinterInfo(printer_name, &info); 341 return GetPrinterInfo(printer_name, &info);
239 } 342 }
240 343
241 class PrinterChangeNotifier::NotificationState 344 PrintSystem::PrintServerWatcher*
242 : public base::RefCountedThreadSafe< 345 PrintSystemCUPS::CreatePrintServerWatcher() {
243 PrinterChangeNotifier::NotificationState> { 346 return new PrintServerWatcherCUPS();
244 public:
245 NotificationState() : delegate_(NULL) {}
246 bool Start(const std::string& printer_name,
247 PrinterChangeNotifier::Delegate* delegate) {
248 if (delegate_ != NULL)
249 Stop();
250
251 printer_name_ = printer_name;
252 delegate_ = delegate;
253
254 MessageLoop::current()->PostDelayedTask(FROM_HERE,
255 NewRunnableMethod(this,
256 &PrinterChangeNotifier::NotificationState::Update), 5000);
257 return true;
258 }
259 bool Stop() {
260 delegate_ = NULL;
261 return true;
262 }
263 void Update() {
264 if (delegate_ == NULL)
265 return; // Orphan call. We have been stopped already.
266 // For CUPS proxy, we are going to fire OnJobChanged notification
267 // periodically. Higher level will check if there are any outstanding
268 // jobs for this printer and check their status. If printer has no
269 // outstanding jobs, OnJobChanged() will do nothing.
270 delegate_->OnJobChanged();
271 MessageLoop::current()->PostDelayedTask(FROM_HERE,
272 NewRunnableMethod(this,
273 &PrinterChangeNotifier::NotificationState::Update),
274 kNotificationTimeout);
275 }
276 std::string printer_name() const {
277 return printer_name_;
278 }
279 private:
280 friend class base::RefCountedThreadSafe<
281 PrinterChangeNotifier::NotificationState>;
282 ~NotificationState() {
283 Stop();
284 }
285 static const int kNotificationTimeout = 5000; // in ms
286 std::string printer_name_; // The printer being watched
287 PrinterChangeNotifier::Delegate* delegate_; // Delegate to notify
288 };
289
290 PrinterChangeNotifier::PrinterChangeNotifier() : state_(NULL) {
291 } 347 }
292 348
293 PrinterChangeNotifier::~PrinterChangeNotifier() { 349 PrintSystem::PrinterWatcher* PrintSystemCUPS::CreatePrinterWatcher(
294 StopWatching(); 350 const std::string& printer_name) {
351 DCHECK(!printer_name.empty());
352 return new PrinterWatcherCUPS(this, printer_name);
295 } 353 }
296 354
297 bool PrinterChangeNotifier::StartWatching(const std::string& printer_name, 355 std::string PrintSystem::GenerateProxyId() {
298 Delegate* delegate) { 356 // TODO(gene): This code should generate a unique id for proxy. ID should be
299 if (state_) { 357 // unique for this user. Rand may return the same number. We'll need to change
300 NOTREACHED(); 358 // this in the future.
301 return false; 359 std::string id("CP_PROXY_");
302 } 360 id += Uint64ToString(base::RandUint64());
303 state_ = new NotificationState; 361 return id;
304 state_->AddRef();
305 if (!state_->Start(printer_name, delegate)) {
306 StopWatching();
307 return false;
308 }
309 return true;
310 } 362 }
311 363
312 bool PrinterChangeNotifier::StopWatching() { 364 scoped_refptr<PrintSystem> PrintSystem::CreateInstance() {
313 if (!state_) { 365 return new PrintSystemCUPS;
314 return false;
315 }
316 state_->Stop();
317 state_->Release();
318 state_ = NULL;
319 return true;
320 } 366 }
321 367
322 bool PrinterChangeNotifier::GetCurrentPrinterInfo(
323 PrinterBasicInfo* printer_info) {
324 if (!state_) {
325 return false;
326 }
327 DCHECK(printer_info);
328 return GetPrinterInfo(state_->printer_name(), printer_info);
329 }
330 } // namespace cloud_print 368 } // namespace cloud_print
331 369
OLDNEW
« no previous file with comments | « chrome/service/cloud_print/print_system.h ('k') | chrome/service/cloud_print/print_system_dummy.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698