| OLD | NEW |
| (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/browser/bug_report_util.h" | |
| 6 | |
| 7 #include <sstream> | |
| 8 #include <string> | |
| 9 | |
| 10 #include "base/bind.h" | |
| 11 #include "base/command_line.h" | |
| 12 #include "base/file_util.h" | |
| 13 #include "base/file_version_info.h" | |
| 14 #include "base/memory/singleton.h" | |
| 15 #include "base/string_util.h" | |
| 16 #include "base/stringprintf.h" | |
| 17 #include "base/utf_string_conversions.h" | |
| 18 #include "base/win/windows_version.h" | |
| 19 #include "chrome/browser/browser_process_impl.h" | |
| 20 #include "chrome/browser/profiles/profile.h" | |
| 21 #include "chrome/browser/safe_browsing/safe_browsing_util.h" | |
| 22 #include "chrome/browser/ui/browser_list.h" | |
| 23 #include "chrome/common/chrome_switches.h" | |
| 24 #include "chrome/common/chrome_version_info.h" | |
| 25 #include "content/public/browser/web_contents.h" | |
| 26 #include "content/public/common/url_fetcher.h" | |
| 27 #include "content/public/common/url_fetcher_delegate.h" | |
| 28 #include "googleurl/src/gurl.h" | |
| 29 #include "grit/generated_resources.h" | |
| 30 #include "grit/locale_settings.h" | |
| 31 #include "grit/theme_resources.h" | |
| 32 #include "net/url_request/url_request_status.h" | |
| 33 #include "ui/base/l10n/l10n_util.h" | |
| 34 #include "unicode/locid.h" | |
| 35 | |
| 36 #if defined(OS_CHROMEOS) | |
| 37 #include "chrome/browser/chromeos/notifications/system_notification.h" | |
| 38 #endif | |
| 39 | |
| 40 using content::WebContents; | |
| 41 | |
| 42 namespace { | |
| 43 | |
| 44 const int kBugReportVersion = 1; | |
| 45 | |
| 46 const char kReportPhishingUrl[] = | |
| 47 "http://www.google.com/safebrowsing/report_phish/"; | |
| 48 | |
| 49 // URL to post bug reports to. | |
| 50 static char const kBugReportPostUrl[] = | |
| 51 "https://www.google.com/tools/feedback/chrome/__submit"; | |
| 52 | |
| 53 static char const kProtBufMimeType[] = "application/x-protobuf"; | |
| 54 static char const kPngMimeType[] = "image/png"; | |
| 55 | |
| 56 // Tags we use in product specific data | |
| 57 static char const kChromeVersionTag[] = "CHROME VERSION"; | |
| 58 static char const kOsVersionTag[] = "OS VERSION"; | |
| 59 | |
| 60 static char const kNotificationId[] = "feedback.chromeos"; | |
| 61 | |
| 62 static int const kHttpPostSuccessNoContent = 204; | |
| 63 static int const kHttpPostFailNoConnection = -1; | |
| 64 static int const kHttpPostFailClientError = 400; | |
| 65 static int const kHttpPostFailServerError = 500; | |
| 66 | |
| 67 #if defined(OS_CHROMEOS) | |
| 68 static char const kBZip2MimeType[] = "application/x-bzip2"; | |
| 69 static char const kLogsAttachmentName[] = "system_logs.bz2"; | |
| 70 // Maximum number of lines in system info log chunk to be still included | |
| 71 // in product specific data. | |
| 72 const size_t kMaxLineCount = 40; | |
| 73 // Maximum number of bytes in system info log chunk to be still included | |
| 74 // in product specific data. | |
| 75 const size_t kMaxSystemLogLength = 4 * 1024; | |
| 76 #endif | |
| 77 | |
| 78 const int64 kInitialRetryDelay = 900000; // 15 minutes | |
| 79 const int64 kRetryDelayIncreaseFactor = 2; | |
| 80 const int64 kRetryDelayLimit = 14400000; // 4 hours | |
| 81 | |
| 82 | |
| 83 } // namespace | |
| 84 | |
| 85 | |
| 86 // Simple content::URLFetcherDelegate to clean up URLFetcher on completion. | |
| 87 class BugReportUtil::PostCleanup : public content::URLFetcherDelegate { | |
| 88 public: | |
| 89 PostCleanup(Profile* profile, std::string* post_body, | |
| 90 int64 previous_delay) : profile_(profile), | |
| 91 post_body_(post_body), | |
| 92 previous_delay_(previous_delay) { } | |
| 93 // Overridden from content::URLFetcherDelegate. | |
| 94 virtual void OnURLFetchComplete(const content::URLFetcher* source); | |
| 95 | |
| 96 protected: | |
| 97 virtual ~PostCleanup() {} | |
| 98 | |
| 99 private: | |
| 100 Profile* profile_; | |
| 101 std::string* post_body_; | |
| 102 int64 previous_delay_; | |
| 103 | |
| 104 DISALLOW_COPY_AND_ASSIGN(PostCleanup); | |
| 105 }; | |
| 106 | |
| 107 // Don't use the data parameter, instead use the pointer we pass into every | |
| 108 // post cleanup object - that pointer will be deleted and deleted only on a | |
| 109 // successful post to the feedback server. | |
| 110 void BugReportUtil::PostCleanup::OnURLFetchComplete( | |
| 111 const content::URLFetcher* source) { | |
| 112 std::stringstream error_stream; | |
| 113 int response_code = source->GetResponseCode(); | |
| 114 if (response_code == kHttpPostSuccessNoContent) { | |
| 115 // We've sent our report, delete the report data | |
| 116 delete post_body_; | |
| 117 | |
| 118 error_stream << "Success"; | |
| 119 } else { | |
| 120 // Uh oh, feedback failed, send it off to retry | |
| 121 if (previous_delay_) { | |
| 122 if (previous_delay_ < kRetryDelayLimit) | |
| 123 previous_delay_ *= kRetryDelayIncreaseFactor; | |
| 124 } else { | |
| 125 previous_delay_ = kInitialRetryDelay; | |
| 126 } | |
| 127 BugReportUtil::DispatchFeedback(profile_, post_body_, previous_delay_); | |
| 128 | |
| 129 // Process the error for debug output | |
| 130 if (response_code == kHttpPostFailNoConnection) { | |
| 131 error_stream << "No connection to server."; | |
| 132 } else if ((response_code > kHttpPostFailClientError) && | |
| 133 (response_code < kHttpPostFailServerError)) { | |
| 134 error_stream << "Client error: HTTP response code " << response_code; | |
| 135 } else if (response_code > kHttpPostFailServerError) { | |
| 136 error_stream << "Server error: HTTP response code " << response_code; | |
| 137 } else { | |
| 138 error_stream << "Unknown error: HTTP response code " << response_code; | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 LOG(WARNING) << "FEEDBACK: Submission to feedback server (" << | |
| 143 source->GetURL() << ") status: " << error_stream.str(); | |
| 144 | |
| 145 // Delete the URLFetcher. | |
| 146 delete source; | |
| 147 // And then delete ourselves. | |
| 148 delete this; | |
| 149 } | |
| 150 | |
| 151 // static | |
| 152 void BugReportUtil::SetOSVersion(std::string* os_version) { | |
| 153 #if defined(OS_WIN) | |
| 154 base::win::OSInfo* os_info = base::win::OSInfo::GetInstance(); | |
| 155 base::win::OSInfo::VersionNumber version_number = os_info->version_number(); | |
| 156 *os_version = base::StringPrintf("%d.%d.%d", | |
| 157 version_number.major, | |
| 158 version_number.minor, | |
| 159 version_number.build); | |
| 160 int service_pack = os_info->service_pack().major; | |
| 161 if (service_pack > 0) | |
| 162 os_version->append(base::StringPrintf("Service Pack %d", service_pack)); | |
| 163 #elif defined(OS_MACOSX) | |
| 164 *os_version = base::SysInfo::OperatingSystemVersion(); | |
| 165 #else | |
| 166 *os_version = "unknown"; | |
| 167 #endif | |
| 168 } | |
| 169 | |
| 170 // static | |
| 171 void BugReportUtil::DispatchFeedback(Profile* profile, | |
| 172 std::string* post_body, | |
| 173 int64 delay) { | |
| 174 DCHECK(post_body); | |
| 175 | |
| 176 MessageLoop::current()->PostDelayedTask(FROM_HERE, base::Bind( | |
| 177 &BugReportUtil::SendFeedback, profile, post_body, delay), delay); | |
| 178 } | |
| 179 | |
| 180 // static | |
| 181 void BugReportUtil::SendFeedback(Profile* profile, | |
| 182 std::string* post_body, | |
| 183 int64 previous_delay) { | |
| 184 DCHECK(post_body); | |
| 185 | |
| 186 GURL post_url; | |
| 187 if (CommandLine::ForCurrentProcess()-> | |
| 188 HasSwitch(switches::kFeedbackServer)) | |
| 189 post_url = GURL(CommandLine::ForCurrentProcess()-> | |
| 190 GetSwitchValueASCII(switches::kFeedbackServer)); | |
| 191 else | |
| 192 post_url = GURL(kBugReportPostUrl); | |
| 193 | |
| 194 content::URLFetcher* fetcher = content::URLFetcher::Create( | |
| 195 post_url, content::URLFetcher::POST, | |
| 196 new BugReportUtil::PostCleanup(profile, post_body, previous_delay)); | |
| 197 fetcher->SetRequestContext(profile->GetRequestContext()); | |
| 198 | |
| 199 fetcher->SetUploadData(std::string(kProtBufMimeType), *post_body); | |
| 200 fetcher->Start(); | |
| 201 } | |
| 202 | |
| 203 | |
| 204 // static | |
| 205 void BugReportUtil::AddFeedbackData( | |
| 206 userfeedback::ExternalExtensionSubmit* feedback_data, | |
| 207 const std::string& key, const std::string& value) { | |
| 208 // Don't bother with empty keys or values | |
| 209 if (key=="" || value == "") return; | |
| 210 // Create log_value object and add it to the web_data object | |
| 211 userfeedback::ProductSpecificData log_value; | |
| 212 log_value.set_key(key); | |
| 213 log_value.set_value(value); | |
| 214 userfeedback::WebData* web_data = feedback_data->mutable_web_data(); | |
| 215 *(web_data->add_product_specific_data()) = log_value; | |
| 216 } | |
| 217 | |
| 218 #if defined(OS_CHROMEOS) | |
| 219 bool BugReportUtil::ValidFeedbackSize(const std::string& content) { | |
| 220 if (content.length() > kMaxSystemLogLength) | |
| 221 return false; | |
| 222 size_t line_count = 0; | |
| 223 const char* text = content.c_str(); | |
| 224 for (size_t i = 0; i < content.length(); i++) { | |
| 225 if (*(text + i) == '\n') { | |
| 226 line_count++; | |
| 227 if (line_count > kMaxLineCount) | |
| 228 return false; | |
| 229 } | |
| 230 } | |
| 231 return true; | |
| 232 } | |
| 233 #endif | |
| 234 | |
| 235 // static | |
| 236 void BugReportUtil::SendReport( | |
| 237 Profile* profile | |
| 238 , int problem_type | |
| 239 , const std::string& page_url_text | |
| 240 , const std::string& description | |
| 241 , ScreenshotDataPtr image_data_ptr | |
| 242 , int png_width | |
| 243 , int png_height | |
| 244 #if defined(OS_CHROMEOS) | |
| 245 , const std::string& user_email_text | |
| 246 , const char* zipped_logs_data | |
| 247 , int zipped_logs_length | |
| 248 , const chromeos::system::LogDictionaryType* const sys_info | |
| 249 #endif | |
| 250 ) { | |
| 251 // Create google feedback protocol buffer objects | |
| 252 userfeedback::ExternalExtensionSubmit feedback_data; | |
| 253 // type id set to 0, unused field but needs to be initialized to 0 | |
| 254 feedback_data.set_type_id(0); | |
| 255 | |
| 256 userfeedback::CommonData* common_data = feedback_data.mutable_common_data(); | |
| 257 userfeedback::WebData* web_data = feedback_data.mutable_web_data(); | |
| 258 | |
| 259 // Set GAIA id to 0. We're not using gaia id's for recording | |
| 260 // use feedback - we're using the e-mail field, allows users to | |
| 261 // submit feedback from incognito mode and specify any mail id | |
| 262 // they wish | |
| 263 common_data->set_gaia_id(0); | |
| 264 | |
| 265 #if defined(OS_CHROMEOS) | |
| 266 // Add the user e-mail to the feedback object | |
| 267 common_data->set_user_email(user_email_text); | |
| 268 #endif | |
| 269 | |
| 270 // Add the description to the feedback object | |
| 271 common_data->set_description(description); | |
| 272 | |
| 273 // Add the language | |
| 274 std::string chrome_locale = g_browser_process->GetApplicationLocale(); | |
| 275 common_data->set_source_description_language(chrome_locale); | |
| 276 | |
| 277 // Set the url | |
| 278 web_data->set_url(page_url_text); | |
| 279 | |
| 280 // Add the Chrome version | |
| 281 chrome::VersionInfo version_info; | |
| 282 if (version_info.is_valid()) { | |
| 283 std::string chrome_version = version_info.Name() + " - " + | |
| 284 version_info.Version() + | |
| 285 " (" + version_info.LastChange() + ")"; | |
| 286 AddFeedbackData(&feedback_data, std::string(kChromeVersionTag), | |
| 287 chrome_version); | |
| 288 } | |
| 289 | |
| 290 // Add OS version (eg, for WinXP SP2: "5.1.2600 Service Pack 2"). | |
| 291 std::string os_version = ""; | |
| 292 SetOSVersion(&os_version); | |
| 293 AddFeedbackData(&feedback_data, std::string(kOsVersionTag), os_version); | |
| 294 | |
| 295 // Include the page image if we have one. | |
| 296 if (image_data_ptr.get() && image_data_ptr->size()) { | |
| 297 userfeedback::PostedScreenshot screenshot; | |
| 298 screenshot.set_mime_type(kPngMimeType); | |
| 299 // Set the dimensions of the screenshot | |
| 300 userfeedback::Dimensions dimensions; | |
| 301 dimensions.set_width(static_cast<float>(png_width)); | |
| 302 dimensions.set_height(static_cast<float>(png_height)); | |
| 303 *(screenshot.mutable_dimensions()) = dimensions; | |
| 304 | |
| 305 int image_data_size = image_data_ptr->size(); | |
| 306 char* image_data = reinterpret_cast<char*>(&(image_data_ptr->front())); | |
| 307 screenshot.set_binary_content(std::string(image_data, image_data_size)); | |
| 308 | |
| 309 // Set the screenshot object in feedback | |
| 310 *(feedback_data.mutable_screenshot()) = screenshot; | |
| 311 } | |
| 312 | |
| 313 #if defined(OS_CHROMEOS) | |
| 314 if (sys_info) { | |
| 315 // Add the product specific data | |
| 316 for (chromeos::system::LogDictionaryType::const_iterator i = | |
| 317 sys_info->begin(); i != sys_info->end(); ++i) { | |
| 318 if (!CommandLine::ForCurrentProcess()->HasSwitch( | |
| 319 switches::kCompressSystemFeedback) || ValidFeedbackSize(i->second)) { | |
| 320 AddFeedbackData(&feedback_data, i->first, i->second); | |
| 321 } | |
| 322 } | |
| 323 | |
| 324 // If we have zipped logs, add them here | |
| 325 if (zipped_logs_data && CommandLine::ForCurrentProcess()->HasSwitch( | |
| 326 switches::kCompressSystemFeedback)) { | |
| 327 userfeedback::ProductSpecificBinaryData attachment; | |
| 328 attachment.set_mime_type(kBZip2MimeType); | |
| 329 attachment.set_name(kLogsAttachmentName); | |
| 330 attachment.set_data(std::string(zipped_logs_data, zipped_logs_length)); | |
| 331 *(feedback_data.add_product_specific_binary_data()) = attachment; | |
| 332 } | |
| 333 } | |
| 334 #endif | |
| 335 | |
| 336 // Set our Chrome specific data | |
| 337 userfeedback::ChromeData chrome_data; | |
| 338 #if defined(OS_CHROMEOS) | |
| 339 chrome_data.set_chrome_platform( | |
| 340 userfeedback::ChromeData_ChromePlatform_CHROME_OS); | |
| 341 userfeedback::ChromeOsData chrome_os_data; | |
| 342 chrome_os_data.set_category( | |
| 343 (userfeedback::ChromeOsData_ChromeOsCategory) problem_type); | |
| 344 *(chrome_data.mutable_chrome_os_data()) = chrome_os_data; | |
| 345 #else | |
| 346 chrome_data.set_chrome_platform( | |
| 347 userfeedback::ChromeData_ChromePlatform_CHROME_BROWSER); | |
| 348 userfeedback::ChromeBrowserData chrome_browser_data; | |
| 349 chrome_browser_data.set_category( | |
| 350 (userfeedback::ChromeBrowserData_ChromeBrowserCategory) problem_type); | |
| 351 *(chrome_data.mutable_chrome_browser_data()) = chrome_browser_data; | |
| 352 #endif | |
| 353 | |
| 354 *(feedback_data.mutable_chrome_data()) = chrome_data; | |
| 355 | |
| 356 // Serialize our report to a string pointer we can pass around | |
| 357 std::string* post_body = new std::string; | |
| 358 feedback_data.SerializeToString(post_body); | |
| 359 | |
| 360 // We have the body of our POST, so send it off to the server with 0 delay | |
| 361 DispatchFeedback(profile, post_body, 0); | |
| 362 } | |
| 363 | |
| 364 #if defined(ENABLE_SAFE_BROWSING) | |
| 365 // static | |
| 366 void BugReportUtil::ReportPhishing(WebContents* current_tab, | |
| 367 const std::string& phishing_url) { | |
| 368 current_tab->GetController().LoadURL( | |
| 369 safe_browsing_util::GeneratePhishingReportUrl( | |
| 370 kReportPhishingUrl, phishing_url, | |
| 371 false /* not client-side detection */), | |
| 372 content::Referrer(), | |
| 373 content::PAGE_TRANSITION_LINK, | |
| 374 std::string()); | |
| 375 } | |
| 376 #endif | |
| 377 | |
| 378 static std::vector<unsigned char>* screenshot_png = NULL; | |
| 379 static gfx::Rect* screenshot_size = NULL; | |
| 380 | |
| 381 // static | |
| 382 std::vector<unsigned char>* BugReportUtil::GetScreenshotPng() { | |
| 383 if (screenshot_png == NULL) | |
| 384 screenshot_png = new std::vector<unsigned char>; | |
| 385 return screenshot_png; | |
| 386 } | |
| 387 | |
| 388 // static | |
| 389 void BugReportUtil::ClearScreenshotPng() { | |
| 390 if (screenshot_png) | |
| 391 screenshot_png->clear(); | |
| 392 } | |
| 393 | |
| 394 // static | |
| 395 gfx::Rect& BugReportUtil::GetScreenshotSize() { | |
| 396 if (screenshot_size == NULL) | |
| 397 screenshot_size = new gfx::Rect(); | |
| 398 return *screenshot_size; | |
| 399 } | |
| 400 | |
| 401 // static | |
| 402 void BugReportUtil::SetScreenshotSize(const gfx::Rect& rect) { | |
| 403 gfx::Rect& screen_size = GetScreenshotSize(); | |
| 404 screen_size = rect; | |
| 405 } | |
| OLD | NEW |