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

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

Issue 8387011: Chrome proxy refactoring. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 2 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) 2011 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/cloud_print_connector.h"
6
7 #include "base/md5.h"
8 #include "base/rand_util.h"
9 #include "base/string_number_conversions.h"
10 #include "base/string_split.h"
11 #include "base/stringprintf.h"
12 #include "base/utf_string_conversions.h"
13 #include "base/values.h"
14 #include "chrome/service/cloud_print/cloud_print_consts.h"
15 #include "chrome/service/cloud_print/cloud_print_helpers.h"
16 #include "grit/generated_resources.h"
17 #include "ui/base/l10n/l10n_util.h"
18
19 CloudPrintConnector::CloudPrintConnector(
20 Client* client,
21 const std::string& proxy_id,
22 const GURL& cloud_print_server_url,
23 const DictionaryValue* print_system_settings)
24 : client_(client),
25 proxy_id_(proxy_id),
26 cloud_print_server_url_(cloud_print_server_url),
27 next_response_handler_(NULL) {
28 if (print_system_settings) {
29 // It is possible to have no print settings specified.
30 print_system_settings_.reset(print_system_settings->DeepCopy());
31 }
32 }
33
34 bool CloudPrintConnector::Start() {
35 DCHECK(!print_system_.get());
36 VLOG(1) << "CP_PROXY: Starting proxy, id: " << proxy_id_;
37
38 pedning_tasks_.clear();
39
40 print_system_ =
41 cloud_print::PrintSystem::CreateInstance(print_system_settings_.get());
42 if (!print_system_.get()) {
43 NOTREACHED();
44 return false; // No print system available, fail initalization.
45 }
46 cloud_print::PrintSystem::PrintSystemResult result = print_system_->Init();
47 if (!result.succeeded()) {
48 // We could not initialize the print system. We need to notify the server.
49 ReportUserMessage(kPrintSystemFailedMessageId, result.message());
50 print_system_.release();
51 return false;
52 }
53
54 // Start watching for updates from the print system.
55 print_server_watcher_ = print_system_->CreatePrintServerWatcher();
56 print_server_watcher_->StartWatching(this);
57
58 // Get list of registered printers.
59 AddPendingAvailableTask();
60 return true;
61 }
62
63 void CloudPrintConnector::Stop() {
64 VLOG(1) << "CP_PROXY: Stopping proxy, id: " << proxy_id_;
65 DCHECK(print_system_.get());
66 if (print_system_.get()) {
67 // Do uninitialization here.
68 pedning_tasks_.clear();
69 print_server_watcher_.release();
70 print_system_.release();
71 }
72 }
73
74 bool CloudPrintConnector::IsRunning() {
75 return print_system_.get() != NULL;
76 }
77
78 void CloudPrintConnector::RegisterPrinters(
79 const printing::PrinterList& printers) {
80 if (!IsRunning())
81 return;
82 printing::PrinterList::const_iterator it;
83 for (it = printers.begin(); it != printers.end(); ++it) {
84 AddPendingRegisterTask(*it);
85 }
86 }
87
88 // Check for jobs for specific printer
89 void CloudPrintConnector::CheckForJobs(const std::string& reason,
90 const std::string& printer_id) {
91 if (!IsRunning())
92 return;
93 if (!printer_id.empty()) {
94 JobHandlerMap::iterator index = job_handler_map_.find(printer_id);
95 if (index != job_handler_map_.end())
96 index->second->CheckForJobs(reason);
97 } else {
98 for (JobHandlerMap::iterator index = job_handler_map_.begin();
99 index != job_handler_map_.end(); index++) {
100 index->second->CheckForJobs(reason);
101 }
102 }
103 }
104
105 void CloudPrintConnector::OnPrinterAdded() {
106 AddPendingAvailableTask();
107 }
108
109 void CloudPrintConnector::OnPrinterDeleted(const std::string& printer_id) {
110 AddPendingDeleteTask(printer_id);
111 }
112
113 void CloudPrintConnector::OnAuthError() {
114 client_->OnAuthFailed();
115 }
116
117 // CloudPrintURLFetcher::Delegate implementation.
118 CloudPrintURLFetcher::ResponseAction CloudPrintConnector::HandleRawData(
119 const URLFetcher* source,
120 const GURL& url,
121 const std::string& data) {
122 // If this notification came as a result of user message call, stop it.
123 // Otherwise proceed continue processing.
124 if (user_message_request_.get() &&
125 user_message_request_->IsSameRequest(source))
126 return CloudPrintURLFetcher::STOP_PROCESSING;
127 return CloudPrintURLFetcher::CONTINUE_PROCESSING;
128 }
129
130 CloudPrintURLFetcher::ResponseAction CloudPrintConnector::HandleJSONData(
131 const URLFetcher* source,
132 const GURL& url,
133 DictionaryValue* json_data,
134 bool succeeded) {
135 if (!IsRunning()) // Orphant response. Connector has been stopped already.
136 return CloudPrintURLFetcher::STOP_PROCESSING;
137
138 DCHECK(next_response_handler_);
139 return (this->*next_response_handler_)(source, url, json_data, succeeded);
140 }
141
142 CloudPrintURLFetcher::ResponseAction
143 CloudPrintConnector::HandlePrinterListResponse(
144 const URLFetcher* source,
145 const GURL& url,
146 DictionaryValue* json_data,
147 bool succeeded) {
148 DCHECK(succeeded);
149 if (!succeeded)
150 return CloudPrintURLFetcher::RETRY_REQUEST;
151
152 // Now we need to get the list of printers from the print system
153 // and split printers into 3 categories:
154 // - existing and registered printers
155 // - new printers
156 // - deleted printers
157
158 // Get list of the printers from the print system.
159 printing::PrinterList local_printers;
160 cloud_print::PrintSystem::PrintSystemResult result =
161 print_system_->EnumeratePrinters(&local_printers);
162 bool full_list = result.succeeded();
163 if (!result.succeeded()) {
164 std::string message = result.message();
165 if (message.empty())
166 message = l10n_util::GetStringUTF8(IDS_CLOUD_PRINT_ENUM_FAILED);
167 // There was a failure enumerating printers. Send a message to the server.
168 ReportUserMessage(kEnumPrintersFailedMessageId, message);
169 }
170
171 // Go through the list of the cloud printers and init print job handlers.
172 ListValue* printer_list = NULL;
173 // There may be no "printers" value in the JSON
174 if (json_data->GetList(kPrinterListValue, &printer_list) && printer_list) {
175 for (size_t index = 0; index < printer_list->GetSize(); index++) {
176 DictionaryValue* printer_data = NULL;
177 if (printer_list->GetDictionary(index, &printer_data)) {
178 std::string printer_name;
179 printer_data->GetString(kNameValue, &printer_name);
180 if (RemovePrinterFromList(printer_name, &local_printers)) {
181 InitJobHandlerForPrinter(printer_data);
182 } else {
183 // Cloud printer is not found on the local system.
184 if (full_list) { // Delete only if we get the full list of printer.
185 std::string printer_id;
186 printer_data->GetString(kIdValue, &printer_id);
187 AddPendingDeleteTask(printer_id);
188 }
189 }
190 } else {
191 NOTREACHED();
192 }
193 }
194 }
195
196 request_ = NULL;
197 if (!local_printers.empty()) {
198 // Notify client that we have a list of printers available.
199 // Client will call us back to finish registration
200 client_->OnPrintersAvailable(local_printers);
201 }
202 ContinuePendingTaskProcessing(); // Continue processing background tasks.
203 return CloudPrintURLFetcher::STOP_PROCESSING;
204 }
205
206 CloudPrintURLFetcher::ResponseAction
207 CloudPrintConnector::HandlePrinterDeleteResponse(
208 const URLFetcher* source,
209 const GURL& url,
210 DictionaryValue* json_data,
211 bool succeeded) {
212 VLOG(1) << "CP_PROXY: Handler printer delete response, succeeded:"
213 << succeeded << " url: " << url;
214 ContinuePendingTaskProcessing(); // Continue processing background tasks.
215 return CloudPrintURLFetcher::STOP_PROCESSING;
216 }
217
218 CloudPrintURLFetcher::ResponseAction
219 CloudPrintConnector::HandleRegisterPrinterResponse(
220 const URLFetcher* source,
221 const GURL& url,
222 DictionaryValue* json_data,
223 bool succeeded) {
224 VLOG(1) << "CP_PROXY: Handler printer register response, succeeded:"
225 << succeeded << " url: " << url;
226 if (succeeded) {
227 ListValue* printer_list = NULL;
228 // There should be a "printers" value in the JSON
229 if (json_data->GetList(kPrinterListValue, &printer_list)) {
230 DictionaryValue* printer_data = NULL;
231 if (printer_list->GetDictionary(0, &printer_data))
232 InitJobHandlerForPrinter(printer_data);
233 }
234 }
235 ContinuePendingTaskProcessing(); // Continue processing background tasks.
236 return CloudPrintURLFetcher::STOP_PROCESSING;
237 }
238
239
240 CloudPrintURLFetcher::ResponseAction CloudPrintConnector::OnRequestAuthError() {
241 OnAuthError();
242 return CloudPrintURLFetcher::STOP_PROCESSING;
243 }
244
245 std::string CloudPrintConnector::GetAuthHeader() {
246 return CloudPrintHelpers::GetCloudPrintAuthHeader();
247 }
248
249 void CloudPrintConnector::StartGetRequest(const GURL& url,
250 int max_retries,
251 ResponseHandler handler) {
252 next_response_handler_ = handler;
253 request_ = new CloudPrintURLFetcher;
254 request_->StartGetRequest(url, this, max_retries, std::string());
255 }
256
257 void CloudPrintConnector::StartPostRequest(const GURL& url,
258 int max_retries,
259 const std::string& mime_type,
260 const std::string& post_data,
261 ResponseHandler handler) {
262 next_response_handler_ = handler;
263 request_ = new CloudPrintURLFetcher;
264 request_->StartPostRequest(
265 url, this, max_retries, mime_type, post_data, std::string());
266 }
267
268 void CloudPrintConnector::ReportUserMessage(const std::string& message_id,
269 const std::string& failure_msg) {
270 // This is a fire and forget type of function.
271 // Result of this request will be ignored.
272 std::string mime_boundary;
273 CloudPrintHelpers::CreateMimeBoundaryForUpload(&mime_boundary);
274 GURL url = CloudPrintHelpers::GetUrlForUserMessage(cloud_print_server_url_,
275 message_id);
276 std::string post_data;
277 CloudPrintHelpers::AddMultipartValueForUpload(kMessageTextValue,
278 failure_msg,
279 mime_boundary,
280 std::string(),
281 &post_data);
282 // Terminate the request body
283 post_data.append("--" + mime_boundary + "--\r\n");
284 std::string mime_type("multipart/form-data; boundary=");
285 mime_type += mime_boundary;
286 user_message_request_ = new CloudPrintURLFetcher;
287 user_message_request_->StartPostRequest(url, this, 1, mime_type, post_data,
288 std::string());
289 }
290
291 bool CloudPrintConnector::RemovePrinterFromList(
292 const std::string& printer_name,
293 printing::PrinterList* printer_list) {
294 for (printing::PrinterList::iterator index = printer_list->begin();
295 index != printer_list->end(); index++) {
296 if (IsSamePrinter(index->printer_name, printer_name)) {
297 index = printer_list->erase(index);
298 return true;
299 }
300 }
301 return false;
302 }
303
304 void CloudPrintConnector::InitJobHandlerForPrinter(
305 DictionaryValue* printer_data) {
306 DCHECK(printer_data);
307 PrinterJobHandler::PrinterInfoFromCloud printer_info_cloud;
308 printer_data->GetString(kIdValue, &printer_info_cloud.printer_id);
309 DCHECK(!printer_info_cloud.printer_id.empty());
310 VLOG(1) << "CP_PROXY: Init job handler for printer id: "
311 << printer_info_cloud.printer_id;
312 JobHandlerMap::iterator index = job_handler_map_.find(
313 printer_info_cloud.printer_id);
314 if (index != job_handler_map_.end())
315 return; // Nothing to do if we already have a job handler for this printer.
316
317 printing::PrinterBasicInfo printer_info;
318 printer_data->GetString(kNameValue, &printer_info.printer_name);
319 DCHECK(!printer_info.printer_name.empty());
320 printer_data->GetString(kPrinterDescValue,
321 &printer_info.printer_description);
322 // Printer status is a string value which actually contains an integer.
323 std::string printer_status;
324 if (printer_data->GetString(kPrinterStatusValue, &printer_status)) {
325 base::StringToInt(printer_status, &printer_info.printer_status);
326 }
327 printer_data->GetString(kPrinterCapsHashValue,
328 &printer_info_cloud.caps_hash);
329 ListValue* tags_list = NULL;
330 if (printer_data->GetList(kTagsValue, &tags_list) && tags_list) {
331 for (size_t index = 0; index < tags_list->GetSize(); index++) {
332 std::string tag;
333 if (tags_list->GetString(index, &tag) &&
334 StartsWithASCII(tag, kTagsHashTagName, false)) {
335 std::vector<std::string> tag_parts;
336 base::SplitStringDontTrim(tag, '=', &tag_parts);
337 DCHECK_EQ(tag_parts.size(), 2U);
338 if (tag_parts.size() == 2)
339 printer_info_cloud.tags_hash = tag_parts[1];
340 }
341 }
342 }
343 scoped_refptr<PrinterJobHandler> job_handler;
344 job_handler = new PrinterJobHandler(printer_info,
345 printer_info_cloud,
346 cloud_print_server_url_,
347 print_system_.get(),
348 this);
349 job_handler_map_[printer_info_cloud.printer_id] = job_handler;
350 job_handler->Initialize();
351 }
352
353 void CloudPrintConnector::AddPendingAvailableTask() {
354 PendingTask task;
355 task.type = PEDNING_PRINTERS_AVAILABLE;
356 AddPendingTask(task);
357 }
358
359 void CloudPrintConnector::AddPendingDeleteTask(const std::string& id) {
360 PendingTask task;
361 task.type = PEDNING_PRINTER_DELETE;
362 task.printer_id = id;
363 AddPendingTask(task);
364 }
365
366 void CloudPrintConnector::AddPendingRegisterTask(
367 const printing::PrinterBasicInfo& info) {
368 PendingTask task;
369 task.type = PEDNING_PRINTER_REGISTER;
370 task.printer_info = info;
371 AddPendingTask(task);
372 }
373
374 void CloudPrintConnector::AddPendingTask(const PendingTask& task) {
375 pedning_tasks_.push_back(task);
376 // If this is the only pending task, we need to start the process.
377 if (pedning_tasks_.size() == 1) {
378 MessageLoop::current()->PostTask(
379 FROM_HERE, NewRunnableMethod(
380 this, &CloudPrintConnector::ProcessPendingTask));
381 }
382 }
383
384 void CloudPrintConnector::ProcessPendingTask() {
385 if (!IsRunning())
386 return; // Orphant call.
387 if (pedning_tasks_.size() == 0)
388 return; // No peding tasks.
389
390 PendingTask task = pedning_tasks_.front();
391
392 switch (task.type) {
393 case PEDNING_PRINTERS_AVAILABLE :
394 OnPrintersAvailable();
395 break;
396 case PEDNING_PRINTER_REGISTER :
397 OnPrinterRegister(task.printer_info);
398 break;
399 case PEDNING_PRINTER_DELETE :
400 OnPrinterDelete(task.printer_id);
401 break;
402 default:
403 NOTREACHED();
404 }
405 }
406
407 void CloudPrintConnector::OnPrintersAvailable() {
408 GURL printer_list_url =
409 CloudPrintHelpers::GetUrlForPrinterList(cloud_print_server_url_,
410 proxy_id_);
411 StartGetRequest(printer_list_url,
412 kCloudPrintRegisterMaxRetryCount,
413 &CloudPrintConnector::HandlePrinterListResponse);
414 }
415
416 void CloudPrintConnector::OnPrinterRegister(
417 const printing::PrinterBasicInfo& info) {
418 for (JobHandlerMap::iterator it = job_handler_map_.begin();
419 it != job_handler_map_.end(); ++it) {
420 if (IsSamePrinter(it->second->GetPrinterName(), info.printer_name)) {
421 // Printer already registered, continue to the next task.
422 ContinuePendingTaskProcessing();
423 return;
424 }
425 }
426
427 cloud_print::PrintSystem::PrinterCapsAndDefaultsCallback* callback =
428 NewCallback(this, &CloudPrintConnector::OnReceivePrinterCaps);
429 // Asnchronously fetch the printer caps and defaults. The story will
430 // continue in OnReceivePrinterCaps.
431 print_system_->GetPrinterCapsAndDefaults(
432 info.printer_name.c_str(), callback);
433 }
434
435 void CloudPrintConnector::OnPrinterDelete(const std::string& printer_id) {
436 // Remove corresponding printer job handler.
437 JobHandlerMap::iterator it = job_handler_map_.find(printer_id);
438 if (it != job_handler_map_.end()) {
439 it->second->Shutdown();
440 job_handler_map_.erase(it);
441 }
442
443 // TODO(gene): We probably should not try indefinitely here. Just once or
444 // twice should be enough.
445 GURL url = CloudPrintHelpers::GetUrlForPrinterDelete(cloud_print_server_url_,
446 printer_id);
447 StartGetRequest(url,
448 kCloudPrintAPIMaxRetryCount,
449 &CloudPrintConnector::HandlePrinterDeleteResponse);
450 }
451
452 void CloudPrintConnector::ContinuePendingTaskProcessing() {
453 if (pedning_tasks_.size() == 0)
454 return; // No peding tasks.
455
456 // Delete current task and repost if we have more task avaialble.
457 pedning_tasks_.pop_front();
458 if (pedning_tasks_.size() != 0) {
459 MessageLoop::current()->PostTask(
460 FROM_HERE, NewRunnableMethod(
461 this, &CloudPrintConnector::ProcessPendingTask));
462 }
463 }
464
465 void CloudPrintConnector::OnReceivePrinterCaps(
466 bool succeeded,
467 const std::string& printer_name,
468 const printing::PrinterCapsAndDefaults& caps_and_defaults) {
469 if (!IsRunning())
470 return; // Orphant call.
471 DCHECK(pedning_tasks_.size() > 0 &&
472 pedning_tasks_.front().type == PEDNING_PRINTER_REGISTER);
473
474 if (!succeeded) {
475 LOG(ERROR) << "CP_PROXY: Failed to get printer info for: " <<
476 printer_name;
477 // This printer failed to register, notify the server of this failure.
478 string16 printer_name_utf16 = UTF8ToUTF16(printer_name);
479 std::string status_message = l10n_util::GetStringFUTF8(
480 IDS_CLOUD_PRINT_REGISTER_PRINTER_FAILED,
481 printer_name_utf16);
482 ReportUserMessage(kGetPrinterCapsFailedMessageId, status_message);
483
484 ContinuePendingTaskProcessing(); // Skip this printer registration.
485 return;
486 }
487
488 const printing::PrinterBasicInfo& info = pedning_tasks_.front().printer_info;
489 DCHECK(IsSamePrinter(info.printer_name, printer_name));
490
491 std::string mime_boundary;
492 CloudPrintHelpers::CreateMimeBoundaryForUpload(&mime_boundary);
493 std::string post_data;
494
495 CloudPrintHelpers::AddMultipartValueForUpload(kProxyIdValue, proxy_id_,
496 mime_boundary,
497 std::string(), &post_data);
498 CloudPrintHelpers::AddMultipartValueForUpload(kPrinterNameValue,
499 info.printer_name,
500 mime_boundary,
501 std::string(), &post_data);
502 CloudPrintHelpers::AddMultipartValueForUpload(kPrinterDescValue,
503 info.printer_description,
504 mime_boundary,
505 std::string() , &post_data);
506 CloudPrintHelpers::AddMultipartValueForUpload(
507 kPrinterStatusValue, base::StringPrintf("%d", info.printer_status),
508 mime_boundary, std::string(), &post_data);
509 // Add printer options as tags.
510 CloudPrintHelpers::GenerateMultipartPostDataForPrinterTags(info.options,
511 mime_boundary,
512 &post_data);
513
514 CloudPrintHelpers::AddMultipartValueForUpload(
515 kPrinterCapsValue, caps_and_defaults.printer_capabilities,
516 mime_boundary, caps_and_defaults.caps_mime_type,
517 &post_data);
518 CloudPrintHelpers::AddMultipartValueForUpload(
519 kPrinterDefaultsValue, caps_and_defaults.printer_defaults,
520 mime_boundary, caps_and_defaults.defaults_mime_type,
521 &post_data);
522 // Send a hash of the printer capabilities to the server. We will use this
523 // later to check if the capabilities have changed
524 CloudPrintHelpers::AddMultipartValueForUpload(
525 kPrinterCapsHashValue,
526 base::MD5String(caps_and_defaults.printer_capabilities),
527 mime_boundary, std::string(), &post_data);
528
529 // Terminate the request body
530 post_data.append("--" + mime_boundary + "--\r\n");
531 std::string mime_type("multipart/form-data; boundary=");
532 mime_type += mime_boundary;
533
534 GURL post_url = CloudPrintHelpers::GetUrlForPrinterRegistration(
535 cloud_print_server_url_);
536 StartPostRequest(post_url,
537 kCloudPrintAPIMaxRetryCount,
538 mime_type,
539 post_data,
540 &CloudPrintConnector::HandleRegisterPrinterResponse);
541 }
542
543 bool CloudPrintConnector::IsSamePrinter(const std::string& name1,
544 const std::string& name2) const {
545 return (0 == base::strcasecmp(name1.c_str(), name2.c_str()));
546 }
547
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698