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