1 // Copyright 2018 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/media/webrtc/webrtc_event_log_history.h"
6 
7 #include <limits>
8 #include <utility>
9 #include <vector>
10 
11 #include "base/files/file_util.h"
12 #include "base/logging.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/stl_util.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_split.h"
17 #include "chrome/browser/media/webrtc/webrtc_event_log_manager_common.h"
18 
19 namespace webrtc_event_logging {
20 
21 const size_t kWebRtcEventLogMaxUploadIdBytes = 100;
22 
23 namespace {
24 // Compactness is not important for these few and small files; we therefore
25 // go with a human-readable format.
26 const char kCaptureTimeLinePrefix[] =
27     "Capture time (seconds since UNIX epoch): ";
28 const char kUploadTimeLinePrefix[] = "Upload time (seconds since UNIX epoch): ";
29 const char kUploadIdLinePrefix[] = "Upload ID: ";
30 
31 // No need to use \r\n for Windows; better have a consistent file format
32 // between platforms.
33 const char kEOL[] = "\n";
34 static_assert(base::size(kEOL) == 1 + 1 /* +1 for the implicit \0. */,
35               "SplitString relies on this being a single character.");
36 
37 // |time| must *not* be earlier than UNIX epoch start. If it is, the empty
38 // string is returned.
DeltaFromEpochSeconds(base::Time time)39 std::string DeltaFromEpochSeconds(base::Time time) {
40   if (time.is_null() || time.is_min() || time.is_max()) {
41     LOG(ERROR) << "Not a valid time (" << time << ").";
42     return std::string();
43   }
44 
45   const base::Time epoch = base::Time::UnixEpoch();
46   if (time < epoch) {
47     LOG(WARNING) << "Time to go back to the future.";
48     return std::string();
49   }
50 
51   return base::NumberToString((time - epoch).InSeconds());
52 }
53 
54 // Helper for ParseTime; see its documentation for details.
StringToTime(const std::string & time)55 base::Time StringToTime(const std::string& time) {
56   int64_t seconds_from_epoch;
57   if (!base::StringToInt64(time, &seconds_from_epoch) ||
58       seconds_from_epoch < 0) {
59     LOG(WARNING) << "Error encountered while reading time.";
60     return base::Time();
61   }
62 
63   return base::Time::UnixEpoch() +
64          base::TimeDelta::FromSeconds(seconds_from_epoch);
65 }
66 
67 // Convert a history file's timestamp, which is the number of seconds since
68 // UNIX epoch, into a base::Time object.
69 // This function errors on timestamps from UNIX epoch or before it.
ParseTime(const std::string & line,const std::string & prefix,base::Time * out)70 bool ParseTime(const std::string& line,
71                const std::string& prefix,
72                base::Time* out) {
73   DCHECK(line.find(prefix) == 0);
74   DCHECK(out);
75 
76   if (!out->is_null()) {
77     LOG(WARNING) << "Repeated line.";
78     return false;
79   }
80 
81   const base::Time time = StringToTime(line.substr(prefix.length()));
82   if (time.is_null()) {
83     LOG(WARNING) << "Null time.";
84     return false;
85   }
86 
87   *out = time;
88 
89   return true;
90 }
91 
ParseString(const std::string & line,const std::string & prefix,std::string * out)92 bool ParseString(const std::string& line,
93                  const std::string& prefix,
94                  std::string* out) {
95   DCHECK(line.find(prefix) == 0);
96   DCHECK(out);
97 
98   if (!out->empty()) {
99     LOG(WARNING) << "Repeated line.";
100     return false;
101   }
102 
103   *out = line.substr(prefix.length());
104 
105   if (out->empty()) {
106     LOG(WARNING) << "Empty string.";
107     return false;
108   }
109 
110   return true;
111 }
112 }  // namespace
113 
114 std::unique_ptr<WebRtcEventLogHistoryFileWriter>
Create(const base::FilePath & path)115 WebRtcEventLogHistoryFileWriter::Create(const base::FilePath& path) {
116   auto history_file_writer =
117       base::WrapUnique(new WebRtcEventLogHistoryFileWriter(path));
118   if (!history_file_writer->Init()) {
119     LOG(WARNING) << "Initialization of history file writer failed.";
120     return nullptr;
121   }
122   return history_file_writer;
123 }
124 
WebRtcEventLogHistoryFileWriter(const base::FilePath & path)125 WebRtcEventLogHistoryFileWriter::WebRtcEventLogHistoryFileWriter(
126     const base::FilePath& path)
127     : path_(path), valid_(false) {}
128 
Init()129 bool WebRtcEventLogHistoryFileWriter::Init() {
130   DCHECK(!valid_);
131 
132   if (base::PathExists(path_)) {
133     if (!base::DeleteFile(path_)) {
134       LOG(ERROR) << "History file already exists, and could not be deleted.";
135       return false;
136     }
137     LOG(WARNING) << "History file already existed; deleted.";
138   }
139 
140   // Attempt to create the file.
141   constexpr int file_flags = base::File::FLAG_CREATE | base::File::FLAG_WRITE |
142                              base::File::FLAG_EXCLUSIVE_WRITE;
143   file_.Initialize(path_, file_flags);
144   if (!file_.IsValid() || !file_.created()) {
145     LOG(WARNING) << "Couldn't create history file.";
146     if (!base::DeleteFile(path_)) {
147       LOG(ERROR) << "Failed to delete " << path_ << ".";
148     }
149     return false;
150   }
151 
152   valid_ = true;
153   return true;
154 }
155 
WriteCaptureTime(base::Time capture_time)156 bool WebRtcEventLogHistoryFileWriter::WriteCaptureTime(
157     base::Time capture_time) {
158   DCHECK(valid_);
159 
160   if (capture_time.is_null()) {
161     valid_ = false;
162     return false;
163   }
164 
165   const std::string delta_seconds = DeltaFromEpochSeconds(capture_time);
166   if (delta_seconds.empty()) {
167     valid_ = false;
168     return false;
169   }
170 
171   const bool written = Write(kCaptureTimeLinePrefix + delta_seconds + kEOL);
172   if (!written) {
173     // Error logged by Write().
174     valid_ = false;
175     return false;
176   }
177 
178   return true;
179 }
180 
WriteUploadTime(base::Time upload_time)181 bool WebRtcEventLogHistoryFileWriter::WriteUploadTime(base::Time upload_time) {
182   DCHECK(valid_);
183 
184   if (upload_time.is_null()) {
185     valid_ = false;
186     return false;
187   }
188 
189   const std::string delta_seconds = DeltaFromEpochSeconds(upload_time);
190   if (delta_seconds.empty()) {
191     valid_ = false;
192     return false;
193   }
194 
195   const bool written = Write(kUploadTimeLinePrefix + delta_seconds + kEOL);
196   if (!written) {
197     valid_ = false;
198     return false;
199   }
200 
201   return true;
202 }
203 
WriteUploadId(const std::string & upload_id)204 bool WebRtcEventLogHistoryFileWriter::WriteUploadId(
205     const std::string& upload_id) {
206   DCHECK(valid_);
207   DCHECK(!upload_id.empty());
208   DCHECK_LE(upload_id.length(), kWebRtcEventLogMaxUploadIdBytes);
209 
210   const bool written = Write(kUploadIdLinePrefix + upload_id + kEOL);
211   if (!written) {
212     valid_ = false;
213     return false;
214   }
215 
216   return true;
217 }
218 
Delete()219 void WebRtcEventLogHistoryFileWriter::Delete() {
220   if (!base::DeleteFile(path_)) {
221     LOG(ERROR) << "History file could not be deleted.";
222   }
223 
224   valid_ = false;  // Like was already false.
225 }
226 
path() const227 base::FilePath WebRtcEventLogHistoryFileWriter::path() const {
228   DCHECK(valid_);  // Can be performed on invalid objects, but likely shouldn't.
229   return path_;
230 }
231 
Write(const std::string & str)232 bool WebRtcEventLogHistoryFileWriter::Write(const std::string& str) {
233   DCHECK(valid_);
234   DCHECK(!str.empty());
235   DCHECK_LE(str.length(), static_cast<size_t>(std::numeric_limits<int>::max()));
236 
237   const int written = file_.WriteAtCurrentPos(str.c_str(), str.length());
238   if (written != static_cast<int>(str.length())) {
239     LOG(WARNING) << "Writing to history file failed.";
240     valid_ = false;
241     return false;
242   }
243 
244   // Writes to the history file are infrequent, and happen on a |task_runner_|
245   // dedicated to event logs. We can therefore afford to Flush() after every
246   // write, giving us greater confidence that information would not get lost if,
247   // e.g., Chrome crashes.
248   file_.Flush();
249 
250   return true;
251 }
252 
253 std::unique_ptr<WebRtcEventLogHistoryFileReader>
Create(const base::FilePath & path)254 WebRtcEventLogHistoryFileReader::Create(const base::FilePath& path) {
255   auto history_file_reader =
256       base::WrapUnique(new WebRtcEventLogHistoryFileReader(path));
257   if (!history_file_reader->Init()) {
258     LOG(WARNING) << "Initialization of history file reader failed.";
259     return nullptr;
260   }
261   return history_file_reader;
262 }
263 
WebRtcEventLogHistoryFileReader(const base::FilePath & path)264 WebRtcEventLogHistoryFileReader::WebRtcEventLogHistoryFileReader(
265     const base::FilePath& path)
266     : path_(path),
267       local_id_(ExtractRemoteBoundWebRtcEventLogLocalIdFromPath(path_)),
268       valid_(false) {}
269 
WebRtcEventLogHistoryFileReader(WebRtcEventLogHistoryFileReader && other)270 WebRtcEventLogHistoryFileReader::WebRtcEventLogHistoryFileReader(
271     WebRtcEventLogHistoryFileReader&& other)
272     : path_(other.path_),
273       local_id_(other.local_id_),
274       capture_time_(other.capture_time_),
275       upload_time_(other.upload_time_),
276       upload_id_(other.upload_id_),
277       valid_(other.valid_) {
278   other.valid_ = false;
279 }
280 
Init()281 bool WebRtcEventLogHistoryFileReader::Init() {
282   DCHECK(!valid_);
283 
284   if (local_id_.empty()) {
285     LOG(WARNING) << "Unknown local ID.";
286     return false;
287   }
288 
289   if (local_id_.length() > kWebRtcEventLogMaxUploadIdBytes) {
290     LOG(WARNING) << "Excessively long local ID.";
291     return false;
292   }
293 
294   if (!base::PathExists(path_)) {
295     LOG(WARNING) << "File does not exist.";
296     return false;
297   }
298 
299   constexpr int file_flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
300   base::File file(path_, file_flags);
301   if (!file.IsValid()) {
302     LOG(WARNING) << "Couldn't read history file.";
303     if (!base::DeleteFile(path_)) {
304       LOG(ERROR) << "Failed to delete " << path_ << ".";
305     }
306     return false;
307   }
308 
309   constexpr size_t kMaxHistoryFileSizeBytes = 1024;
310   static_assert(kWebRtcEventLogMaxUploadIdBytes < kMaxHistoryFileSizeBytes, "");
311 
312   std::string file_contents;
313   file_contents.resize(kMaxHistoryFileSizeBytes);
314   const int read_bytes = file.Read(0, &file_contents[0], file_contents.size());
315   if (read_bytes < 0) {
316     LOG(WARNING) << "Couldn't read contents of history file.";
317     return false;
318   }
319   DCHECK_LE(static_cast<size_t>(read_bytes), file_contents.size());
320   file_contents.resize(static_cast<size_t>(read_bytes));
321   // Note: In excessively long files, the rest of the file will be ignored; the
322   // beginning of the file will encounter a parse error.
323 
324   if (!Parse(file_contents)) {
325     LOG(WARNING) << "Parsing of history file failed.";
326     return false;
327   }
328 
329   valid_ = true;
330   return true;
331 }
332 
LocalId() const333 std::string WebRtcEventLogHistoryFileReader::LocalId() const {
334   DCHECK(valid_);
335   DCHECK(!local_id_.empty());
336   return local_id_;
337 }
338 
CaptureTime() const339 base::Time WebRtcEventLogHistoryFileReader::CaptureTime() const {
340   DCHECK(valid_);
341   DCHECK(!capture_time_.is_null());
342   return capture_time_;
343 }
344 
UploadTime() const345 base::Time WebRtcEventLogHistoryFileReader::UploadTime() const {
346   DCHECK(valid_);
347   return upload_time_;  // May be null (which indicates "unset").
348 }
349 
UploadId() const350 std::string WebRtcEventLogHistoryFileReader::UploadId() const {
351   DCHECK(valid_);
352   return upload_id_;
353 }
354 
path() const355 base::FilePath WebRtcEventLogHistoryFileReader::path() const {
356   DCHECK(valid_);
357   return path_;
358 }
359 
operator <(const WebRtcEventLogHistoryFileReader & other) const360 bool WebRtcEventLogHistoryFileReader::operator<(
361     const WebRtcEventLogHistoryFileReader& other) const {
362   DCHECK(valid_);
363   DCHECK(!capture_time_.is_null());
364   DCHECK(other.valid_);
365   DCHECK(!other.capture_time_.is_null());
366   if (capture_time_ == other.capture_time_) {
367     // Resolve ties arbitrarily, but consistently (Local IDs are unique).
368     return LocalId() < other.LocalId();
369   }
370   return (capture_time_ < other.capture_time_);
371 }
372 
Parse(const std::string & file_contents)373 bool WebRtcEventLogHistoryFileReader::Parse(const std::string& file_contents) {
374   DCHECK(!valid_);
375   DCHECK(capture_time_.is_null());
376   DCHECK(upload_time_.is_null());
377   DCHECK(upload_id_.empty());
378 
379   const std::vector<std::string> lines =
380       base::SplitString(file_contents, kEOL, base::TRIM_WHITESPACE,
381                         base::SplitResult::SPLIT_WANT_NONEMPTY);
382 
383   for (const std::string& line : lines) {
384     if (line.find(kCaptureTimeLinePrefix) == 0) {
385       if (!ParseTime(line, kCaptureTimeLinePrefix, &capture_time_)) {
386         return false;
387       }
388     } else if (line.find(kUploadTimeLinePrefix) == 0) {
389       if (!ParseTime(line, kUploadTimeLinePrefix, &upload_time_)) {
390         return false;
391       }
392     } else if (line.find(kUploadIdLinePrefix) == 0) {
393       if (!ParseString(line, kUploadIdLinePrefix, &upload_id_)) {
394         return false;
395       }
396     } else {
397       LOG(WARNING) << "Unrecognized line in history file.";
398       return false;
399     }
400   }
401 
402   if (capture_time_.is_null()) {
403     LOG(WARNING) << "Incomplete history file; capture time unknown.";
404     return false;
405   }
406 
407   if (!upload_id_.empty() && upload_time_.is_null()) {
408     LOG(WARNING) << "Incomplete history file; upload time known, "
409                  << "but ID unknown.";
410     return false;
411   }
412 
413   if (!upload_time_.is_null() && upload_time_ < capture_time_) {
414     LOG(WARNING) << "Defective history file; claims to have been uploaded "
415                  << "before being captured.";
416     return false;
417   }
418 
419   return true;
420 }
421 
422 }  // namespace webrtc_event_logging
423