1227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks// Copyright 2014 The Chromium Authors. All rights reserved. 2227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks// Use of this source code is governed by a BSD-style license that can be 3227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks// found in the LICENSE file. 4227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 5227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "components/search_provider_logos/google_logo_api.h" 6227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 7227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "base/base64.h" 8227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "base/json/json_reader.h" 9227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "base/memory/ref_counted_memory.h" 10227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "base/strings/string_util.h" 11227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "base/values.h" 12227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 13227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksnamespace search_provider_logos { 14227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 15227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksnamespace { 16227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksconst char kResponsePreamble[] = ")]}'"; 17227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks} 18227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 19227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin HendricksGURL GoogleAppendFingerprintToLogoURL(const GURL& logo_url, 20227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks const std::string& fingerprint) { 21227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks // Note: we can't just use net::AppendQueryParameter() because it escapes 22227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks // ":" to "%3A", but the server requires the colon not to be escaped. 23227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks // See: http://crbug.com/413845 24227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 25227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks // TODO(newt): Switch to using net::AppendQueryParameter once it no longer 26227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks // escapes ":" 27227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 28227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks std::string query(logo_url.query()); 29227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks if (!query.empty()) 30227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks query += "&"; 31227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 32227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks query += "async=es_dfp:"; 33227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks query += fingerprint; 34227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks GURL::Replacements replacements; 35227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks replacements.SetQueryStr(query); 36227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks return logo_url.ReplaceComponents(replacements); 37227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks} 38227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 39227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksscoped_ptr<EncodedLogo> GoogleParseLogoResponse( 40227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks const scoped_ptr<std::string>& response, 41227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks base::Time response_time) { 42227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks // Google doodles are sent as JSON with a prefix. Example: 43227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks // )]}' {"update":{"logo":{ 44227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks // "data": "/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/...", 45227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks // "mime_type": "image/png", 46227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks // "fingerprint": "db063e32", 47227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks // "target": "http://www.google.com.au/search?q=Wilbur+Christiansen", 48227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks // "alt": "Wilbur Christiansen's Birthday" 49227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks // "time_to_live": 1389304799 50227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks // }}} 51227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 52227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks // The response may start with )]}'. Ignore this. 53227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks base::StringPiece response_sp(*response); 54227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks if (response_sp.starts_with(kResponsePreamble)) 55227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks response_sp.remove_prefix(strlen(kResponsePreamble)); 56227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 57227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks scoped_ptr<base::Value> value(base::JSONReader::Read(response_sp)); 58227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks if (!value.get()) 59227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks return scoped_ptr<EncodedLogo>(); 60227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 61227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks // The important data lives inside several nested dictionaries: 62227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks // {"update": {"logo": { "mime_type": ..., etc } } } 63227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks const base::DictionaryValue* outer_dict; 64227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks if (!value->GetAsDictionary(&outer_dict)) 65227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks return scoped_ptr<EncodedLogo>(); 66227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks const base::DictionaryValue* update_dict; 67 if (!outer_dict->GetDictionary("update", &update_dict)) 68 return scoped_ptr<EncodedLogo>(); 69 const base::DictionaryValue* logo_dict; 70 if (!update_dict->GetDictionary("logo", &logo_dict)) 71 return scoped_ptr<EncodedLogo>(); 72 73 scoped_ptr<EncodedLogo> logo(new EncodedLogo()); 74 75 std::string encoded_image_base64; 76 if (logo_dict->GetString("data", &encoded_image_base64)) { 77 // Data is optional, since we may be revalidating a cached logo. 78 base::RefCountedString* encoded_image_string = new base::RefCountedString(); 79 if (!base::Base64Decode(encoded_image_base64, 80 &encoded_image_string->data())) 81 return scoped_ptr<EncodedLogo>(); 82 logo->encoded_image = encoded_image_string; 83 if (!logo_dict->GetString("mime_type", &logo->metadata.mime_type)) 84 return scoped_ptr<EncodedLogo>(); 85 } 86 87 // Don't check return values since these fields are optional. 88 logo_dict->GetString("target", &logo->metadata.on_click_url); 89 logo_dict->GetString("fingerprint", &logo->metadata.fingerprint); 90 logo_dict->GetString("alt", &logo->metadata.alt_text); 91 92 base::TimeDelta time_to_live; 93 int time_to_live_ms; 94 if (logo_dict->GetInteger("time_to_live", &time_to_live_ms)) { 95 time_to_live = base::TimeDelta::FromMilliseconds( 96 std::min(static_cast<int64>(time_to_live_ms), kMaxTimeToLiveMS)); 97 logo->metadata.can_show_after_expiration = false; 98 } else { 99 time_to_live = base::TimeDelta::FromMilliseconds(kMaxTimeToLiveMS); 100 logo->metadata.can_show_after_expiration = true; 101 } 102 logo->metadata.expiration_time = response_time + time_to_live; 103 104 return logo.Pass(); 105} 106 107} // namespace search_provider_logos 108