blob: 2477e6652c38fec078fdcf8d93fa7aedccb22cb2 [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/service/cloud_print/cloud_print_helpers.h"
#include "base/json/json_reader.h"
#include "base/md5.h"
#include "base/memory/scoped_ptr.h"
#include "base/rand_util.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "base/task.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/service/cloud_print/cloud_print_consts.h"
#include "chrome/service/service_process.h"
#include "content/common/net/url_fetcher.h"
std::string StringFromJobStatus(cloud_print::PrintJobStatus status) {
std::string ret;
switch (status) {
case cloud_print::PRINT_JOB_STATUS_IN_PROGRESS:
ret = "in_progress";
break;
case cloud_print::PRINT_JOB_STATUS_ERROR:
ret = "error";
break;
case cloud_print::PRINT_JOB_STATUS_COMPLETED:
ret = "done";
break;
default:
ret = "unknown";
NOTREACHED();
break;
}
return ret;
}
// Appends a relative path to the url making sure to append a '/' if the
// URL's path does not end with a slash. It is assumed that |path| does not
// begin with a '/'.
// NOTE: Since we ALWAYS want to append here, we simply append the path string
// instead of calling url_utils::ResolveRelative. The input |url| may or may not
// contain a '/' at the end.
std::string AppendPathToUrl(const GURL& url, const std::string& path) {
DCHECK(path[0] != '/');
std::string ret = url.path();
if (url.has_path() && (ret[ret.length() - 1] != '/')) {
ret += '/';
}
ret += path;
return ret;
}
GURL CloudPrintHelpers::GetUrlForPrinterRegistration(
const GURL& cloud_print_server_url) {
std::string path(AppendPathToUrl(cloud_print_server_url, "register"));
GURL::Replacements replacements;
replacements.SetPathStr(path);
return cloud_print_server_url.ReplaceComponents(replacements);
}
GURL CloudPrintHelpers::GetUrlForPrinterUpdate(
const GURL& cloud_print_server_url, const std::string& printer_id) {
std::string path(AppendPathToUrl(cloud_print_server_url, "update"));
GURL::Replacements replacements;
replacements.SetPathStr(path);
std::string query = StringPrintf("printerid=%s", printer_id.c_str());
replacements.SetQueryStr(query);
return cloud_print_server_url.ReplaceComponents(replacements);
}
GURL CloudPrintHelpers::GetUrlForPrinterDelete(
const GURL& cloud_print_server_url, const std::string& printer_id) {
std::string path(AppendPathToUrl(cloud_print_server_url, "delete"));
GURL::Replacements replacements;
replacements.SetPathStr(path);
std::string query = StringPrintf("printerid=%s", printer_id.c_str());
replacements.SetQueryStr(query);
return cloud_print_server_url.ReplaceComponents(replacements);
}
GURL CloudPrintHelpers::GetUrlForPrinterList(const GURL& cloud_print_server_url,
const std::string& proxy_id) {
std::string path(AppendPathToUrl(cloud_print_server_url, "list"));
GURL::Replacements replacements;
replacements.SetPathStr(path);
std::string query = StringPrintf("proxy=%s", proxy_id.c_str());
replacements.SetQueryStr(query);
return cloud_print_server_url.ReplaceComponents(replacements);
}
GURL CloudPrintHelpers::GetUrlForJobFetch(const GURL& cloud_print_server_url,
const std::string& printer_id,
const std::string& reason) {
std::string path(AppendPathToUrl(cloud_print_server_url, "fetch"));
GURL::Replacements replacements;
replacements.SetPathStr(path);
std::string query = StringPrintf("printerid=%s&deb=%s",
printer_id.c_str(),
reason.c_str());
replacements.SetQueryStr(query);
return cloud_print_server_url.ReplaceComponents(replacements);
}
GURL CloudPrintHelpers::GetUrlForJobStatusUpdate(
const GURL& cloud_print_server_url, const std::string& job_id,
cloud_print::PrintJobStatus status) {
std::string status_string = StringFromJobStatus(status);
std::string path(AppendPathToUrl(cloud_print_server_url, "control"));
GURL::Replacements replacements;
replacements.SetPathStr(path);
std::string query = StringPrintf("jobid=%s&status=%s",
job_id.c_str(), status_string.c_str());
replacements.SetQueryStr(query);
return cloud_print_server_url.ReplaceComponents(replacements);
}
GURL CloudPrintHelpers::GetUrlForJobStatusUpdate(
const GURL& cloud_print_server_url, const std::string& job_id,
const cloud_print::PrintJobDetails& details) {
std::string status_string = StringFromJobStatus(details.status);
std::string path(AppendPathToUrl(cloud_print_server_url, "control"));
GURL::Replacements replacements;
replacements.SetPathStr(path);
std::string query =
StringPrintf("jobid=%s&status=%s&code=%d&message=%s"
"&numpages=%d&pagesprinted=%d",
job_id.c_str(),
status_string.c_str(),
details.platform_status_flags,
details.status_message.c_str(),
details.total_pages,
details.pages_printed);
replacements.SetQueryStr(query);
return cloud_print_server_url.ReplaceComponents(replacements);
}
GURL CloudPrintHelpers::GetUrlForUserMessage(const GURL& cloud_print_server_url,
const std::string& message_id) {
std::string path(AppendPathToUrl(cloud_print_server_url, "user/message"));
GURL::Replacements replacements;
replacements.SetPathStr(path);
std::string query = StringPrintf("code=%s", message_id.c_str());
replacements.SetQueryStr(query);
return cloud_print_server_url.ReplaceComponents(replacements);
}
GURL CloudPrintHelpers::GetUrlForGetAuthCode(const GURL& cloud_print_server_url,
const std::string& oauth_client_id,
const std::string& proxy_id) {
// We use the internal API "createrobot" instead of "getauthcode". This API
// will add the robot as owner to all the existing printers for this user.
std::string path(AppendPathToUrl(cloud_print_server_url, "createrobot"));
GURL::Replacements replacements;
replacements.SetPathStr(path);
std::string query = StringPrintf("oauth_client_id=%s&proxy=%s",
oauth_client_id.c_str(),
proxy_id.c_str());
replacements.SetQueryStr(query);
return cloud_print_server_url.ReplaceComponents(replacements);
}
bool CloudPrintHelpers::ParseResponseJSON(
const std::string& response_data, bool* succeeded,
DictionaryValue** response_dict) {
scoped_ptr<Value> message_value(base::JSONReader::Read(response_data, false));
if (!message_value.get())
return false;
if (!message_value->IsType(Value::TYPE_DICTIONARY))
return false;
scoped_ptr<DictionaryValue> response_dict_local(
static_cast<DictionaryValue*>(message_value.release()));
if (succeeded) {
if (!response_dict_local->GetBoolean(kSuccessValue, succeeded))
*succeeded = false;
}
if (response_dict)
*response_dict = response_dict_local.release();
return true;
}
void CloudPrintHelpers::AddMultipartValueForUpload(
const std::string& value_name, const std::string& value,
const std::string& mime_boundary, const std::string& content_type,
std::string* post_data) {
DCHECK(post_data);
// First line is the boundary
post_data->append("--" + mime_boundary + "\r\n");
// Next line is the Content-disposition
post_data->append(StringPrintf("Content-Disposition: form-data; "
"name=\"%s\"\r\n", value_name.c_str()));
if (!content_type.empty()) {
// If Content-type is specified, the next line is that
post_data->append(StringPrintf("Content-Type: %s\r\n",
content_type.c_str()));
}
// Leave an empty line and append the value.
post_data->append(StringPrintf("\r\n%s\r\n", value.c_str()));
}
// Create a MIME boundary marker (27 '-' characters followed by 16 hex digits).
void CloudPrintHelpers::CreateMimeBoundaryForUpload(std::string* out) {
int r1 = base::RandInt(0, kint32max);
int r2 = base::RandInt(0, kint32max);
base::SStringPrintf(out, "---------------------------%08X%08X", r1, r2);
}
std::string CloudPrintHelpers::GenerateHashOfStringMap(
const std::map<std::string, std::string>& string_map) {
std::string values_list;
std::map<std::string, std::string>::const_iterator it;
for (it = string_map.begin(); it != string_map.end(); ++it) {
values_list.append(it->first);
values_list.append(it->second);
}
return base::MD5String(values_list);
}
void CloudPrintHelpers::GenerateMultipartPostDataForPrinterTags(
const std::map<std::string, std::string>& printer_tags,
const std::string& mime_boundary,
std::string* post_data) {
// We do not use the GenerateHashOfStringMap to compute the hash because that
// method iterates through the map again. Since we are already iterating here
// we just compute it on the fly.
// Also, in some cases, the code that calls this has already computed the
// hash. We could take in the precomputed hash as an argument but that just
// makes the code more complicated.
std::string tags_list;
std::map<std::string, std::string>::const_iterator it;
for (it = printer_tags.begin(); it != printer_tags.end(); ++it) {
// TODO(gene) Escape '=' char from name. Warning for now.
if (it->first.find('=') != std::string::npos) {
LOG(WARNING) <<
"CP_PROXY: Printer option name contains '=' character";
NOTREACHED();
}
tags_list.append(it->first);
tags_list.append(it->second);
// All our tags have a special prefix to identify them as such.
std::string msg(kProxyTagPrefix);
msg += it->first;
msg += "=";
msg += it->second;
AddMultipartValueForUpload(kPrinterTagValue, msg, mime_boundary,
std::string(), post_data);
}
std::string tags_hash = base::MD5String(tags_list);
std::string tags_hash_msg(kTagsHashTagName);
tags_hash_msg += "=";
tags_hash_msg += tags_hash;
AddMultipartValueForUpload(kPrinterTagValue, tags_hash_msg, mime_boundary,
std::string(), post_data);
}
bool CloudPrintHelpers::IsDryRunJob(const std::vector<std::string>& tags) {
std::vector<std::string>::const_iterator it;
for (it = tags.begin(); it != tags.end(); ++it) {
if (*it == kTagDryRunFlag)
return true;
}
return false;
}