bool CefCrashReporterClient::ReadCrashConfigFile()

in libcef/common/crash_reporter_client.cc [354:530]


bool CefCrashReporterClient::ReadCrashConfigFile() {
  if (has_crash_config_file_)
    return true;

  PathString config_path = GetCrashConfigPath();
  if (config_path.empty())
    return false;

#if defined(OS_WIN)
  FILE* fp = _wfopen(config_path.c_str(), L"r");
#else
  FILE* fp = fopen(config_path.c_str(), "r");
#endif
  if (!fp)
    return false;

  char line[1000];

  size_t small_index = 0;
  size_t medium_index = 0;
  size_t large_index = 0;
  std::string map_keys;

  enum section {
    kNoSection,
    kConfigSection,
    kCrashKeysSection,
  } current_section = kNoSection;

  while (fgets(line, sizeof(line) - 1, fp) != nullptr) {
    std::string str = line;
    base::TrimString(str, base::kWhitespaceASCII, &str);
    if (str.empty() || str[0] == '#')
      continue;

    if (str == "[Config]") {
      current_section = kConfigSection;
      continue;
    } else if (str == "[CrashKeys]") {
      current_section = kCrashKeysSection;
      continue;
    } else if (str[0] == '[') {
      current_section = kNoSection;
      continue;
    }

    if (current_section == kNoSection)
      continue;

    size_t div = str.find('=');
    if (div == std::string::npos)
      continue;

    std::string name_str = str.substr(0, div);
    base::TrimString(name_str, base::kWhitespaceASCII, &name_str);
    std::string val_str = str.substr(div + 1);
    base::TrimString(val_str, base::kWhitespaceASCII, &val_str);
    if (name_str.empty())
      continue;

    if (current_section == kConfigSection) {
      if (name_str == "ServerURL") {
        ParseURL(val_str, &server_url_);
      } else if (name_str == "ProductName") {
        product_name_ = val_str;
      } else if (name_str == "ProductVersion") {
        product_version_ = val_str;
      } else if (name_str == "RateLimitEnabled") {
        rate_limit_ = ParseBool(val_str);
      } else if (name_str == "MaxUploadsPerDay") {
        max_uploads_ = ParseZeroBasedInt(val_str);
      } else if (name_str == "MaxDatabaseSizeInMb") {
        max_db_size_ = ParseZeroBasedInt(val_str);
      } else if (name_str == "MaxDatabaseAgeInDays") {
        max_db_age_ = ParseZeroBasedInt(val_str);
      }
#if defined(OS_WIN)
      else if (name_str == "ExternalHandler") {
        if (!val_str.empty())
          external_handler_ = sanitizePath(val_str);
      } else if (name_str == "AppName") {
        if (!val_str.empty()) {
          val_str = sanitizePathComponent(val_str);
          if (!val_str.empty())
            app_name_ = val_str;
        }
      }
#elif defined(OS_MAC)
      else if (name_str == "BrowserCrashForwardingEnabled") {
        enable_browser_crash_forwarding_ = ParseBool(val_str);
      }
#endif
    } else if (current_section == kCrashKeysSection) {
      // Skip duplicate definitions.
      if (!crash_keys_.empty() &&
          crash_keys_.find(name_str) != crash_keys_.end()) {
        continue;
      }

      KeySize size;
      size_t index;
      char group;
      if (val_str == "small") {
        size = SMALL_SIZE;
        index = small_index++;
        group = 'S';
      } else if (val_str == "medium") {
        size = MEDIUM_SIZE;
        index = medium_index++;
        group = 'M';
      } else if (val_str == "large") {
        size = LARGE_SIZE;
        index = large_index++;
        group = 'L';
      } else {
        continue;
      }

      name_str = NormalizeCrashKey(name_str);
      crash_keys_.insert(std::make_pair(name_str, std::make_pair(size, index)));

      const std::string& key =
          std::string(1, group) + "-" + std::string(1, 'A' + index);
      if (!map_keys.empty()) {
        map_keys.append(std::string(1, kKeyMapDelim));
      }
      map_keys.append(key + "=" + name_str);
    }
  }

  fclose(fp);

  if (!map_keys.empty()) {
    // Split |map_keys| across multiple Annotations if necessary.
    // Must match the logic in crash_report_utils::FilterParameters.
    using IDKey =
        crash_reporter::CrashKeyString<crashpad::Annotation::kValueMaxSize>;
    static IDKey ids[] = {
        {"K-A", IDKey::Tag::kArray},
        {"K-B", IDKey::Tag::kArray},
        {"K-C", IDKey::Tag::kArray},
    };

    // Make sure we can fit all possible name/value pairs.
    static_assert(base::size(ids) * crashpad::Annotation::kValueMaxSize >=
                      3 * 26 /* sizes (small, medium, large) * slots (A to Z) */
                          * (3 + 2 /* key size ("S-A") + delim size ("=,") */
                             + crashpad::Annotation::kNameMaxLength),
                  "Not enough storage for key map");

    size_t offset = 0;
    for (size_t i = 0; i < base::size(ids); ++i) {
      size_t length = std::min(map_keys.size() - offset,
                               crashpad::Annotation::kValueMaxSize);
      ids[i].Set(base::StringPiece(map_keys.data() + offset, length));
      offset += length;
      if (offset >= map_keys.size())
        break;
    }
  }

  // Allow override of some values via environment variables.
  {
    std::unique_ptr<base::Environment> env(base::Environment::Create());
    std::string val_str;

    if (env->GetVar("CEF_CRASH_REPORTER_SERVER_URL", &val_str)) {
      ParseURL(val_str, &server_url_);
    }
    if (env->GetVar("CEF_CRASH_REPORTER_RATE_LIMIT_ENABLED", &val_str)) {
      rate_limit_ = ParseBool(val_str);
    }
  }

  has_crash_config_file_ = true;
  return true;
}