text_log_upload_list.cc 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. // Copyright 2017 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. #include "components/upload_list/text_log_upload_list.h"
  5. #include <algorithm>
  6. #include <sstream>
  7. #include "base/containers/adapters.h"
  8. #include "base/files/file_util.h"
  9. #include "base/json/json_reader.h"
  10. #include "base/strings/string_number_conversions.h"
  11. #include "base/strings/string_split.h"
  12. #include "base/strings/string_util.h"
  13. #include "base/time/time.h"
  14. #include "third_party/abseil-cpp/absl/types/optional.h"
  15. namespace {
  16. constexpr size_t kUploadTimeIndex = 0;
  17. constexpr size_t kCaptureTimeIndex = 3;
  18. constexpr char kJsonLogKeyUploadId[] = "upload_id";
  19. constexpr char kJsonLogKeyUploadTime[] = "upload_time";
  20. constexpr char kJsonLogKeyLocalId[] = "local_id";
  21. constexpr char kJsonLogKeyCaptureTime[] = "capture_time";
  22. constexpr char kJsonLogKeyState[] = "state";
  23. constexpr char kJsonLogKeySource[] = "source";
  24. std::vector<std::string> SplitIntoLines(const std::string& file_contents) {
  25. return base::SplitString(file_contents, base::kWhitespaceASCII,
  26. base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
  27. }
  28. std::vector<std::string> SplitIntoComponents(const std::string& line) {
  29. return base::SplitString(line, ",", base::TRIM_WHITESPACE,
  30. base::SPLIT_WANT_ALL);
  31. }
  32. bool CheckCsvUploadListOutOfRange(const std::string& line,
  33. const base::Time& begin,
  34. const base::Time& end) {
  35. std::vector<std::string> components = SplitIntoComponents(line);
  36. double seconds_since_epoch;
  37. if (components.size() > kUploadTimeIndex &&
  38. !components[kUploadTimeIndex].empty() &&
  39. base::StringToDouble(components[kUploadTimeIndex],
  40. &seconds_since_epoch)) {
  41. base::Time upload_time = base::Time::FromDoubleT(seconds_since_epoch);
  42. if (begin <= upload_time && upload_time <= end)
  43. return false;
  44. }
  45. if (components.size() > kCaptureTimeIndex &&
  46. !components[kCaptureTimeIndex].empty() &&
  47. base::StringToDouble(components[kCaptureTimeIndex],
  48. &seconds_since_epoch)) {
  49. base::Time capture_time = base::Time::FromDoubleT(seconds_since_epoch);
  50. if (begin <= capture_time && capture_time <= end)
  51. return false;
  52. }
  53. return true;
  54. }
  55. bool CheckFieldOutOfRange(const std::string* time_string,
  56. const base::Time& begin,
  57. const base::Time& end) {
  58. if (time_string) {
  59. double upload_time_double = 0.0;
  60. if (base::StringToDouble(*time_string, &upload_time_double)) {
  61. base::Time upload_time = base::Time::FromDoubleT(upload_time_double);
  62. if (begin <= upload_time && upload_time <= end)
  63. return false;
  64. }
  65. }
  66. return true;
  67. }
  68. bool CheckJsonUploadListOutOfRange(const base::Value& dict,
  69. const base::Time& begin,
  70. const base::Time& end) {
  71. const std::string* upload_time_string =
  72. dict.FindStringKey(kJsonLogKeyUploadTime);
  73. const std::string* capture_time_string =
  74. dict.FindStringKey(kJsonLogKeyCaptureTime);
  75. return CheckFieldOutOfRange(upload_time_string, begin, end) &&
  76. CheckFieldOutOfRange(capture_time_string, begin, end);
  77. }
  78. // Tries to parse one upload log line based on CSV format, then converts it to
  79. // a UploadInfo entry. If the conversion succeeds, it returns a valid UploadInfo
  80. // instance. Otherwise, it returns nullptr.
  81. std::unique_ptr<TextLogUploadList::UploadInfo> TryParseCsvLogEntry(
  82. const std::string& log_line) {
  83. std::vector<std::string> components = SplitIntoComponents(log_line);
  84. // Skip any blank (or corrupted) lines.
  85. if (components.size() < 2 || components.size() > 5)
  86. return nullptr;
  87. base::Time upload_time;
  88. double seconds_since_epoch;
  89. if (!components[kUploadTimeIndex].empty()) {
  90. if (!base::StringToDouble(components[kUploadTimeIndex],
  91. &seconds_since_epoch))
  92. return nullptr;
  93. upload_time = base::Time::FromDoubleT(seconds_since_epoch);
  94. }
  95. auto info = std::make_unique<TextLogUploadList::UploadInfo>(components[1],
  96. upload_time);
  97. // Add local ID if present.
  98. if (components.size() > 2)
  99. info->local_id = components[2];
  100. // Add capture time if present.
  101. if (components.size() > kCaptureTimeIndex &&
  102. !components[kCaptureTimeIndex].empty() &&
  103. base::StringToDouble(components[kCaptureTimeIndex],
  104. &seconds_since_epoch)) {
  105. info->capture_time = base::Time::FromDoubleT(seconds_since_epoch);
  106. }
  107. int state;
  108. if (components.size() > 4 && !components[4].empty() &&
  109. base::StringToInt(components[4], &state)) {
  110. info->state = static_cast<TextLogUploadList::UploadInfo::State>(state);
  111. }
  112. return info;
  113. }
  114. // Tries to parse one upload log dictionary based on line-based JSON format (no
  115. // internal additional newline is permitted), then converts it to a UploadInfo
  116. // entry. If the conversion succeeds, it returns a valid UploadInfo instance.
  117. // Otherwise, it returns nullptr.
  118. std::unique_ptr<TextLogUploadList::UploadInfo> TryParseJsonLogEntry(
  119. const base::Value& dict) {
  120. // Parse upload_id.
  121. const base::Value* upload_id_value = dict.FindKey(kJsonLogKeyUploadId);
  122. if (upload_id_value && !upload_id_value->is_string())
  123. return nullptr;
  124. // Parse upload_time.
  125. const std::string* upload_time_string =
  126. dict.FindStringKey(kJsonLogKeyUploadTime);
  127. double upload_time_double = 0.0;
  128. if (upload_time_string &&
  129. !base::StringToDouble(*upload_time_string, &upload_time_double))
  130. return nullptr;
  131. auto info = std::make_unique<TextLogUploadList::UploadInfo>(
  132. upload_id_value ? upload_id_value->GetString() : std::string(),
  133. base::Time::FromDoubleT(upload_time_double));
  134. // Parse local_id.
  135. const std::string* local_id = dict.FindStringKey(kJsonLogKeyLocalId);
  136. if (local_id)
  137. info->local_id = *local_id;
  138. // Parse capture_time.
  139. const std::string* capture_time_string =
  140. dict.FindStringKey(kJsonLogKeyCaptureTime);
  141. double capture_time_double = 0.0;
  142. if (capture_time_string &&
  143. base::StringToDouble(*capture_time_string, &capture_time_double))
  144. info->capture_time = base::Time::FromDoubleT(capture_time_double);
  145. // Parse state.
  146. absl::optional<int> state = dict.FindIntKey(kJsonLogKeyState);
  147. if (state.has_value())
  148. info->state =
  149. static_cast<TextLogUploadList::UploadInfo::State>(state.value());
  150. // Parse source.
  151. const std::string* source = dict.FindStringKey(kJsonLogKeySource);
  152. if (source)
  153. info->source = *source;
  154. return info;
  155. }
  156. } // namespace
  157. TextLogUploadList::TextLogUploadList(const base::FilePath& upload_log_path)
  158. : upload_log_path_(upload_log_path) {}
  159. TextLogUploadList::~TextLogUploadList() = default;
  160. std::vector<UploadList::UploadInfo> TextLogUploadList::LoadUploadList() {
  161. std::vector<UploadInfo> uploads;
  162. if (base::PathExists(upload_log_path_)) {
  163. std::string contents;
  164. base::ReadFileToString(upload_log_path_, &contents);
  165. ParseLogEntries(SplitIntoLines(contents), &uploads);
  166. }
  167. return uploads;
  168. }
  169. void TextLogUploadList::ClearUploadList(const base::Time& begin,
  170. const base::Time& end) {
  171. if (!base::PathExists(upload_log_path_))
  172. return;
  173. std::string contents;
  174. base::ReadFileToString(upload_log_path_, &contents);
  175. std::vector<std::string> log_entries = SplitIntoLines(contents);
  176. std::ostringstream new_contents_stream;
  177. for (const std::string& line : log_entries) {
  178. absl::optional<base::Value> json = base::JSONReader::Read(line);
  179. bool should_copy = false;
  180. if (json.has_value())
  181. should_copy = CheckJsonUploadListOutOfRange(json.value(), begin, end);
  182. else
  183. should_copy = CheckCsvUploadListOutOfRange(line, begin, end);
  184. if (should_copy)
  185. new_contents_stream << line << std::endl;
  186. }
  187. std::string new_contents = new_contents_stream.str();
  188. if (new_contents.size() == 0) {
  189. base::DeleteFile(upload_log_path_);
  190. } else {
  191. base::WriteFile(upload_log_path_, new_contents.c_str(),
  192. new_contents.size());
  193. }
  194. }
  195. void TextLogUploadList::ParseLogEntries(
  196. const std::vector<std::string>& log_entries,
  197. std::vector<UploadInfo>* uploads) {
  198. for (const std::string& line : base::Reversed(log_entries)) {
  199. std::unique_ptr<UploadInfo> info;
  200. absl::optional<base::Value> json = base::JSONReader::Read(line);
  201. if (json.has_value() && json->is_dict())
  202. info = TryParseJsonLogEntry(json.value());
  203. else
  204. info = TryParseCsvLogEntry(line);
  205. if (info)
  206. uploads->push_back(*info);
  207. }
  208. }