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

Side by Side Diff: chrome/service/cloud_print/printer_info_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
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "chrome/service/cloud_print/printer_info.h"
6
7 #include <cups/cups.h>
8 #include <list>
9 #include <map>
10
11 #include "base/json/json_reader.h"
12 #include "base/file_path.h"
13 #include "base/file_util.h"
14 #include "base/logging.h"
15 #include "base/lock.h"
16 #include "base/message_loop.h"
17 #include "base/rand_util.h"
18 #include "base/string_util.h"
19 #include "base/task.h"
20 #include "base/values.h"
21 #include "base/utf_string_conversions.h"
22
23 namespace cloud_print {
24
25 static const char kCUPSPrinterInfoOpt[] = "printer-info";
26 static const char kCUPSPrinterStateOpt[] = "printer-state";
27
28 void EnumeratePrinters(PrinterList* printer_list) {
29 DCHECK(printer_list);
30 printer_list->clear();
31
32 cups_dest_t* destinations = NULL;
33 int num_dests = cupsGetDests(&destinations);
34
35 for (int printer_index = 0; printer_index < num_dests; printer_index++) {
36 const cups_dest_t& printer = destinations[printer_index];
37
38 PrinterBasicInfo printer_info;
39 printer_info.printer_name = printer.name;
40
41 const char* info = cupsGetOption(kCUPSPrinterInfoOpt,
42 printer.num_options, printer.options);
43 if (info != NULL)
44 printer_info.printer_description = info;
45
46 const char* state = cupsGetOption(kCUPSPrinterStateOpt,
47 printer.num_options, printer.options);
48 if (state != NULL)
49 StringToInt(state, &printer_info.printer_status);
50
51 // Store printer options.
52 for (int opt_index = 0; opt_index < printer.num_options; opt_index++) {
53 printer_info.options[printer.options[opt_index].name] =
54 printer.options[opt_index].value;
55 }
56
57 printer_list->push_back(printer_info);
58 }
59
60 cupsFreeDests(num_dests, destinations);
61
62 DLOG(INFO) << "Enumerated " << printer_list->size() << " printers.";
63 }
64
65 bool GetPrinterCapsAndDefaults(const std::string& printer_name,
66 PrinterCapsAndDefaults* printer_info) {
67 DCHECK(printer_info);
68
69 DLOG(INFO) << "Getting Caps and Defaults for: " << printer_name;
70
71 static Lock ppd_lock;
72 // cupsGetPPD returns a filename stored in a static buffer in CUPS.
73 // Protect this code with lock.
74 ppd_lock.Acquire();
75 FilePath ppd_path(cupsGetPPD(printer_name.c_str()));
76 ppd_lock.Release();
77
78 std::string content;
79 bool res = file_util::ReadFileToString(ppd_path, &content);
80
81 file_util::Delete(ppd_path, false);
82
83 if (res) {
84 printer_info->printer_capabilities.swap(content);
85 printer_info->caps_mime_type = "application/pagemaker";
86 // In CUPS, printer defaults is a part of PPD file. Nothing to upload here.
87 printer_info->printer_defaults.clear();
88 printer_info->defaults_mime_type.clear();
89 }
90
91 return res;
92 }
93
94 bool ValidatePrintTicket(const std::string& printer_name,
95 const std::string& print_ticket_data) {
96 scoped_ptr<Value> ticket_value(base::JSONReader::Read(print_ticket_data,
97 false));
98 return ticket_value != NULL && ticket_value->IsType(Value::TYPE_DICTIONARY);
99 }
100
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.
111 bool ParsePrintTicket(const std::string& print_ticket,
112 std::map<std::string, std::string>* options) {
113 DCHECK(options);
114 scoped_ptr<Value> ticket_value(base::JSONReader::Read(print_ticket, false));
115 if (ticket_value == NULL || !ticket_value->IsType(Value::TYPE_DICTIONARY))
116 return false;
117
118 options->clear();
119 DictionaryValue* ticket_dict =
120 static_cast<DictionaryValue*>(ticket_value.get());
121 DictionaryValue::key_iterator it(ticket_dict->begin_keys());
122 for (; it != ticket_dict->end_keys(); ++it) {
123 std::wstring key = *it;
124 std::string value;
125 if (ticket_dict->GetString(key, &value)) {
126 (*options)[WideToUTF8(key.c_str())] = value;
127 }
128 }
129
130 return true;
131 }
132
133 bool SpoolPrintJob(const std::string& print_ticket,
134 const FilePath& print_data_file_path,
135 const std::string& print_data_mime_type,
136 const std::string& printer_name,
137 const std::string& job_title,
138 PlatformJobId* job_id_ret) {
139 DCHECK(job_id_ret);
140
141 DLOG(INFO) << "Spooling print job for: " << printer_name;
142
143 // 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
145 // Dictionary value from JSON parser returns wchat_t.
146 std::map<std::string, std::string> options;
147 bool res = ParsePrintTicket(print_ticket, &options);
148 DCHECK(res); // If print ticket is invalid we still print using defaults.
149
150 std::vector<cups_option_t> cups_options;
151 std::map<std::string, std::string>::iterator it;
152 for (it = options.begin(); it != options.end(); ++it) {
153 cups_option_t opt;
154 opt.name = const_cast<char*>(it->first.c_str());
155 opt.value = const_cast<char*>(it->second.c_str());
156 cups_options.push_back(opt);
157 }
158
159 int job_id = cupsPrintFile(printer_name.c_str(),
160 print_data_file_path.value().c_str(),
161 job_title.c_str(),
162 cups_options.size(),
163 &(cups_options[0]));
164
165 if (job_id == 0)
166 return false;
167
168 *job_id_ret = job_id;
169 return true;
170 }
171
172 bool GetJobDetails(const std::string& printer_name,
173 PlatformJobId job_id,
174 PrintJobDetails *job_details) {
175 DCHECK(job_details);
176
177 DLOG(INFO) << "Getting job details for: " << printer_name <<
178 " job_id: " << job_id;
179
180 cups_job_t* jobs = NULL;
181 int num_jobs = cupsGetJobs(&jobs, printer_name.c_str(), 1, -1);
182
183 bool found = false;
184 for (int i = 0; i < num_jobs; i++) {
185 if (jobs[i].id == job_id) {
186 found = true;
187 switch (jobs[i].state) {
188 case IPP_JOB_PENDING :
189 case IPP_JOB_HELD :
190 case IPP_JOB_PROCESSING :
191 job_details->status = PRINT_JOB_STATUS_IN_PROGRESS;
192 break;
193 case IPP_JOB_STOPPED :
194 case IPP_JOB_CANCELLED :
195 case IPP_JOB_ABORTED :
196 job_details->status = PRINT_JOB_STATUS_ERROR;
197 break;
198 case IPP_JOB_COMPLETED :
199 job_details->status = PRINT_JOB_STATUS_COMPLETED;
200 break;
201 default:
202 job_details->status = PRINT_JOB_STATUS_INVALID;
203 }
204 job_details->platform_status_flags = jobs[i].state;
205
206 // We don't have any details on the number of processed pages here.
207 break;
208 }
209 }
210
211 cupsFreeJobs(num_jobs, jobs);
212 return found;
213 }
214
215 bool GetPrinterInfo(const std::string& printer_name, PrinterBasicInfo* info) {
216 DCHECK(info);
217
218 DLOG(INFO) << "Getting printer info for: " << printer_name;
219
220 // 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
222 // everywhere (for example, it supported from Mac OS 10.6 only).
223 PrinterList printer_list;
224 EnumeratePrinters(&printer_list);
225
226 PrinterList::iterator it;
227 for (it = printer_list.begin(); it != printer_list.end(); ++it) {
228 if (it->printer_name == printer_name) {
229 *info = *it;
230 return true;
231 }
232 }
233 return false;
234 }
235
236 bool IsValidPrinter(const std::string& printer_name) {
237 PrinterBasicInfo info;
238 return GetPrinterInfo(printer_name, &info);
239 }
240
241 class PrinterChangeNotifier::NotificationState
242 : public base::RefCountedThreadSafe<
243 PrinterChangeNotifier::NotificationState> {
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 }
292
293 PrinterChangeNotifier::~PrinterChangeNotifier() {
294 StopWatching();
295 }
296
297 bool PrinterChangeNotifier::StartWatching(const std::string& printer_name,
298 Delegate* delegate) {
299 if (state_) {
300 NOTREACHED();
301 return false;
302 }
303 state_ = new NotificationState;
304 state_->AddRef();
305 if (!state_->Start(printer_name, delegate)) {
306 StopWatching();
307 return false;
308 }
309 return true;
310 }
311
312 bool PrinterChangeNotifier::StopWatching() {
313 if (!state_) {
314 return false;
315 }
316 state_->Stop();
317 state_->Release();
318 state_ = NULL;
319 return true;
320 }
321
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
331
OLDNEW
« no previous file with comments | « chrome/service/cloud_print/printer_info.h ('k') | chrome/service/cloud_print/printer_info_dummy.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698