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

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

Powered by Google App Engine
This is Rietveld 408576698