CRepository::ResolveResult CRepository::ResolvePathAndHash(const AddonPtr& addon) const { std::string const& path = addon->Path(); auto dirIt = std::find_if(m_dirs.begin(), m_dirs.end(), [&path](DirInfo const& dir) { return URIUtils::PathHasParent(path, dir.datadir, true); }); if (dirIt == m_dirs.end()) { CLog::Log(LOGERROR, "Requested path {} not found in known repository directories", path); return {}; } if (dirIt->hashType == CDigest::Type::INVALID) { // We have a path, but need no hash return {path, {}}; } // Do not follow mirror redirect, we want the headers of the redirect response CURL url{path}; url.SetProtocolOption("redirect-limit", "0"); CCurlFile file; if (!file.Open(url)) { CLog::Log(LOGERROR, "Could not fetch addon location and hash from {}", path); return {}; } std::string hashTypeStr = CDigest::TypeToString(dirIt->hashType); // Return the location from the header so we don't have to look it up again // (saves one request per addon install) std::string location = file.GetRedirectURL(); // content-* headers are base64, convert to base16 TypedDigest hash{dirIt->hashType, StringUtils::ToHexadecimal(Base64::Decode(file.GetHttpHeader().GetValue(std::string("content-") + hashTypeStr)))}; if (hash.Empty()) { // Expected hash, but none found -> fall back to old method if (!FetchChecksum(path + "." + hashTypeStr, hash.value) || hash.Empty()) { CLog::Log(LOGERROR, "Failed to find hash for {} from HTTP header and in separate file", path); return {}; } } if (location.empty()) { // Fall back to original URL if we do not get a redirect location = path; } CLog::Log(LOGDEBUG, "Resolved addon path {} to {} hash {}", path, location, hash.value); return {location, hash}; }