bug_report_util.cc revision 3345a6884c488ff3a535c2c9acdd33d74b37e311
1// Copyright (c) 2010 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 "app/l10n_util.h"
11#include "base/command_line.h"
12#include "base/file_version_info.h"
13#include "base/file_util.h"
14#include "base/singleton.h"
15#include "base/string_util.h"
16#include "base/utf_string_conversions.h"
17#include "chrome/browser/browser_list.h"
18#include "chrome/browser/browser_process_impl.h"
19#include "chrome/browser/profile.h"
20#include "chrome/browser/safe_browsing/safe_browsing_util.h"
21#include "chrome/browser/tab_contents/tab_contents.h"
22#include "chrome/common/chrome_version_info.h"
23#include "chrome/common/chrome_switches.h"
24#include "chrome/common/net/url_fetcher.h"
25#include "googleurl/src/gurl.h"
26#include "grit/generated_resources.h"
27#include "grit/locale_settings.h"
28#include "grit/theme_resources.h"
29#include "net/url_request/url_request_status.h"
30#include "unicode/locid.h"
31
32#if defined(OS_CHROMEOS)
33#include "chrome/browser/chromeos/notifications/system_notification.h"
34#endif
35
36namespace {
37
38const int kBugReportVersion = 1;
39
40const char kReportPhishingUrl[] =
41    "http://www.google.com/safebrowsing/report_phish/";
42
43// URL to post bug reports to.
44static char const kBugReportPostUrl[] =
45    "https://www.google.com/tools/feedback/chrome/__submit";
46
47static char const kProtBufMimeType[] = "application/x-protobuf";
48static char const kPngMimeType[] = "image/png";
49
50// Tags we use in product specific data
51static char const kPageTitleTag[] = "PAGE TITLE";
52static char const kProblemTypeIdTag[] = "PROBLEM TYPE ID";
53static char const kProblemTypeTag[] = "PROBLEM TYPE";
54static char const kChromeVersionTag[] = "CHROME VERSION";
55static char const kOsVersionTag[] = "OS VERSION";
56
57static char const kNotificationId[] = "feedback.chromeos";
58
59static int const kHttpPostSuccessNoContent = 204;
60static int const kHttpPostFailNoConnection = -1;
61static int const kHttpPostFailClientError = 400;
62static int const kHttpPostFailServerError = 500;
63
64} // namespace
65
66
67#if defined(OS_CHROMEOS)
68class FeedbackNotification {
69 public:
70  // Previous notification cleanup is handled by scoped_ptr.
71  // Note: notification will show only on one profile at a time.
72  void Show(Profile* profile, const string16& message, bool urgent) {
73    notification_.reset(
74        new chromeos::SystemNotification(profile, kNotificationId,
75                               IDR_STATUSBAR_FEEDBACK,
76                               l10n_util::GetStringUTF16(
77                                   IDS_BUGREPORT_NOTIFICATION_TITLE)));
78    notification_->Show(message, urgent, false);
79  }
80
81 private:
82  FeedbackNotification() {}
83  friend struct DefaultSingletonTraits<FeedbackNotification>;
84
85  scoped_ptr<chromeos::SystemNotification> notification_;
86  DISALLOW_COPY_AND_ASSIGN(FeedbackNotification);
87};
88#endif
89
90// Simple URLFetcher::Delegate to clean up URLFetcher on completion.
91class BugReportUtil::PostCleanup : public URLFetcher::Delegate {
92 public:
93#if defined(OS_CHROMEOS)
94  explicit PostCleanup(Profile* profile);
95#else
96  PostCleanup();
97#endif
98  // Overridden from URLFetcher::Delegate.
99  virtual void OnURLFetchComplete(const URLFetcher* source,
100                                  const GURL& url,
101                                  const URLRequestStatus& status,
102                                  int response_code,
103                                  const ResponseCookies& cookies,
104                                  const std::string& data);
105
106 protected:
107  virtual ~PostCleanup() {}
108
109 private:
110  Profile* profile_;
111
112  DISALLOW_COPY_AND_ASSIGN(PostCleanup);
113};
114
115#if defined(OS_CHROMEOS)
116  BugReportUtil::PostCleanup::PostCleanup(Profile* profile)
117      : profile_(profile) {
118#else
119  BugReportUtil::PostCleanup::PostCleanup() {
120#endif
121}
122
123void BugReportUtil::PostCleanup::OnURLFetchComplete(
124    const URLFetcher* source,
125    const GURL& url,
126    const URLRequestStatus& status,
127    int response_code,
128    const ResponseCookies& cookies,
129    const std::string& data) {
130
131  std::stringstream error_stream;
132  if (response_code == kHttpPostSuccessNoContent) {
133    error_stream << "Success";
134  } else if (response_code == kHttpPostFailNoConnection) {
135        error_stream << "No connection to server.";
136  } else if ((response_code > kHttpPostFailClientError) &&
137      (response_code < kHttpPostFailServerError)) {
138        error_stream << "Client error: HTTP response code " << response_code;
139  } else if (response_code > kHttpPostFailServerError) {
140        error_stream << "Server error: HTTP response code " << response_code;
141  } else {
142        error_stream << "Unknown error: HTTP response code " << response_code;
143  }
144
145  LOG(WARNING) << "Submission to feedback server (" << url <<
146      ") status: " << error_stream.str() << std::endl;
147
148#if defined(OS_CHROMEOS)
149  // Show the notification to the user; this notification will stay active till
150  // either the user closes it, or we display another notification.
151  if (response_code == kHttpPostSuccessNoContent) {
152    Singleton<FeedbackNotification>()->Show(profile_, l10n_util::GetStringUTF16(
153        IDS_BUGREPORT_FEEDBACK_STATUS_SUCCESS), false);
154  } else {
155    Singleton<FeedbackNotification>()->Show(profile_,
156        l10n_util::GetStringFUTF16(IDS_BUGREPORT_FEEDBACK_STATUS_FAIL,
157                                   ASCIIToUTF16(error_stream.str())),
158        true);
159  }
160#endif
161
162  // Delete the URLFetcher.
163  delete source;
164  // And then delete ourselves.
165  delete this;
166}
167
168// static
169void BugReportUtil::SetOSVersion(std::string *os_version) {
170#if defined(OS_WIN)
171  OSVERSIONINFO osvi;
172  ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
173  osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
174
175  if (GetVersionEx(&osvi)) {
176    *os_version = StringPrintf("%d.%d.%d %S",
177      osvi.dwMajorVersion,
178      osvi.dwMinorVersion,
179      osvi.dwBuildNumber,
180      osvi.szCSDVersion);
181  } else {
182    *os_version = "unknown";
183  }
184#elif defined(OS_MACOSX)
185  int32 major;
186  int32 minor;
187  int32 bugFix;
188  base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugFix);
189  *os_version = StringPrintf("%d.%d.%d", major, minor, bugFix);
190#else
191  *os_version = "unknown";
192#endif
193}
194
195// static
196std::string BugReportUtil::feedback_server_("");
197
198// static
199void BugReportUtil::SetFeedbackServer(const std::string& server) {
200  feedback_server_ = server;
201}
202
203
204// static
205void BugReportUtil::AddFeedbackData(
206    userfeedback::ExternalExtensionSubmit* feedback_data,
207    const std::string& key, const std::string& value) {
208  // We have no reason to log any empty values - gives us no data
209  if (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// static
219void BugReportUtil::SendReport(Profile* profile,
220    const std::string& page_title_text,
221    int problem_type,
222    const std::string& page_url_text,
223    const std::string& description,
224    const char* png_data,
225    int png_data_length,
226    int png_width,
227#if defined(OS_CHROMEOS)
228    int png_height,
229    const std::string& user_email_text,
230    const chromeos::LogDictionaryType* const sys_info) {
231#else
232    int png_height) {
233#endif
234  GURL post_url;
235
236  if (CommandLine::ForCurrentProcess()->
237      HasSwitch(switches::kFeedbackServer))
238    post_url = GURL(CommandLine::ForCurrentProcess()->
239        GetSwitchValueASCII(switches::kFeedbackServer));
240  else
241    post_url = GURL(kBugReportPostUrl);
242
243  // Create google feedback protocol buffer objects
244  userfeedback::ExternalExtensionSubmit feedback_data;
245  // type id set to 0, unused field but needs to be initialized to 0
246  feedback_data.set_type_id(0);
247
248  userfeedback::CommonData* common_data = feedback_data.mutable_common_data();
249  userfeedback::WebData* web_data = feedback_data.mutable_web_data();
250
251  // Set GAIA id to 0. We're not using gaia id's for recording
252  // use feedback - we're using the e-mail field, allows users to
253  // submit feedback from incognito mode and specify any mail id
254  // they wish
255  common_data->set_gaia_id(0);
256
257  // Add the page title.
258  AddFeedbackData(&feedback_data, std::string(kPageTitleTag),
259                  page_title_text);
260
261#if defined(OS_CHROMEOS)
262  // Add the user e-mail to the feedback object
263  common_data->set_user_email(user_email_text);
264#endif
265
266  // Add the description to the feedback object
267  common_data->set_description(description);
268
269  // Add the language
270  std::string chrome_locale = g_browser_process->GetApplicationLocale();
271  common_data->set_source_description_language(chrome_locale);
272
273  // Set the url
274  web_data->set_url(page_url_text);
275
276  // Add the Chrome version
277  chrome::VersionInfo version_info;
278  if (version_info.is_valid()) {
279    std::string chrome_version = version_info.Name() + " - " +
280        version_info.Version() +
281        " (" + version_info.LastChange() + ")";
282    AddFeedbackData(&feedback_data, std::string(kChromeVersionTag),
283                    chrome_version);
284  }
285
286  // Add OS version (eg, for WinXP SP2: "5.1.2600 Service Pack 2").
287  std::string os_version = "";
288  SetOSVersion(&os_version);
289  AddFeedbackData(&feedback_data, std::string(kOsVersionTag), os_version);
290
291#if defined(OS_CHROMEOS)
292  if (sys_info) {
293    for (chromeos::LogDictionaryType::const_iterator i = sys_info->begin();
294         i != sys_info->end(); ++i)
295      AddFeedbackData(&feedback_data, i->first, i->second);
296  }
297#endif
298
299  // Include the page image if we have one.
300  if (png_data) {
301    userfeedback::PostedScreenshot screenshot;
302    screenshot.set_mime_type(kPngMimeType);
303    // Set the dimensions of the screenshot
304    userfeedback::Dimensions dimensions;
305    dimensions.set_width(static_cast<float>(png_width));
306    dimensions.set_height(static_cast<float>(png_height));
307    *(screenshot.mutable_dimensions()) = dimensions;
308    screenshot.set_binary_content(std::string(png_data, png_data_length));
309
310    // Set the screenshot object in feedback
311    *(feedback_data.mutable_screenshot()) = screenshot;
312  }
313
314  // Set our Chrome specific data
315  userfeedback::ChromeData chrome_data;
316#if defined(OS_CHROMEOS)
317  chrome_data.set_chrome_platform(
318      userfeedback::ChromeData_ChromePlatform_CHROME_OS);
319  userfeedback::ChromeOsData chrome_os_data;
320  chrome_os_data.set_category(
321      (userfeedback::ChromeOsData_ChromeOsCategory) problem_type);
322  *(chrome_data.mutable_chrome_os_data()) = chrome_os_data;
323#else
324  chrome_data.set_chrome_platform(
325      userfeedback::ChromeData_ChromePlatform_CHROME_BROWSER);
326  userfeedback::ChromeBrowserData chrome_browser_data;
327  chrome_browser_data.set_category(
328      (userfeedback::ChromeBrowserData_ChromeBrowserCategory) problem_type);
329  *(chrome_data.mutable_chrome_browser_data()) = chrome_browser_data;
330#endif
331
332  *(feedback_data.mutable_chrome_data()) = chrome_data;
333
334  // We have the body of our POST, so send it off to the server.
335  URLFetcher* fetcher = new URLFetcher(post_url, URLFetcher::POST,
336#if defined(OS_CHROMEOS)
337                                       new BugReportUtil::PostCleanup(profile));
338#else
339                                       new BugReportUtil::PostCleanup());
340#endif
341  fetcher->set_request_context(profile->GetRequestContext());
342
343  std::string post_body;
344  feedback_data.SerializeToString(&post_body);
345  fetcher->set_upload_data(std::string(kProtBufMimeType), post_body);
346  fetcher->Start();
347}
348
349// static
350void BugReportUtil::ReportPhishing(TabContents* currentTab,
351                                   const std::string& phishing_url) {
352  currentTab->controller().LoadURL(
353      safe_browsing_util::GeneratePhishingReportUrl(
354          kReportPhishingUrl, phishing_url),
355      GURL(),
356      PageTransition::LINK);
357}
358