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_, /*recursive=*/false)) {
134       LOG(ERROR) << "History file already exists, and could not be deleted.";
135       return false;
136     } else {
137       LOG(WARNING) << "History file already existed; deleted.";
138     }
139   }
140 
141   // Attempt to create the file.
142   constexpr int file_flags = base::File::FLAG_CREATE | base::File::FLAG_WRITE |
143                              base::File::FLAG_EXCLUSIVE_WRITE;
144   file_.Initialize(path_, file_flags);
145   if (!file_.IsValid() || !file_.created()) {
146     LOG(WARNING) << "Couldn't create history file.";
147     if (!base::DeleteFile(path_, /*recursive=*/false)) {
148       LOG(ERROR) << "Failed to delete " << path_ << ".";
149     }
150     return false;
151   }
152 
153   valid_ = true;
154   return true;
155 }
156 
WriteCaptureTime(base::Time capture_time)157 bool WebRtcEventLogHistoryFileWriter::WriteCaptureTime(
158     base::Time capture_time) {
159   DCHECK(valid_);
160 
161   if (capture_time.is_null()) {
162     valid_ = false;
163     return false;
164   }
165 
166   const std::string delta_seconds = DeltaFromEpochSeconds(capture_time);
167   if (delta_seconds.empty()) {
168     valid_ = false;
169     return false;
170   }
171 
172   const bool written = Write(kCaptureTimeLinePrefix + delta_seconds + kEOL);
173   if (!written) {
174     // Error logged by Write().
175     valid_ = false;
176     return false;
177   }
178 
179   return true;
180 }
181 
WriteUploadTime(base::Time upload_time)182 bool WebRtcEventLogHistoryFileWriter::WriteUploadTime(base::Time upload_time) {
183   DCHECK(valid_);
184 
185   if (upload_time.is_null()) {
186     valid_ = false;
187     return false;
188   }
189 
190   const std::string delta_seconds = DeltaFromEpochSeconds(upload_time);
191   if (delta_seconds.empty()) {
192     valid_ = false;
193     return false;
194   }
195 
196   const bool written = Write(kUploadTimeLinePrefix + delta_seconds + kEOL);
197   if (!written) {
198     valid_ = false;
199     return false;
200   }
201 
202   return true;
203 }
204 
WriteUploadId(const std::string & upload_id)205 bool WebRtcEventLogHistoryFileWriter::WriteUploadId(
206     const std::string& upload_id) {
207   DCHECK(valid_);
208   DCHECK(!upload_id.empty());
209   DCHECK_LE(upload_id.length(), kWebRtcEventLogMaxUploadIdBytes);
210 
211   const bool written = Write(kUploadIdLinePrefix + upload_id + kEOL);
212   if (!written) {
213     valid_ = false;
214     return false;
215   }
216 
217   return true;
218 }
219 
Delete()220 void WebRtcEventLogHistoryFileWriter::Delete() {
221   if (!base::DeleteFile(path_, /*recursive=*/false)) {
222     LOG(ERROR) << "History file could not be deleted.";
223   }
224 
225   valid_ = false;  // Like was already false.
226 }
227 
path() const228 base::FilePath WebRtcEventLogHistoryFileWriter::path() const {
229   DCHECK(valid_);  // Can be performed on invalid objects, but likely shouldn't.
230   return path_;
231 }
232 
Write(const std::string & str)233 bool WebRtcEventLogHistoryFileWriter::Write(const std::string& str) {
234   DCHECK(valid_);
235   DCHECK(!str.empty());
236   DCHECK_LE(str.length(), static_cast<size_t>(std::numeric_limits<int>::max()));
237 
238   const int written = file_.WriteAtCurrentPos(str.c_str(), str.length());
239   if (written != static_cast<int>(str.length())) {
240     LOG(WARNING) << "Writing to history file failed.";
241     valid_ = false;
242     return false;
243   }
244 
245   // Writes to the history file are infrequent, and happen on a |task_runner_|
246   // dedicated to event logs. We can therefore afford to Flush() after every
247   // write, giving us greater confidence that information would not get lost if,
248   // e.g., Chrome crashes.
249   file_.Flush();
250 
251   return true;
252 }
253 
254 std::unique_ptr<WebRtcEventLogHistoryFileReader>
Create(const base::FilePath & path)255 WebRtcEventLogHistoryFileReader::Create(const base::FilePath& path) {
256   auto history_file_reader =
257       base::WrapUnique(new WebRtcEventLogHistoryFileReader(path));
258   if (!history_file_reader->Init()) {
259     LOG(WARNING) << "Initialization of history file reader failed.";
260     return nullptr;
261   }
262   return history_file_reader;
263 }
264 
WebRtcEventLogHistoryFileReader(const base::FilePath & path)265 WebRtcEventLogHistoryFileReader::WebRtcEventLogHistoryFileReader(
266     const base::FilePath& path)
267     : path_(path),
268       local_id_(ExtractRemoteBoundWebRtcEventLogLocalIdFromPath(path_)),
269       valid_(false) {}
270 
WebRtcEventLogHistoryFileReader(WebRtcEventLogHistoryFileReader && other)271 WebRtcEventLogHistoryFileReader::WebRtcEventLogHistoryFileReader(
272     WebRtcEventLogHistoryFileReader&& other)
273     : path_(other.path_),
274       local_id_(other.local_id_),
275       capture_time_(other.capture_time_),
276       upload_time_(other.upload_time_),
277       upload_id_(other.upload_id_),
278       valid_(other.valid_) {
279   other.valid_ = false;
280 }
281 
Init()282 bool WebRtcEventLogHistoryFileReader::Init() {
283   DCHECK(!valid_);
284 
285   if (local_id_.empty()) {
286     LOG(WARNING) << "Unknown local ID.";
287     return false;
288   }
289 
290   if (local_id_.length() > kWebRtcEventLogMaxUploadIdBytes) {
291     LOG(WARNING) << "Excessively long local ID.";
292     return false;
293   }
294 
295   if (!base::PathExists(path_)) {
296     LOG(WARNING) << "File does not exist.";
297     return false;
298   }
299 
300   constexpr int file_flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
301   base::File file(path_, file_flags);
302   if (!file.IsValid()) {
303     LOG(WARNING) << "Couldn't read history file.";
304     if (!base::DeleteFile(path_, /*recursive=*/false)) {
305       LOG(ERROR) << "Failed to delete " << path_ << ".";
306     }
307     return false;
308   }
309 
310   constexpr size_t kMaxHistoryFileSizeBytes = 1024;
311   static_assert(kWebRtcEventLogMaxUploadIdBytes < kMaxHistoryFileSizeBytes, "");
312 
313   std::string file_contents;
314   file_contents.resize(kMaxHistoryFileSizeBytes);
315   const int read_bytes = file.Read(0, &file_contents[0], file_contents.size());
316   if (read_bytes < 0) {
317     LOG(WARNING) << "Couldn't read contents of history file.";
318     return false;
319   }
320   DCHECK_LE(static_cast<size_t>(read_bytes), file_contents.size());
321   file_contents.resize(static_cast<size_t>(read_bytes));
322   // Note: In excessively long files, the rest of the file will be ignored; the
323   // beginning of the file will encounter a parse error.
324 
325   if (!Parse(file_contents)) {
326     LOG(WARNING) << "Parsing of history file failed.";
327     return false;
328   }
329 
330   valid_ = true;
331   return true;
332 }
333 
LocalId() const334 std::string WebRtcEventLogHistoryFileReader::LocalId() const {
335   DCHECK(valid_);
336   DCHECK(!local_id_.empty());
337   return local_id_;
338 }
339 
CaptureTime() const340 base::Time WebRtcEventLogHistoryFileReader::CaptureTime() const {
341   DCHECK(valid_);
342   DCHECK(!capture_time_.is_null());
343   return capture_time_;
344 }
345 
UploadTime() const346 base::Time WebRtcEventLogHistoryFileReader::UploadTime() const {
347   DCHECK(valid_);
348   return upload_time_;  // May be null (which indicates "unset").
349 }
350 
UploadId() const351 std::string WebRtcEventLogHistoryFileReader::UploadId() const {
352   DCHECK(valid_);
353   return upload_id_;
354 }
355 
path() const356 base::FilePath WebRtcEventLogHistoryFileReader::path() const {
357   DCHECK(valid_);
358   return path_;
359 }
360 
operator <(const WebRtcEventLogHistoryFileReader & other) const361 bool WebRtcEventLogHistoryFileReader::operator<(
362     const WebRtcEventLogHistoryFileReader& other) const {
363   DCHECK(valid_);
364   DCHECK(!capture_time_.is_null());
365   DCHECK(other.valid_);
366   DCHECK(!other.capture_time_.is_null());
367   if (capture_time_ == other.capture_time_) {
368     // Resolve ties arbitrarily, but consistently (Local IDs are unique).
369     return LocalId() < other.LocalId();
370   }
371   return (capture_time_ < other.capture_time_);
372 }
373 
Parse(const std::string & file_contents)374 bool WebRtcEventLogHistoryFileReader::Parse(const std::string& file_contents) {
375   DCHECK(!valid_);
376   DCHECK(capture_time_.is_null());
377   DCHECK(upload_time_.is_null());
378   DCHECK(upload_id_.empty());
379 
380   const std::vector<std::string> lines =
381       base::SplitString(file_contents, kEOL, base::TRIM_WHITESPACE,
382                         base::SplitResult::SPLIT_WANT_NONEMPTY);
383 
384   for (const std::string& line : lines) {
385     if (line.find(kCaptureTimeLinePrefix) == 0) {
386       if (!ParseTime(line, kCaptureTimeLinePrefix, &capture_time_)) {
387         return false;
388       }
389     } else if (line.find(kUploadTimeLinePrefix) == 0) {
390       if (!ParseTime(line, kUploadTimeLinePrefix, &upload_time_)) {
391         return false;
392       }
393     } else if (line.find(kUploadIdLinePrefix) == 0) {
394       if (!ParseString(line, kUploadIdLinePrefix, &upload_id_)) {
395         return false;
396       }
397     } else {
398       LOG(WARNING) << "Unrecognized line in history file.";
399       return false;
400     }
401   }
402 
403   if (capture_time_.is_null()) {
404     LOG(WARNING) << "Incomplete history file; capture time unknown.";
405     return false;
406   }
407 
408   if (!upload_id_.empty() && upload_time_.is_null()) {
409     LOG(WARNING) << "Incomplete history file; upload time known, "
410                  << "but ID unknown.";
411     return false;
412   }
413 
414   if (!upload_time_.is_null() && upload_time_ < capture_time_) {
415     LOG(WARNING) << "Defective history file; claims to have been uploaded "
416                  << "before being captured.";
417     return false;
418   }
419 
420   return true;
421 }
422 
423 }  // namespace webrtc_event_logging
424