• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "feedback_common.h"
6 
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"
12 
13 namespace {
14 
15 const char kMultilineIndicatorString[] = "<multiline>\n";
16 const char kMultilineStartString[] = "---------- START ----------\n";
17 const char kMultilineEndString[] = "---------- END ----------\n\n";
18 
19 const size_t kFeedbackMaxLength = 4 * 1024;
20 const size_t kFeedbackMaxLineCount = 40;
21 
22 const base::FilePath::CharType kLogsFilename[] =
23     FILE_PATH_LITERAL("system_logs.txt");
24 const char kLogsAttachmentName[] = "system_logs.zip";
25 
26 const char kZipExt[] = ".zip";
27 
28 const char kPngMimeType[] = "image/png";
29 const char kArbitraryMimeType[] = "application/octet-stream";
30 
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.
LogsToString(const FeedbackCommon::SystemLogsMap & sys_info)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();
38        it != sys_info.end();
39        ++it) {
40     std::string key = it->first;
41     std::string value = it->second;
42 
43     if (FeedbackCommon::BelowCompressionThreshold(value))
44       continue;
45 
46     base::TrimString(key, "\n ", &key);
47     base::TrimString(value, "\n ", &value);
48 
49     if (value.find("\n") != std::string::npos) {
50       syslogs_string->append(key + "=" + kMultilineIndicatorString +
51                              kMultilineStartString + value + "\n" +
52                              kMultilineEndString);
53     } else {
54       syslogs_string->append(key + "=" + value + "\n");
55     }
56   }
57   return syslogs_string;
58 }
59 
AddFeedbackData(userfeedback::ExtensionSubmit * feedback_data,const std::string & key,const std::string & value)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())
65     return;
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;
72 }
73 
74 // Adds data as an attachment to feedback_data if the data is non-empty.
AddAttachment(userfeedback::ExtensionSubmit * feedback_data,const char * name,const std::string & data)75 void AddAttachment(userfeedback::ExtensionSubmit* feedback_data,
76                    const char* name,
77                    const std::string& data) {
78   if (data.empty())
79     return;
80 
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);
86 }
87 
88 }  // namespace
89 
AttachedFile(const std::string & filename,scoped_ptr<std::string> data)90 FeedbackCommon::AttachedFile::AttachedFile(const std::string& filename,
91                                            scoped_ptr<std::string> data)
92     : name(filename), data(data.Pass()) {}
93 
~AttachedFile()94 FeedbackCommon::AttachedFile::~AttachedFile() {}
95 
96 
FeedbackCommon()97 FeedbackCommon::FeedbackCommon() : product_id_(0) {}
98 
~FeedbackCommon()99 FeedbackCommon::~FeedbackCommon() {}
100 
101 // static
BelowCompressionThreshold(const std::string & content)102 bool FeedbackCommon::BelowCompressionThreshold(const std::string& content) {
103   if (content.length() > kFeedbackMaxLength)
104     return false;
105   const size_t line_count = std::count(content.begin(), content.end(), '\n');
106   if (line_count > kFeedbackMaxLineCount)
107     return false;
108   return true;
109 }
110 
CompressFile(const base::FilePath & filename,const std::string & zipname,scoped_ptr<std::string> data)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);
121   }
122   if (feedback_util::ZipString(filename, *(data.get()), file->data.get())) {
123     base::AutoLock lock(attachments_lock_);
124     attachments_.push_back(file);
125   } else {
126     delete file;
127   }
128 }
129 
AddFile(const std::string & filename,scoped_ptr<std::string> data)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()));
134 }
135 
AddLog(const std::string & name,const std::string & value)136 void FeedbackCommon::AddLog(const std::string& name, const std::string& value) {
137   if (!logs_.get())
138     logs_ = scoped_ptr<SystemLogsMap>(new SystemLogsMap);
139   (*logs_.get())[name] = value;
140 }
141 
AddLogs(scoped_ptr<SystemLogsMap> logs)142 void FeedbackCommon::AddLogs(scoped_ptr<SystemLogsMap> logs) {
143   if (logs_) {
144     logs_->insert(logs->begin(), logs->end());
145   } else {
146     logs_ = logs.Pass();
147   }
148 }
149 
CompressLogs()150 void FeedbackCommon::CompressLogs() {
151   if (!logs_)
152     return;
153   std::string* logs = LogsToString(*logs_.get());
154   if (!logs->empty())
155     CompressFile(
156         base::FilePath(kLogsFilename), kLogsAttachmentName,
157         scoped_ptr<std::string>(logs));
158 }
159 
AddFilesAndLogsToReport(userfeedback::ExtensionSubmit * feedback_data) const160 void FeedbackCommon::AddFilesAndLogsToReport(
161     userfeedback::ExtensionSubmit* feedback_data) const {
162   if (sys_info()) {
163     for (FeedbackCommon::SystemLogsMap::const_iterator i = sys_info()->begin();
164          i != sys_info()->end();
165          ++i) {
166       if (BelowCompressionThreshold(i->second))
167         AddFeedbackData(feedback_data, i->first, i->second);
168     }
169   }
170 
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());
174   }
175 }
176 
PrepareReport(userfeedback::ExtensionSubmit * feedback_data) const177 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_);
182 
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());
189 
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());
193 
194   AddFilesAndLogsToReport(feedback_data);
195 
196   if (image() && image()->size()) {
197     userfeedback::PostedScreenshot screenshot;
198     screenshot.set_mime_type(kPngMimeType);
199 
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);
205 
206     *(screenshot.mutable_dimensions()) = dimensions;
207     screenshot.set_binary_content(*image());
208 
209     *(feedback_data->mutable_screenshot()) = screenshot;
210   }
211 
212   if (category_tag().size())
213     feedback_data->set_bucket(category_tag());
214 }
215