OLD | NEW |
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/print_system.h" | 5 #include "chrome/service/cloud_print/print_system.h" |
6 | 6 |
7 #include <cups/cups.h> | 7 #include <cups/cups.h> |
8 #include <dlfcn.h> | 8 #include <dlfcn.h> |
9 #include <errno.h> | 9 #include <errno.h> |
10 #include <pthread.h> | 10 #include <pthread.h> |
11 | 11 |
| 12 #include <algorithm> |
12 #include <list> | 13 #include <list> |
13 #include <map> | 14 #include <map> |
14 | 15 |
15 #include "base/file_path.h" | 16 #include "base/file_path.h" |
16 #include "base/json/json_reader.h" | 17 #include "base/json/json_reader.h" |
17 #include "base/lock.h" | 18 #include "base/lock.h" |
18 #include "base/logging.h" | 19 #include "base/logging.h" |
| 20 #include "base/md5.h" |
19 #include "base/message_loop.h" | 21 #include "base/message_loop.h" |
20 #include "base/rand_util.h" | 22 #include "base/rand_util.h" |
21 #include "base/string_number_conversions.h" | 23 #include "base/string_number_conversions.h" |
22 #include "base/string_util.h" | 24 #include "base/string_util.h" |
23 #include "base/task.h" | 25 #include "base/task.h" |
24 #include "base/values.h" | 26 #include "base/values.h" |
25 #include "base/utf_string_conversions.h" | 27 #include "base/utf_string_conversions.h" |
26 #include "googleurl/src/gurl.h" | 28 #include "googleurl/src/gurl.h" |
27 #include "printing/backend/cups_helper.h" | 29 #include "printing/backend/cups_helper.h" |
28 #include "printing/backend/print_backend.h" | 30 #include "printing/backend/print_backend.h" |
29 | 31 |
30 namespace cloud_print { | 32 namespace { |
31 | |
32 static const char kCUPSPrinterInfoOpt[] = "printer-info"; | 33 static const char kCUPSPrinterInfoOpt[] = "printer-info"; |
33 static const char kCUPSPrinterStateOpt[] = "printer-state"; | 34 static const char kCUPSPrinterStateOpt[] = "printer-state"; |
34 static const char kCUPSPrintServerURL[] = "print_server_url"; | 35 static const char kCUPSPrintServerURLs[] = "print_server_urls"; |
| 36 static const char kCUPSUpdateTimeoutMs[] = "update_timeout_ms"; |
| 37 static const char kCUPSPrintBackendServerURL[] = "print_server_url"; |
35 | 38 |
36 // Default port for IPP print servers. | 39 // Default port for IPP print servers. |
37 static const int kDefaultIPPServerPort = 631; | 40 static const int kDefaultIPPServerPort = 631; |
38 | 41 |
| 42 // Time interval to check for printer's updates. |
| 43 const int kCheckForPrinterUpdatesMs = 6*60*60*1000; |
| 44 |
| 45 // Job update timeput |
| 46 const int kJobUpdateTimeoutMs = 5000; |
| 47 |
| 48 struct PrintServerInfoCUPS { |
| 49 GURL url; |
| 50 scoped_refptr<printing::PrintBackend> backend; |
| 51 printing::PrinterList printers; |
| 52 // CapsMap cache PPD until the next update and give a fast access to it by |
| 53 // printer name. PPD request is relatively expensive and this should minimize |
| 54 // the number of requests. |
| 55 typedef std::map<std::string, printing::PrinterCapsAndDefaults> CapsMap; |
| 56 CapsMap caps_cache; |
| 57 }; |
| 58 |
| 59 } // namespace |
| 60 |
| 61 namespace cloud_print { |
| 62 |
39 class PrintSystemCUPS : public PrintSystem { | 63 class PrintSystemCUPS : public PrintSystem { |
40 public: | 64 public: |
41 PrintSystemCUPS(const GURL& print_server_url, | 65 explicit PrintSystemCUPS(const DictionaryValue* print_system_settings); |
42 const DictionaryValue* print_system_settings); | |
43 | 66 |
44 // PrintSystem implementation. | 67 // PrintSystem implementation. |
45 virtual printing::PrintBackend* GetPrintBackend(); | 68 virtual void Init(); |
| 69 |
| 70 virtual void EnumeratePrinters(printing::PrinterList* printer_list); |
| 71 |
| 72 virtual bool GetPrinterCapsAndDefaults( |
| 73 const std::string& printer_name, |
| 74 printing::PrinterCapsAndDefaults* printer_info); |
| 75 |
| 76 virtual bool IsValidPrinter(const std::string& printer_name); |
46 | 77 |
47 virtual bool ValidatePrintTicket(const std::string& printer_name, | 78 virtual bool ValidatePrintTicket(const std::string& printer_name, |
48 const std::string& print_ticket_data); | 79 const std::string& print_ticket_data); |
49 | 80 |
50 virtual bool GetJobDetails(const std::string& printer_name, | 81 virtual bool GetJobDetails(const std::string& printer_name, |
51 PlatformJobId job_id, | 82 PlatformJobId job_id, |
52 PrintJobDetails *job_details); | 83 PrintJobDetails *job_details); |
53 | 84 |
54 // TODO(gene): Add implementation for CUPS print server watcher. | |
55 class PrintServerWatcherCUPS | |
56 : public PrintSystem::PrintServerWatcher { | |
57 public: | |
58 PrintServerWatcherCUPS() {} | |
59 | |
60 // PrintSystem::PrintServerWatcher interface | |
61 virtual bool StartWatching( | |
62 PrintSystem::PrintServerWatcher::Delegate* delegate) { | |
63 NOTIMPLEMENTED(); | |
64 return true; | |
65 } | |
66 virtual bool StopWatching() { | |
67 NOTIMPLEMENTED(); | |
68 return true; | |
69 } | |
70 }; | |
71 | |
72 class PrinterWatcherCUPS | |
73 : public PrintSystem::PrinterWatcher { | |
74 public: | |
75 explicit PrinterWatcherCUPS(PrintSystemCUPS* print_system, | |
76 const std::string& printer_name) | |
77 : printer_name_(printer_name), | |
78 delegate_(NULL), | |
79 print_system_(print_system) { | |
80 } | |
81 | |
82 // PrintSystem::PrinterWatcher interface | |
83 virtual bool StartWatching( | |
84 PrintSystem::PrinterWatcher::Delegate* delegate) { | |
85 if (delegate_ != NULL) | |
86 StopWatching(); | |
87 delegate_ = delegate; | |
88 MessageLoop::current()->PostDelayedTask(FROM_HERE, | |
89 NewRunnableMethod(this, | |
90 &PrintSystemCUPS::PrinterWatcherCUPS::Update), 5000); | |
91 return true; | |
92 } | |
93 virtual bool StopWatching() { | |
94 delegate_ = NULL; | |
95 return true; | |
96 } | |
97 bool GetCurrentPrinterInfo(printing::PrinterBasicInfo* printer_info) { | |
98 DCHECK(printer_info); | |
99 return print_system_->GetPrinterInfo(printer_name_, printer_info); | |
100 } | |
101 | |
102 void Update() { | |
103 if (delegate_ == NULL) | |
104 return; // Orphan call. We have been stopped already. | |
105 // For CUPS proxy, we are going to fire OnJobChanged notification | |
106 // periodically. Higher level will check if there are any outstanding | |
107 // jobs for this printer and check their status. If printer has no | |
108 // outstanding jobs, OnJobChanged() will do nothing. | |
109 delegate_->OnJobChanged(); | |
110 MessageLoop::current()->PostDelayedTask(FROM_HERE, | |
111 NewRunnableMethod(this, | |
112 &PrintSystemCUPS::PrinterWatcherCUPS::Update), | |
113 kNotificationTimeout); | |
114 } | |
115 private: | |
116 static const int kNotificationTimeout = 5000; // in ms | |
117 std::string printer_name_; | |
118 PrintSystem::PrinterWatcher::Delegate* delegate_; | |
119 scoped_refptr<PrintSystemCUPS> print_system_; | |
120 DISALLOW_COPY_AND_ASSIGN(PrinterWatcherCUPS); | |
121 }; | |
122 | |
123 class JobSpoolerCUPS : public PrintSystem::JobSpooler { | |
124 public: | |
125 explicit JobSpoolerCUPS(PrintSystemCUPS* print_system) | |
126 : print_system_(print_system) { | |
127 DCHECK(print_system_.get()); | |
128 } | |
129 // PrintSystem::JobSpooler implementation. | |
130 virtual bool Spool(const std::string& print_ticket, | |
131 const FilePath& print_data_file_path, | |
132 const std::string& print_data_mime_type, | |
133 const std::string& printer_name, | |
134 const std::string& job_title, | |
135 JobSpooler::Delegate* delegate) { | |
136 DCHECK(delegate); | |
137 int job_id = print_system_->SpoolPrintJob( | |
138 print_ticket, print_data_file_path, print_data_mime_type, | |
139 printer_name, job_title); | |
140 MessageLoop::current()->PostTask(FROM_HERE, | |
141 NewRunnableFunction( | |
142 &JobSpoolerCUPS::NotifyDelegate, | |
143 delegate, | |
144 job_id)); | |
145 return true; | |
146 } | |
147 | |
148 static void NotifyDelegate(JobSpooler::Delegate* delegate, int job_id) { | |
149 if (job_id) | |
150 delegate->OnJobSpoolSucceeded(job_id); | |
151 else | |
152 delegate->OnJobSpoolFailed(); | |
153 } | |
154 private: | |
155 scoped_refptr<PrintSystemCUPS> print_system_; | |
156 DISALLOW_COPY_AND_ASSIGN(JobSpoolerCUPS); | |
157 }; | |
158 | |
159 virtual PrintSystem::PrintServerWatcher* CreatePrintServerWatcher(); | 85 virtual PrintSystem::PrintServerWatcher* CreatePrintServerWatcher(); |
160 virtual PrintSystem::PrinterWatcher* CreatePrinterWatcher( | 86 virtual PrintSystem::PrinterWatcher* CreatePrinterWatcher( |
161 const std::string& printer_name); | 87 const std::string& printer_name); |
162 virtual PrintSystem::JobSpooler* CreateJobSpooler(); | 88 virtual PrintSystem::JobSpooler* CreateJobSpooler(); |
163 | 89 |
164 // Helper functions. | 90 // Helper functions. |
165 PlatformJobId SpoolPrintJob(const std::string& print_ticket, | 91 PlatformJobId SpoolPrintJob(const std::string& print_ticket, |
166 const FilePath& print_data_file_path, | 92 const FilePath& print_data_file_path, |
167 const std::string& print_data_mime_type, | 93 const std::string& print_data_mime_type, |
168 const std::string& printer_name, | 94 const std::string& printer_name, |
169 const std::string& job_title); | 95 const std::string& job_title); |
170 bool GetPrinterInfo(const std::string& printer_name, | 96 bool GetPrinterInfo(const std::string& printer_name, |
171 printing::PrinterBasicInfo* info); | 97 printing::PrinterBasicInfo* info); |
172 bool ParsePrintTicket(const std::string& print_ticket, | 98 bool ParsePrintTicket(const std::string& print_ticket, |
173 std::map<std::string, std::string>* options); | 99 std::map<std::string, std::string>* options); |
174 | 100 |
| 101 int GetUpdateTimeoutMs() const { |
| 102 return update_timeout_; |
| 103 } |
| 104 |
175 private: | 105 private: |
176 // Following functions are wrappers around corresponding CUPS functions. | 106 // Following functions are wrappers around corresponding CUPS functions. |
177 // <functions>2() are called when print server is specified, and plain | 107 // <functions>2() are called when print server is specified, and plain |
178 // version in another case. There is an issue specifing CUPS_HTTP_DEFAULT | 108 // version in another case. There is an issue specifing CUPS_HTTP_DEFAULT |
179 // in the <functions>2(), it does not work in CUPS prior to 1.4. | 109 // in the <functions>2(), it does not work in CUPS prior to 1.4. |
180 int GetJobs(cups_job_t** jobs, const char* name, | 110 int GetJobs(cups_job_t** jobs, const GURL& url, const char* name, |
181 int myjobs, int whichjobs); | 111 int myjobs, int whichjobs); |
182 int PrintFile(const char* name, const char* filename, const char* title, | 112 int PrintFile(const GURL& url, const char* name, const char* filename, |
183 int num_options, cups_option_t* options); | 113 const char* title, int num_options, cups_option_t* options); |
184 | 114 |
185 void Init(const DictionaryValue* print_system_settings); | 115 void InitPrintBackends(const DictionaryValue* print_system_settings); |
186 | 116 void AddPrintServer(const std::string& url); |
187 GURL print_server_url_; | 117 |
188 scoped_refptr<printing::PrintBackend> print_backend_; | 118 void UpdatePrinters(); |
| 119 |
| 120 // PrintServerList contains information about all print servers and backends |
| 121 // this proxy is connected to. |
| 122 typedef std::list<PrintServerInfoCUPS> PrintServerList; |
| 123 // PrintersMap provides fast check for printer existence and access to |
| 124 // printer information by printer name. (optimization). |
| 125 typedef std::map<std::string, PrintServerInfoCUPS*> PrintersMap; |
| 126 PrintServerList print_servers_; |
| 127 PrintersMap printer_map_; |
| 128 |
| 129 int update_timeout_; |
| 130 bool initialized_; |
189 }; | 131 }; |
190 | 132 |
191 PrintSystemCUPS::PrintSystemCUPS(const GURL& print_server_url, | 133 class PrintServerWatcherCUPS |
192 const DictionaryValue* print_system_settings) | 134 : public PrintSystem::PrintServerWatcher { |
193 : print_server_url_(print_server_url) { | 135 public: |
194 Init(print_system_settings); | 136 explicit PrintServerWatcherCUPS(PrintSystemCUPS* print_system) |
195 } | 137 : print_system_(print_system) { |
196 | 138 } |
197 void PrintSystemCUPS::Init(const DictionaryValue* print_system_settings) { | 139 ~PrintServerWatcherCUPS() { |
198 print_backend_ = | 140 StopWatching(); |
199 printing::PrintBackend::CreateInstance(print_system_settings); | 141 } |
200 } | 142 |
201 | 143 // PrintSystem::PrintServerWatcher interface |
202 printing::PrintBackend* PrintSystemCUPS::GetPrintBackend() { | 144 virtual bool StartWatching( |
203 return print_backend_; | 145 PrintSystem::PrintServerWatcher::Delegate* delegate) { |
| 146 delegate_ = delegate; |
| 147 printers_hash_ = GetPrintersHash(); |
| 148 MessageLoop::current()->PostDelayedTask(FROM_HERE, |
| 149 NewRunnableMethod(this, &PrintServerWatcherCUPS::CheckForUpdates), |
| 150 print_system_->GetUpdateTimeoutMs()); |
| 151 return true; |
| 152 } |
| 153 virtual bool StopWatching() { |
| 154 delegate_ = NULL; |
| 155 return true; |
| 156 } |
| 157 |
| 158 void CheckForUpdates() { |
| 159 if (delegate_ == NULL) |
| 160 return; // Orphan call. We have been stopped already. |
| 161 VLOG(1) << "CP_CUPS: Checking for new printers"; |
| 162 std::string new_hash = GetPrintersHash(); |
| 163 if (printers_hash_ != new_hash) { |
| 164 printers_hash_ = new_hash; |
| 165 delegate_->OnPrinterAdded(); |
| 166 } |
| 167 MessageLoop::current()->PostDelayedTask(FROM_HERE, |
| 168 NewRunnableMethod(this, &PrintServerWatcherCUPS::CheckForUpdates), |
| 169 print_system_->GetUpdateTimeoutMs()); |
| 170 } |
| 171 private: |
| 172 std::string GetPrintersHash() { |
| 173 printing::PrinterList printer_list; |
| 174 print_system_->EnumeratePrinters(&printer_list); |
| 175 |
| 176 // Sort printer names. |
| 177 std::vector<std::string> printers; |
| 178 printing::PrinterList::iterator it; |
| 179 for (it = printer_list.begin(); it != printer_list.end(); ++it) |
| 180 printers.push_back(it->printer_name); |
| 181 std::sort(printers.begin(), printers.end()); |
| 182 |
| 183 std::string to_hash; |
| 184 for (size_t i = 0; i < printers.size(); i++) |
| 185 to_hash += printers[i]; |
| 186 |
| 187 return MD5String(to_hash); |
| 188 } |
| 189 |
| 190 scoped_refptr<PrintSystemCUPS> print_system_; |
| 191 PrintSystem::PrintServerWatcher::Delegate* delegate_; |
| 192 std::string printers_hash_; |
| 193 DISALLOW_COPY_AND_ASSIGN(PrintServerWatcherCUPS); |
| 194 }; |
| 195 |
| 196 class PrinterWatcherCUPS |
| 197 : public PrintSystem::PrinterWatcher { |
| 198 public: |
| 199 explicit PrinterWatcherCUPS(PrintSystemCUPS* print_system, |
| 200 const std::string& printer_name) |
| 201 : printer_name_(printer_name), |
| 202 delegate_(NULL), |
| 203 print_system_(print_system) { |
| 204 } |
| 205 ~PrinterWatcherCUPS() { |
| 206 StopWatching(); |
| 207 } |
| 208 |
| 209 // PrintSystem::PrinterWatcher interface |
| 210 virtual bool StartWatching( |
| 211 PrintSystem::PrinterWatcher::Delegate* delegate) { |
| 212 if (delegate_ != NULL) |
| 213 StopWatching(); |
| 214 delegate_ = delegate; |
| 215 settings_hash_ = GetSettingsHash(); |
| 216 // Schedule next job status update. |
| 217 MessageLoop::current()->PostDelayedTask(FROM_HERE, |
| 218 NewRunnableMethod(this, &PrinterWatcherCUPS::JobStatusUpdate), |
| 219 kJobUpdateTimeoutMs); |
| 220 // Schedule next printer check. |
| 221 // TODO(gene): Randomize time for the next printer update. |
| 222 MessageLoop::current()->PostDelayedTask(FROM_HERE, |
| 223 NewRunnableMethod(this, &PrinterWatcherCUPS::PrinterUpdate), |
| 224 print_system_->GetUpdateTimeoutMs()); |
| 225 return true; |
| 226 } |
| 227 virtual bool StopWatching() { |
| 228 delegate_ = NULL; |
| 229 return true; |
| 230 } |
| 231 bool GetCurrentPrinterInfo(printing::PrinterBasicInfo* printer_info) { |
| 232 DCHECK(printer_info); |
| 233 return print_system_->GetPrinterInfo(printer_name_, printer_info); |
| 234 } |
| 235 |
| 236 void JobStatusUpdate() { |
| 237 if (delegate_ == NULL) |
| 238 return; // Orphan call. We have been stopped already. |
| 239 // For CUPS proxy, we are going to fire OnJobChanged notification |
| 240 // periodically. Higher level will check if there are any outstanding |
| 241 // jobs for this printer and check their status. If printer has no |
| 242 // outstanding jobs, OnJobChanged() will do nothing. |
| 243 delegate_->OnJobChanged(); |
| 244 MessageLoop::current()->PostDelayedTask(FROM_HERE, |
| 245 NewRunnableMethod(this, &PrinterWatcherCUPS::JobStatusUpdate), |
| 246 kJobUpdateTimeoutMs); |
| 247 } |
| 248 |
| 249 void PrinterUpdate() { |
| 250 if (delegate_ == NULL) |
| 251 return; // Orphan call. We have been stopped already. |
| 252 VLOG(1) << "CP_CUPS: Checking for printer updates: " << printer_name_; |
| 253 std::string new_hash = GetSettingsHash(); |
| 254 if (settings_hash_ != new_hash) { |
| 255 settings_hash_ = new_hash; |
| 256 delegate_->OnPrinterChanged(); |
| 257 VLOG(1) << "CP_CUPS: Printer update detected for: " << printer_name_; |
| 258 } |
| 259 MessageLoop::current()->PostDelayedTask(FROM_HERE, |
| 260 NewRunnableMethod(this, &PrinterWatcherCUPS::PrinterUpdate), |
| 261 print_system_->GetUpdateTimeoutMs()); |
| 262 } |
| 263 private: |
| 264 std::string GetSettingsHash() { |
| 265 printing::PrinterBasicInfo info; |
| 266 if (!print_system_->GetPrinterInfo(printer_name_, &info)) |
| 267 return std::string(); |
| 268 |
| 269 printing::PrinterCapsAndDefaults caps; |
| 270 if (!print_system_->GetPrinterCapsAndDefaults(printer_name_, &caps)) |
| 271 return std::string(); |
| 272 |
| 273 std::string to_hash(info.printer_name); |
| 274 to_hash += info.printer_description; |
| 275 std::map<std::string, std::string>::const_iterator it; |
| 276 for (it = info.options.begin(); it != info.options.end(); ++it) { |
| 277 to_hash += it->first; |
| 278 to_hash += it->second; |
| 279 } |
| 280 |
| 281 to_hash += caps.printer_capabilities; |
| 282 to_hash += caps.caps_mime_type; |
| 283 to_hash += caps.printer_defaults; |
| 284 to_hash += caps.defaults_mime_type; |
| 285 |
| 286 return MD5String(to_hash); |
| 287 } |
| 288 |
| 289 std::string printer_name_; |
| 290 PrintSystem::PrinterWatcher::Delegate* delegate_; |
| 291 scoped_refptr<PrintSystemCUPS> print_system_; |
| 292 std::string settings_hash_; |
| 293 DISALLOW_COPY_AND_ASSIGN(PrinterWatcherCUPS); |
| 294 }; |
| 295 |
| 296 class JobSpoolerCUPS : public PrintSystem::JobSpooler { |
| 297 public: |
| 298 explicit JobSpoolerCUPS(PrintSystemCUPS* print_system) |
| 299 : print_system_(print_system) { |
| 300 DCHECK(print_system_.get()); |
| 301 } |
| 302 // PrintSystem::JobSpooler implementation. |
| 303 virtual bool Spool(const std::string& print_ticket, |
| 304 const FilePath& print_data_file_path, |
| 305 const std::string& print_data_mime_type, |
| 306 const std::string& printer_name, |
| 307 const std::string& job_title, |
| 308 JobSpooler::Delegate* delegate) { |
| 309 DCHECK(delegate); |
| 310 int job_id = print_system_->SpoolPrintJob( |
| 311 print_ticket, print_data_file_path, print_data_mime_type, |
| 312 printer_name, job_title); |
| 313 MessageLoop::current()->PostTask(FROM_HERE, |
| 314 NewRunnableFunction( |
| 315 &JobSpoolerCUPS::NotifyDelegate, |
| 316 delegate, |
| 317 job_id)); |
| 318 return true; |
| 319 } |
| 320 |
| 321 static void NotifyDelegate(JobSpooler::Delegate* delegate, int job_id) { |
| 322 if (job_id) |
| 323 delegate->OnJobSpoolSucceeded(job_id); |
| 324 else |
| 325 delegate->OnJobSpoolFailed(); |
| 326 } |
| 327 private: |
| 328 scoped_refptr<PrintSystemCUPS> print_system_; |
| 329 DISALLOW_COPY_AND_ASSIGN(JobSpoolerCUPS); |
| 330 }; |
| 331 |
| 332 PrintSystemCUPS::PrintSystemCUPS(const DictionaryValue* print_system_settings) |
| 333 : update_timeout_(kCheckForPrinterUpdatesMs), initialized_(false) { |
| 334 if (print_system_settings) { |
| 335 int timeout; |
| 336 if (print_system_settings->GetInteger(kCUPSUpdateTimeoutMs, &timeout)) |
| 337 update_timeout_ = timeout; |
| 338 } |
| 339 |
| 340 InitPrintBackends(print_system_settings); |
| 341 } |
| 342 |
| 343 void PrintSystemCUPS::InitPrintBackends( |
| 344 const DictionaryValue* print_system_settings) { |
| 345 ListValue* url_list; |
| 346 if (print_system_settings && |
| 347 print_system_settings->GetList(kCUPSPrintServerURLs, &url_list)) { |
| 348 for (size_t i = 0; i < url_list->GetSize(); i++) { |
| 349 std::string print_server_url; |
| 350 if (url_list->GetString(i, &print_server_url)) |
| 351 AddPrintServer(print_server_url); |
| 352 } |
| 353 } |
| 354 |
| 355 // If server list is empty, use default print server. |
| 356 if (print_servers_.empty()) |
| 357 AddPrintServer(std::string()); |
| 358 } |
| 359 |
| 360 void PrintSystemCUPS::AddPrintServer(const std::string& url) { |
| 361 if (url.empty()) |
| 362 LOG(WARNING) << "No print server specified. Using default print server."; |
| 363 |
| 364 // Get Print backend for the specific print server. |
| 365 DictionaryValue backend_settings; |
| 366 backend_settings.SetString(kCUPSPrintBackendServerURL, url); |
| 367 |
| 368 PrintServerInfoCUPS print_server; |
| 369 print_server.backend = |
| 370 printing::PrintBackend::CreateInstance(&backend_settings); |
| 371 print_server.url = GURL(url.c_str()); |
| 372 |
| 373 print_servers_.push_back(print_server); |
| 374 } |
| 375 |
| 376 void PrintSystemCUPS::Init() { |
| 377 UpdatePrinters(); |
| 378 initialized_ = true; |
| 379 } |
| 380 |
| 381 void PrintSystemCUPS::UpdatePrinters() { |
| 382 printer_map_.clear(); |
| 383 PrintServerList::iterator it; |
| 384 for (it = print_servers_.begin(); it != print_servers_.end(); ++it) { |
| 385 it->backend->EnumeratePrinters(&it->printers); |
| 386 it->caps_cache.clear(); |
| 387 printing::PrinterList::const_iterator printer_it; |
| 388 for (printer_it = it->printers.begin(); |
| 389 printer_it != it->printers.end(); ++printer_it) { |
| 390 printer_map_[printer_it->printer_name] = &(*it); |
| 391 } |
| 392 VLOG(1) << "CUPS: Updated printer list for url: " << it->url |
| 393 << " Number of printers: " << it->printers.size(); |
| 394 } |
| 395 |
| 396 // Schedule next update. |
| 397 MessageLoop::current()->PostDelayedTask(FROM_HERE, |
| 398 NewRunnableMethod(this, &PrintSystemCUPS::UpdatePrinters), |
| 399 GetUpdateTimeoutMs()); |
| 400 } |
| 401 |
| 402 void PrintSystemCUPS::EnumeratePrinters(printing::PrinterList* printer_list) { |
| 403 DCHECK(initialized_); |
| 404 printer_list->clear(); |
| 405 PrintServerList::iterator it; |
| 406 for (it = print_servers_.begin(); it != print_servers_.end(); ++it) { |
| 407 printer_list->insert(printer_list->end(), |
| 408 it->printers.begin(), it->printers.end()); |
| 409 } |
| 410 VLOG(1) << "CUPS: Total " << printer_list->size() << " printers enumerated."; |
| 411 } |
| 412 |
| 413 bool PrintSystemCUPS::GetPrinterCapsAndDefaults( |
| 414 const std::string& printer_name, |
| 415 printing::PrinterCapsAndDefaults* printer_info) { |
| 416 DCHECK(initialized_); |
| 417 PrintersMap::iterator it = printer_map_.find(printer_name); |
| 418 if (it == printer_map_.end()) |
| 419 return false; |
| 420 |
| 421 PrintServerInfoCUPS::CapsMap::iterator caps_it = |
| 422 it->second->caps_cache.find(printer_name); |
| 423 if (caps_it != it->second->caps_cache.end()) { |
| 424 *printer_info = caps_it->second; |
| 425 return true; |
| 426 } |
| 427 |
| 428 // TODO(gene): Retry multiple times in case of error. |
| 429 if (!it->second->backend->GetPrinterCapsAndDefaults(printer_name, |
| 430 printer_info) ) { |
| 431 return false; |
| 432 } |
| 433 |
| 434 it->second->caps_cache[printer_name] = *printer_info; |
| 435 return true; |
| 436 } |
| 437 |
| 438 bool PrintSystemCUPS::IsValidPrinter(const std::string& printer_name) { |
| 439 DCHECK(initialized_); |
| 440 PrintersMap::iterator it = printer_map_.find(printer_name); |
| 441 return it != printer_map_.end(); |
204 } | 442 } |
205 | 443 |
206 bool PrintSystemCUPS::ValidatePrintTicket(const std::string& printer_name, | 444 bool PrintSystemCUPS::ValidatePrintTicket(const std::string& printer_name, |
207 const std::string& print_ticket_data) { | 445 const std::string& print_ticket_data) { |
| 446 DCHECK(initialized_); |
208 scoped_ptr<Value> ticket_value(base::JSONReader::Read(print_ticket_data, | 447 scoped_ptr<Value> ticket_value(base::JSONReader::Read(print_ticket_data, |
209 false)); | 448 false)); |
210 return ticket_value != NULL && ticket_value->IsType(Value::TYPE_DICTIONARY); | 449 return ticket_value != NULL && ticket_value->IsType(Value::TYPE_DICTIONARY); |
211 } | 450 } |
212 | 451 |
213 // Print ticket on linux is a JSON string containing only one dictionary. | 452 // Print ticket on linux is a JSON string containing only one dictionary. |
214 bool PrintSystemCUPS::ParsePrintTicket(const std::string& print_ticket, | 453 bool PrintSystemCUPS::ParsePrintTicket(const std::string& print_ticket, |
215 std::map<std::string, std::string>* options) { | 454 std::map<std::string, std::string>* options) { |
| 455 DCHECK(initialized_); |
216 DCHECK(options); | 456 DCHECK(options); |
217 scoped_ptr<Value> ticket_value(base::JSONReader::Read(print_ticket, false)); | 457 scoped_ptr<Value> ticket_value(base::JSONReader::Read(print_ticket, false)); |
218 if (ticket_value == NULL || !ticket_value->IsType(Value::TYPE_DICTIONARY)) | 458 if (ticket_value == NULL || !ticket_value->IsType(Value::TYPE_DICTIONARY)) |
219 return false; | 459 return false; |
220 | 460 |
221 options->clear(); | 461 options->clear(); |
222 DictionaryValue* ticket_dict = | 462 DictionaryValue* ticket_dict = |
223 static_cast<DictionaryValue*>(ticket_value.get()); | 463 static_cast<DictionaryValue*>(ticket_value.get()); |
224 DictionaryValue::key_iterator it(ticket_dict->begin_keys()); | 464 DictionaryValue::key_iterator it(ticket_dict->begin_keys()); |
225 for (; it != ticket_dict->end_keys(); ++it) { | 465 for (; it != ticket_dict->end_keys(); ++it) { |
226 const std::string& key = *it; | 466 const std::string& key = *it; |
227 std::string value; | 467 std::string value; |
228 if (ticket_dict->GetString(key, &value)) { | 468 if (ticket_dict->GetString(key, &value)) { |
229 (*options)[key] = value; | 469 (*options)[key] = value; |
230 } | 470 } |
231 } | 471 } |
232 | 472 |
233 return true; | 473 return true; |
234 } | 474 } |
235 | 475 |
236 bool PrintSystemCUPS::GetJobDetails(const std::string& printer_name, | 476 bool PrintSystemCUPS::GetJobDetails(const std::string& printer_name, |
237 PlatformJobId job_id, | 477 PlatformJobId job_id, |
238 PrintJobDetails *job_details) { | 478 PrintJobDetails *job_details) { |
| 479 DCHECK(initialized_); |
239 DCHECK(job_details); | 480 DCHECK(job_details); |
240 | 481 |
| 482 PrintersMap::iterator it = printer_map_.find(printer_name); |
| 483 if (it == printer_map_.end()) |
| 484 return false; |
| 485 |
241 cups_job_t* jobs = NULL; | 486 cups_job_t* jobs = NULL; |
242 int num_jobs = GetJobs(&jobs, printer_name.c_str(), 1, -1); | 487 int num_jobs = GetJobs(&jobs, it->second->url, printer_name.c_str(), 1, -1); |
243 | 488 |
244 bool found = false; | 489 bool found = false; |
245 for (int i = 0; i < num_jobs; i++) { | 490 for (int i = 0; i < num_jobs; i++) { |
246 if (jobs[i].id == job_id) { | 491 if (jobs[i].id == job_id) { |
247 found = true; | 492 found = true; |
248 switch (jobs[i].state) { | 493 switch (jobs[i].state) { |
249 case IPP_JOB_PENDING : | 494 case IPP_JOB_PENDING : |
250 case IPP_JOB_HELD : | 495 case IPP_JOB_HELD : |
251 case IPP_JOB_PROCESSING : | 496 case IPP_JOB_PROCESSING : |
252 job_details->status = PRINT_JOB_STATUS_IN_PROGRESS; | 497 job_details->status = PRINT_JOB_STATUS_IN_PROGRESS; |
(...skipping 22 matching lines...) Expand all Loading... |
275 else | 520 else |
276 LOG(WARNING) << "CP_CUPS: Job not found for: " << printer_name | 521 LOG(WARNING) << "CP_CUPS: Job not found for: " << printer_name |
277 << " job_id: " << job_id; | 522 << " job_id: " << job_id; |
278 | 523 |
279 cupsFreeJobs(num_jobs, jobs); | 524 cupsFreeJobs(num_jobs, jobs); |
280 return found; | 525 return found; |
281 } | 526 } |
282 | 527 |
283 bool PrintSystemCUPS::GetPrinterInfo(const std::string& printer_name, | 528 bool PrintSystemCUPS::GetPrinterInfo(const std::string& printer_name, |
284 printing::PrinterBasicInfo* info) { | 529 printing::PrinterBasicInfo* info) { |
| 530 DCHECK(initialized_); |
285 DCHECK(info); | 531 DCHECK(info); |
286 | |
287 VLOG(1) << "CP_CUPS: Getting printer info for: " << printer_name; | 532 VLOG(1) << "CP_CUPS: Getting printer info for: " << printer_name; |
288 | 533 |
289 // This is not very efficient way to get specific printer info. CUPS 1.4 | 534 PrintersMap::iterator it = printer_map_.find(printer_name); |
290 // supports cupsGetNamedDest() function. However, CUPS 1.4 is not available | 535 if (it == printer_map_.end()) |
291 // everywhere (for example, it supported from Mac OS 10.6 only). | 536 return false; |
292 printing::PrinterList printer_list; | |
293 print_backend_->EnumeratePrinters(&printer_list); | |
294 | 537 |
295 printing::PrinterList::iterator it; | 538 printing::PrinterList::iterator printer_it; |
296 for (it = printer_list.begin(); it != printer_list.end(); ++it) { | 539 for (printer_it = it->second->printers.begin(); |
297 if (it->printer_name == printer_name) { | 540 printer_it != it->second->printers.end(); ++printer_it) { |
298 *info = *it; | 541 if (printer_it->printer_name == printer_name) { |
| 542 *info = *printer_it; |
299 return true; | 543 return true; |
300 } | 544 } |
301 } | 545 } |
302 return false; | 546 return false; |
303 } | 547 } |
304 | 548 |
305 PrintSystem::PrintServerWatcher* | 549 PrintSystem::PrintServerWatcher* |
306 PrintSystemCUPS::CreatePrintServerWatcher() { | 550 PrintSystemCUPS::CreatePrintServerWatcher() { |
307 return new PrintServerWatcherCUPS(); | 551 DCHECK(initialized_); |
| 552 return new PrintServerWatcherCUPS(this); |
308 } | 553 } |
309 | 554 |
310 PrintSystem::PrinterWatcher* PrintSystemCUPS::CreatePrinterWatcher( | 555 PrintSystem::PrinterWatcher* PrintSystemCUPS::CreatePrinterWatcher( |
311 const std::string& printer_name) { | 556 const std::string& printer_name) { |
| 557 DCHECK(initialized_); |
312 DCHECK(!printer_name.empty()); | 558 DCHECK(!printer_name.empty()); |
313 return new PrinterWatcherCUPS(this, printer_name); | 559 return new PrinterWatcherCUPS(this, printer_name); |
314 } | 560 } |
315 | 561 |
316 PrintSystem::JobSpooler* PrintSystemCUPS::CreateJobSpooler() { | 562 PrintSystem::JobSpooler* PrintSystemCUPS::CreateJobSpooler() { |
| 563 DCHECK(initialized_); |
317 return new JobSpoolerCUPS(this); | 564 return new JobSpoolerCUPS(this); |
318 } | 565 } |
319 | 566 |
320 std::string PrintSystem::GenerateProxyId() { | 567 std::string PrintSystem::GenerateProxyId() { |
321 // TODO(gene): This code should generate a unique id for proxy. ID should be | 568 // TODO(gene): This code should generate a unique id for proxy. ID should be |
322 // unique for this user. Rand may return the same number. We'll need to change | 569 // unique for this user. Rand may return the same number. We'll need to change |
323 // this in the future. | 570 // this in the future. |
324 std::string id("CP_PROXY_"); | 571 std::string id("CP_PROXY_"); |
325 id += base::Uint64ToString(base::RandUint64()); | 572 id += base::Uint64ToString(base::RandUint64()); |
326 return id; | 573 return id; |
327 } | 574 } |
328 | 575 |
329 scoped_refptr<PrintSystem> PrintSystem::CreateInstance( | 576 scoped_refptr<PrintSystem> PrintSystem::CreateInstance( |
330 const DictionaryValue* print_system_settings) { | 577 const DictionaryValue* print_system_settings) { |
331 std::string print_server_url_str; | 578 return new PrintSystemCUPS(print_system_settings); |
332 if (print_system_settings) { | |
333 print_system_settings->GetString(kCUPSPrintServerURL, | |
334 &print_server_url_str); | |
335 } | |
336 GURL print_server_url(print_server_url_str.c_str()); | |
337 return new PrintSystemCUPS(print_server_url, print_system_settings); | |
338 } | 579 } |
339 | 580 |
340 int PrintSystemCUPS::PrintFile(const char* name, const char* filename, | 581 int PrintSystemCUPS::PrintFile(const GURL& url, const char* name, |
341 const char* title, int num_options, | 582 const char* filename, const char* title, |
342 cups_option_t* options) { | 583 int num_options, cups_option_t* options) { |
343 if (print_server_url_.is_empty()) { // Use default (local) print server. | 584 DCHECK(initialized_); |
| 585 if (url.is_empty()) { // Use default (local) print server. |
344 return cupsPrintFile(name, filename, title, num_options, options); | 586 return cupsPrintFile(name, filename, title, num_options, options); |
345 } else { | 587 } else { |
346 printing::HttpConnectionCUPS http(print_server_url_); | 588 printing::HttpConnectionCUPS http(url); |
347 return cupsPrintFile2(http.http(), name, filename, | 589 return cupsPrintFile2(http.http(), name, filename, |
348 title, num_options, options); | 590 title, num_options, options); |
349 } | 591 } |
350 } | 592 } |
351 | 593 |
352 int PrintSystemCUPS::GetJobs(cups_job_t** jobs, const char* name, | 594 int PrintSystemCUPS::GetJobs(cups_job_t** jobs, const GURL& url, |
353 int myjobs, int whichjobs) { | 595 const char* name, int myjobs, int whichjobs) { |
354 if (print_server_url_.is_empty()) { // Use default (local) print server. | 596 DCHECK(initialized_); |
| 597 if (url.is_empty()) { // Use default (local) print server. |
355 return cupsGetJobs(jobs, name, myjobs, whichjobs); | 598 return cupsGetJobs(jobs, name, myjobs, whichjobs); |
356 } else { | 599 } else { |
357 printing::HttpConnectionCUPS http(print_server_url_); | 600 printing::HttpConnectionCUPS http(url); |
358 return cupsGetJobs2(http.http(), jobs, name, myjobs, whichjobs); | 601 return cupsGetJobs2(http.http(), jobs, name, myjobs, whichjobs); |
359 } | 602 } |
360 } | 603 } |
361 | 604 |
362 PlatformJobId PrintSystemCUPS::SpoolPrintJob( | 605 PlatformJobId PrintSystemCUPS::SpoolPrintJob( |
363 const std::string& print_ticket, | 606 const std::string& print_ticket, |
364 const FilePath& print_data_file_path, | 607 const FilePath& print_data_file_path, |
365 const std::string& print_data_mime_type, | 608 const std::string& print_data_mime_type, |
366 const std::string& printer_name, | 609 const std::string& printer_name, |
367 const std::string& job_title) { | 610 const std::string& job_title) { |
| 611 DCHECK(initialized_); |
368 VLOG(1) << "CP_CUPS: Spooling print job for: " << printer_name; | 612 VLOG(1) << "CP_CUPS: Spooling print job for: " << printer_name; |
369 | 613 |
| 614 PrintersMap::iterator print_server = printer_map_.find(printer_name); |
| 615 if (print_server == printer_map_.end()) |
| 616 return 0; |
| 617 |
370 // We need to store options as char* string for the duration of the | 618 // We need to store options as char* string for the duration of the |
371 // cupsPrintFile2 call. We'll use map here to store options, since | 619 // cupsPrintFile2 call. We'll use map here to store options, since |
372 // Dictionary value from JSON parser returns wchat_t. | 620 // Dictionary value from JSON parser returns wchat_t. |
373 std::map<std::string, std::string> options; | 621 std::map<std::string, std::string> options; |
374 bool res = ParsePrintTicket(print_ticket, &options); | 622 bool res = ParsePrintTicket(print_ticket, &options); |
375 DCHECK(res); // If print ticket is invalid we still print using defaults. | 623 DCHECK(res); // If print ticket is invalid we still print using defaults. |
376 | 624 |
377 std::vector<cups_option_t> cups_options; | 625 std::vector<cups_option_t> cups_options; |
378 std::map<std::string, std::string>::iterator it; | 626 std::map<std::string, std::string>::iterator it; |
379 for (it = options.begin(); it != options.end(); ++it) { | 627 for (it = options.begin(); it != options.end(); ++it) { |
380 cups_option_t opt; | 628 cups_option_t opt; |
381 opt.name = const_cast<char*>(it->first.c_str()); | 629 opt.name = const_cast<char*>(it->first.c_str()); |
382 opt.value = const_cast<char*>(it->second.c_str()); | 630 opt.value = const_cast<char*>(it->second.c_str()); |
383 cups_options.push_back(opt); | 631 cups_options.push_back(opt); |
384 } | 632 } |
385 | 633 |
386 int job_id = PrintFile(printer_name.c_str(), | 634 int job_id = PrintFile(print_server->second->url, |
| 635 printer_name.c_str(), |
387 print_data_file_path.value().c_str(), | 636 print_data_file_path.value().c_str(), |
388 job_title.c_str(), | 637 job_title.c_str(), |
389 cups_options.size(), | 638 cups_options.size(), |
390 &(cups_options[0])); | 639 &(cups_options[0])); |
391 | 640 |
392 VLOG(1) << "CP_CUPS: Job spooled, id: " << job_id; | 641 VLOG(1) << "CP_CUPS: Job spooled, id: " << job_id; |
393 | 642 |
394 return job_id; | 643 return job_id; |
395 } | 644 } |
396 | 645 |
397 } // namespace cloud_print | 646 } // namespace cloud_print |
OLD | NEW |