1 // 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.
5 #include "feedback_common.h"
7 #include "base/strings/string_util.h"
8 #include "components/feedback/proto/common.pb.h"
9 #include "components/feedback/proto/dom.pb.h"
10 #include "components/feedback/proto/extension.pb.h"
11 #include "components/feedback/proto/math.pb.h"
15 const char kMultilineIndicatorString
[] = "<multiline>\n";
16 const char kMultilineStartString
[] = "---------- START ----------\n";
17 const char kMultilineEndString
[] = "---------- END ----------\n\n";
19 const size_t kFeedbackMaxLength
= 4 * 1024;
20 const size_t kFeedbackMaxLineCount
= 40;
22 const base::FilePath::CharType kLogsFilename
[] =
23 FILE_PATH_LITERAL("system_logs.txt");
24 const char kLogsAttachmentName
[] = "system_logs.zip";
26 const char kZipExt
[] = ".zip";
28 const char kPngMimeType
[] = "image/png";
29 const char kArbitraryMimeType
[] = "application/octet-stream";
31 // Converts the system logs into a string that we can compress and send
32 // with the report. This method only converts those logs that we want in
33 // the compressed zip file sent with the report, hence it ignores any logs
34 // below the size threshold of what we want compressed.
35 std::string
* LogsToString(const FeedbackCommon::SystemLogsMap
& sys_info
) {
36 std::string
* syslogs_string
= new std::string
;
37 for (FeedbackCommon::SystemLogsMap::const_iterator it
= sys_info
.begin();
40 std::string key
= it
->first
;
41 std::string value
= it
->second
;
43 if (FeedbackCommon::BelowCompressionThreshold(value
))
46 base::TrimString(key
, "\n ", &key
);
47 base::TrimString(value
, "\n ", &value
);
49 if (value
.find("\n") != std::string::npos
) {
50 syslogs_string
->append(key
+ "=" + kMultilineIndicatorString
+
51 kMultilineStartString
+ value
+ "\n" +
54 syslogs_string
->append(key
+ "=" + value
+ "\n");
57 return syslogs_string
;
60 void AddFeedbackData(userfeedback::ExtensionSubmit
* feedback_data
,
61 const std::string
& key
,
62 const std::string
& value
) {
63 // Don't bother with empty keys or values.
64 if (key
.empty() || value
.empty())
66 // Create log_value object and add it to the web_data object.
67 userfeedback::ProductSpecificData log_value
;
68 log_value
.set_key(key
);
69 log_value
.set_value(value
);
70 userfeedback::WebData
* web_data
= feedback_data
->mutable_web_data();
71 *(web_data
->add_product_specific_data()) = log_value
;
74 // Adds data as an attachment to feedback_data if the data is non-empty.
75 void AddAttachment(userfeedback::ExtensionSubmit
* feedback_data
,
77 const std::string
& data
) {
81 userfeedback::ProductSpecificBinaryData
* attachment
=
82 feedback_data
->add_product_specific_binary_data();
83 attachment
->set_mime_type(kArbitraryMimeType
);
84 attachment
->set_name(name
);
85 attachment
->set_data(data
);
90 FeedbackCommon::AttachedFile::AttachedFile(const std::string
& filename
,
91 scoped_ptr
<std::string
> data
)
92 : name(filename
), data(data
.Pass()) {}
94 FeedbackCommon::AttachedFile::~AttachedFile() {}
97 FeedbackCommon::FeedbackCommon() : product_id_(0) {}
99 FeedbackCommon::~FeedbackCommon() {}
102 bool FeedbackCommon::BelowCompressionThreshold(const std::string
& content
) {
103 if (content
.length() > kFeedbackMaxLength
)
105 const size_t line_count
= std::count(content
.begin(), content
.end(), '\n');
106 if (line_count
> kFeedbackMaxLineCount
)
111 void FeedbackCommon::CompressFile(const base::FilePath
& filename
,
112 const std::string
& zipname
,
113 scoped_ptr
<std::string
> data
) {
114 AttachedFile
* file
= new AttachedFile(
115 zipname
, scoped_ptr
<std::string
>(new std::string()));
116 if (file
->name
.empty()) {
117 // We need to use the UTF8Unsafe methods here to accomodate Windows, which
118 // uses wide strings to store filepaths.
119 file
->name
= filename
.BaseName().AsUTF8Unsafe();
120 file
->name
.append(kZipExt
);
122 if (feedback_util::ZipString(filename
, *(data
.get()), file
->data
.get())) {
123 base::AutoLock
lock(attachments_lock_
);
124 attachments_
.push_back(file
);
130 void FeedbackCommon::AddFile(const std::string
& filename
,
131 scoped_ptr
<std::string
> data
) {
132 base::AutoLock
lock(attachments_lock_
);
133 attachments_
.push_back(new AttachedFile(filename
, data
.Pass()));
136 void FeedbackCommon::AddLog(const std::string
& name
, const std::string
& value
) {
138 logs_
= scoped_ptr
<SystemLogsMap
>(new SystemLogsMap
);
139 (*logs_
.get())[name
] = value
;
142 void FeedbackCommon::AddLogs(scoped_ptr
<SystemLogsMap
> logs
) {
144 logs_
->insert(logs
->begin(), logs
->end());
150 void FeedbackCommon::CompressLogs() {
153 std::string
* logs
= LogsToString(*logs_
.get());
156 base::FilePath(kLogsFilename
), kLogsAttachmentName
,
157 scoped_ptr
<std::string
>(logs
));
160 void FeedbackCommon::AddFilesAndLogsToReport(
161 userfeedback::ExtensionSubmit
* feedback_data
) const {
163 for (FeedbackCommon::SystemLogsMap::const_iterator i
= sys_info()->begin();
164 i
!= sys_info()->end();
166 if (BelowCompressionThreshold(i
->second
))
167 AddFeedbackData(feedback_data
, i
->first
, i
->second
);
171 for (size_t i
= 0; i
< attachments(); i
++) {
172 const AttachedFile
* file
= attachment(i
);
173 AddAttachment(feedback_data
, file
->name
.c_str(), *file
->data
.get());
177 void FeedbackCommon::PrepareReport(
178 userfeedback::ExtensionSubmit
* feedback_data
) const {
179 // Unused field, needs to be 0 though.
180 feedback_data
->set_type_id(0);
181 feedback_data
->set_product_id(product_id_
);
183 userfeedback::CommonData
* common_data
= feedback_data
->mutable_common_data();
184 // We're not using gaia ids, we're using the e-mail field instead.
185 common_data
->set_gaia_id(0);
186 common_data
->set_user_email(user_email());
187 common_data
->set_description(description());
188 common_data
->set_source_description_language(locale());
190 userfeedback::WebData
* web_data
= feedback_data
->mutable_web_data();
191 web_data
->set_url(page_url());
192 web_data
->mutable_navigator()->set_user_agent(user_agent());
194 AddFilesAndLogsToReport(feedback_data
);
196 if (image() && image()->size()) {
197 userfeedback::PostedScreenshot screenshot
;
198 screenshot
.set_mime_type(kPngMimeType
);
200 // Set that we 'have' dimensions of the screenshot. These dimensions are
201 // ignored by the server but are a 'required' field in the protobuf.
202 userfeedback::Dimensions dimensions
;
203 dimensions
.set_width(0.0);
204 dimensions
.set_height(0.0);
206 *(screenshot
.mutable_dimensions()) = dimensions
;
207 screenshot
.set_binary_content(*image());
209 *(feedback_data
->mutable_screenshot()) = screenshot
;
212 if (category_tag().size())
213 feedback_data
->set_bucket(category_tag());