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