blob: 1a356d68de4b0567053d85454c4eb4b786d97ac9 [file] [log] [blame]
[email protected]42066d52014-06-06 17:51:121// Copyright 2014 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
dcheng51606352015-12-26 21:16:235#include "components/feedback/feedback_common.h"
6
7#include <utility>
[email protected]42066d52014-06-06 17:51:128
dcheng84c358e2016-04-26 07:05:539#include "base/memory/ptr_util.h"
[email protected]42066d52014-06-06 17:51:1210#include "base/strings/string_util.h"
afakhryf3768842016-09-13 20:34:3411#include "components/feedback/feedback_report.h"
afakhry79455092016-08-06 00:16:3812#include "components/feedback/feedback_util.h"
[email protected]42066d52014-06-06 17:51:1213#include "components/feedback/proto/common.pb.h"
14#include "components/feedback/proto/dom.pb.h"
15#include "components/feedback/proto/extension.pb.h"
16#include "components/feedback/proto/math.pb.h"
17
18namespace {
19
afakhry402fadf22016-08-03 22:27:4220#if defined(OS_CHROMEOS)
21constexpr int kChromeOSProductId = 208;
22#else
23constexpr int kChromeBrowserProductId = 237;
24#endif
25
afakhryf3768842016-09-13 20:34:3426constexpr char kMultilineIndicatorString[] = "<multiline>\n";
27constexpr char kMultilineStartString[] = "---------- START ----------\n";
28constexpr char kMultilineEndString[] = "---------- END ----------\n\n";
[email protected]42066d52014-06-06 17:51:1229
afakhry79455092016-08-06 00:16:3830// The below thresholds were chosen arbitrarily to conveniently show small data
31// as part of the report itself without having to look into the system_logs.zip
32// file.
afakhryf3768842016-09-13 20:34:3433constexpr size_t kFeedbackMaxLength = 1024;
34constexpr size_t kFeedbackMaxLineCount = 10;
[email protected]42066d52014-06-06 17:51:1235
afakhryf3768842016-09-13 20:34:3436constexpr base::FilePath::CharType kLogsFilename[] =
[email protected]42066d52014-06-06 17:51:1237 FILE_PATH_LITERAL("system_logs.txt");
afakhryf3768842016-09-13 20:34:3438constexpr char kLogsAttachmentName[] = "system_logs.zip";
[email protected]42066d52014-06-06 17:51:1239
afakhryf3768842016-09-13 20:34:3440constexpr char kZipExt[] = ".zip";
[email protected]42066d52014-06-06 17:51:1241
afakhryf3768842016-09-13 20:34:3442constexpr char kPngMimeType[] = "image/png";
43constexpr char kArbitraryMimeType[] = "application/octet-stream";
[email protected]42066d52014-06-06 17:51:1244
afakhry79455092016-08-06 00:16:3845// Determine if the given feedback value is small enough to not need to
46// be compressed.
47bool BelowCompressionThreshold(const std::string& content) {
48 if (content.length() > kFeedbackMaxLength)
49 return false;
50 const size_t line_count = std::count(content.begin(), content.end(), '\n');
51 if (line_count > kFeedbackMaxLineCount)
52 return false;
53 return true;
54}
55
[email protected]42066d52014-06-06 17:51:1256// Converts the system logs into a string that we can compress and send
afakhryaa732cc2016-08-06 22:38:3157// with the report.
dcheng84c358e2016-04-26 07:05:5358// TODO(dcheng): This should probably just take advantage of string's move
59// constructor.
60std::unique_ptr<std::string> LogsToString(
61 const FeedbackCommon::SystemLogsMap& sys_info) {
62 std::unique_ptr<std::string> syslogs_string(new std::string);
afakhry79455092016-08-06 00:16:3863 for (const auto& iter : sys_info) {
64 std::string key = iter.first;
65 std::string value = iter.second;
[email protected]42066d52014-06-06 17:51:1266
[email protected]42066d52014-06-06 17:51:1267 base::TrimString(key, "\n ", &key);
68 base::TrimString(value, "\n ", &value);
69
afakhryf3768842016-09-13 20:34:3470 // We must avoid adding the crash IDs to the system_logs.txt file for
71 // privacy reasons. They should just be part of the product specific data.
72 if (key == feedback::FeedbackReport::kCrashReportIdsKey)
73 continue;
74
[email protected]42066d52014-06-06 17:51:1275 if (value.find("\n") != std::string::npos) {
76 syslogs_string->append(key + "=" + kMultilineIndicatorString +
77 kMultilineStartString + value + "\n" +
78 kMultilineEndString);
79 } else {
80 syslogs_string->append(key + "=" + value + "\n");
81 }
82 }
83 return syslogs_string;
84}
85
86void AddFeedbackData(userfeedback::ExtensionSubmit* feedback_data,
87 const std::string& key,
88 const std::string& value) {
89 // Don't bother with empty keys or values.
90 if (key.empty() || value.empty())
91 return;
92 // Create log_value object and add it to the web_data object.
93 userfeedback::ProductSpecificData log_value;
94 log_value.set_key(key);
95 log_value.set_value(value);
96 userfeedback::WebData* web_data = feedback_data->mutable_web_data();
97 *(web_data->add_product_specific_data()) = log_value;
98}
99
100// Adds data as an attachment to feedback_data if the data is non-empty.
101void AddAttachment(userfeedback::ExtensionSubmit* feedback_data,
102 const char* name,
103 const std::string& data) {
104 if (data.empty())
105 return;
106
107 userfeedback::ProductSpecificBinaryData* attachment =
108 feedback_data->add_product_specific_binary_data();
109 attachment->set_mime_type(kArbitraryMimeType);
110 attachment->set_name(name);
111 attachment->set_data(data);
112}
113
114} // namespace
115
afakhry79455092016-08-06 00:16:38116////////////////////////////////////////////////////////////////////////////////
117// FeedbackCommon::AttachedFile::
118////////////////////////////////////////////////////////////////////////////////
119
[email protected]42066d52014-06-06 17:51:12120FeedbackCommon::AttachedFile::AttachedFile(const std::string& filename,
dcheng84c358e2016-04-26 07:05:53121 std::unique_ptr<std::string> data)
dcheng51606352015-12-26 21:16:23122 : name(filename), data(std::move(data)) {}
[email protected]42066d52014-06-06 17:51:12123
124FeedbackCommon::AttachedFile::~AttachedFile() {}
125
afakhry79455092016-08-06 00:16:38126////////////////////////////////////////////////////////////////////////////////
127// FeedbackCommon::
128////////////////////////////////////////////////////////////////////////////////
129
afakhry402fadf22016-08-03 22:27:42130FeedbackCommon::FeedbackCommon() : product_id_(-1) {}
[email protected]42066d52014-06-06 17:51:12131
[email protected]42066d52014-06-06 17:51:12132void FeedbackCommon::AddFile(const std::string& filename,
dcheng84c358e2016-04-26 07:05:53133 std::unique_ptr<std::string> data) {
[email protected]42066d52014-06-06 17:51:12134 base::AutoLock lock(attachments_lock_);
afakhry79455092016-08-06 00:16:38135 attachments_.emplace_back(new AttachedFile(filename, std::move(data)));
[email protected]42066d52014-06-06 17:51:12136}
137
138void FeedbackCommon::AddLog(const std::string& name, const std::string& value) {
afakhry79455092016-08-06 00:16:38139 if (!logs_)
dcheng84c358e2016-04-26 07:05:53140 logs_ = base::WrapUnique(new SystemLogsMap);
141 (*logs_)[name] = value;
[email protected]42066d52014-06-06 17:51:12142}
143
dcheng84c358e2016-04-26 07:05:53144void FeedbackCommon::AddLogs(std::unique_ptr<SystemLogsMap> logs) {
afakhry79455092016-08-06 00:16:38145 if (logs_)
[email protected]42066d52014-06-06 17:51:12146 logs_->insert(logs->begin(), logs->end());
afakhry79455092016-08-06 00:16:38147 else
dcheng51606352015-12-26 21:16:23148 logs_ = std::move(logs);
[email protected]42066d52014-06-06 17:51:12149}
150
151void FeedbackCommon::PrepareReport(
152 userfeedback::ExtensionSubmit* feedback_data) const {
153 // Unused field, needs to be 0 though.
154 feedback_data->set_type_id(0);
afakhry402fadf22016-08-03 22:27:42155
156 // Set whether we're reporting from ChromeOS or Chrome on another platform.
157 userfeedback::ChromeData chrome_data;
158#if defined(OS_CHROMEOS)
159 const userfeedback::ChromeData_ChromePlatform chrome_platform =
160 userfeedback::ChromeData_ChromePlatform_CHROME_OS;
161 const int default_product_id = kChromeOSProductId;
162 userfeedback::ChromeOsData chrome_os_data;
163 chrome_os_data.set_category(
164 userfeedback::ChromeOsData_ChromeOsCategory_OTHER);
165 *(chrome_data.mutable_chrome_os_data()) = chrome_os_data;
166#else
167 const userfeedback::ChromeData_ChromePlatform chrome_platform =
168 userfeedback::ChromeData_ChromePlatform_CHROME_BROWSER;
169 const int default_product_id = kChromeBrowserProductId;
170 userfeedback::ChromeBrowserData chrome_browser_data;
171 chrome_browser_data.set_category(
172 userfeedback::ChromeBrowserData_ChromeBrowserCategory_OTHER);
173 *(chrome_data.mutable_chrome_browser_data()) = chrome_browser_data;
174#endif // defined(OS_CHROMEOS)
175 chrome_data.set_chrome_platform(chrome_platform);
176 *(feedback_data->mutable_chrome_data()) = chrome_data;
177
178 feedback_data->set_product_id(HasProductId() ? product_id_
179 : default_product_id);
[email protected]42066d52014-06-06 17:51:12180
181 userfeedback::CommonData* common_data = feedback_data->mutable_common_data();
182 // We're not using gaia ids, we're using the e-mail field instead.
183 common_data->set_gaia_id(0);
184 common_data->set_user_email(user_email());
185 common_data->set_description(description());
186 common_data->set_source_description_language(locale());
187
188 userfeedback::WebData* web_data = feedback_data->mutable_web_data();
189 web_data->set_url(page_url());
190 web_data->mutable_navigator()->set_user_agent(user_agent());
191
192 AddFilesAndLogsToReport(feedback_data);
193
194 if (image() && image()->size()) {
195 userfeedback::PostedScreenshot screenshot;
196 screenshot.set_mime_type(kPngMimeType);
197
198 // Set that we 'have' dimensions of the screenshot. These dimensions are
199 // ignored by the server but are a 'required' field in the protobuf.
200 userfeedback::Dimensions dimensions;
201 dimensions.set_width(0.0);
202 dimensions.set_height(0.0);
203
204 *(screenshot.mutable_dimensions()) = dimensions;
205 screenshot.set_binary_content(*image());
206
207 *(feedback_data->mutable_screenshot()) = screenshot;
208 }
209
210 if (category_tag().size())
211 feedback_data->set_bucket(category_tag());
212}
afakhry79455092016-08-06 00:16:38213
214FeedbackCommon::~FeedbackCommon() {}
215
216void FeedbackCommon::CompressFile(
217 const base::FilePath& filename,
218 const std::string& zipname,
219 std::unique_ptr<std::string> data_to_be_compressed) {
220 std::unique_ptr<std::string> compressed_data(new std::string());
221 if (feedback_util::ZipString(filename, *data_to_be_compressed,
222 compressed_data.get())) {
223 std::string attachment_file_name = zipname;
224 if (attachment_file_name.empty()) {
225 // We need to use the UTF8Unsafe methods here to accommodate Windows,
226 // which uses wide strings to store file paths.
227 attachment_file_name = filename.BaseName().AsUTF8Unsafe().append(kZipExt);
228 }
229
230 AddFile(attachment_file_name, std::move(compressed_data));
231 }
232}
233
234void FeedbackCommon::CompressLogs() {
235 if (!logs_)
236 return;
237 std::unique_ptr<std::string> logs = LogsToString(*logs_);
238 if (!logs->empty()) {
239 CompressFile(base::FilePath(kLogsFilename), kLogsAttachmentName,
240 std::move(logs));
241 }
242}
afakhryf3768842016-09-13 20:34:34243
afakhry79455092016-08-06 00:16:38244void FeedbackCommon::AddFilesAndLogsToReport(
245 userfeedback::ExtensionSubmit* feedback_data) const {
246 for (size_t i = 0; i < attachments(); ++i) {
247 const AttachedFile* file = attachment(i);
248 AddAttachment(feedback_data, file->name.c_str(), *file->data);
249 }
250
251 if (!logs_)
252 return;
253
254 for (const auto& iter : *logs_) {
255 if (BelowCompressionThreshold(iter.second)) {
256 // Small enough logs should end up in the report data itself. However,
257 // they're still added as part of the system_logs.zip file.
258 AddFeedbackData(feedback_data, iter.first, iter.second);
259 }
260 }
261}