mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-18 11:13:28 +01:00
[AltStoreCore] Generalizes Source.sourceID(from:) logic into URL.normalized()
Allows comparing URLs that may have slight (but irrelevant) differences (e.g. trailing slashes).
This commit is contained in:
@@ -371,6 +371,7 @@
|
|||||||
D5418F172AD740890014ABD6 /* AppScreenshotCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5418F162AD740890014ABD6 /* AppScreenshotCollectionViewCell.swift */; };
|
D5418F172AD740890014ABD6 /* AppScreenshotCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5418F162AD740890014ABD6 /* AppScreenshotCollectionViewCell.swift */; };
|
||||||
D54DED1428CBC44B008B27A0 /* ErrorLogTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D54DED1328CBC44B008B27A0 /* ErrorLogTableViewCell.swift */; };
|
D54DED1428CBC44B008B27A0 /* ErrorLogTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D54DED1328CBC44B008B27A0 /* ErrorLogTableViewCell.swift */; };
|
||||||
D552B1D82A042A740066216F /* AppPermissionsCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = D552B1D72A042A740066216F /* AppPermissionsCard.swift */; };
|
D552B1D82A042A740066216F /* AppPermissionsCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = D552B1D72A042A740066216F /* AppPermissionsCard.swift */; };
|
||||||
|
D552EB062AF453F900A3AB4D /* URL+Normalized.swift in Sources */ = {isa = PBXBuildFile; fileRef = D552EB052AF453F900A3AB4D /* URL+Normalized.swift */; };
|
||||||
D55467B82A8D5E2600F4CE90 /* AppShortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55467B12A8D5E2600F4CE90 /* AppShortcuts.swift */; };
|
D55467B82A8D5E2600F4CE90 /* AppShortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55467B12A8D5E2600F4CE90 /* AppShortcuts.swift */; };
|
||||||
D55467C52A8D72C300F4CE90 /* ActiveAppsWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55467C42A8D72C300F4CE90 /* ActiveAppsWidget.swift */; };
|
D55467C52A8D72C300F4CE90 /* ActiveAppsWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55467C42A8D72C300F4CE90 /* ActiveAppsWidget.swift */; };
|
||||||
D561B2EB28EF5A4F006752E4 /* AltSign-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = D561B2EA28EF5A4F006752E4 /* AltSign-Dynamic */; };
|
D561B2EB28EF5A4F006752E4 /* AltSign-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = D561B2EA28EF5A4F006752E4 /* AltSign-Dynamic */; };
|
||||||
@@ -1041,6 +1042,7 @@
|
|||||||
D5418F162AD740890014ABD6 /* AppScreenshotCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppScreenshotCollectionViewCell.swift; sourceTree = "<group>"; };
|
D5418F162AD740890014ABD6 /* AppScreenshotCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppScreenshotCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
D54DED1328CBC44B008B27A0 /* ErrorLogTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorLogTableViewCell.swift; sourceTree = "<group>"; };
|
D54DED1328CBC44B008B27A0 /* ErrorLogTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorLogTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
D552B1D72A042A740066216F /* AppPermissionsCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppPermissionsCard.swift; sourceTree = "<group>"; };
|
D552B1D72A042A740066216F /* AppPermissionsCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppPermissionsCard.swift; sourceTree = "<group>"; };
|
||||||
|
D552EB052AF453F900A3AB4D /* URL+Normalized.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Normalized.swift"; sourceTree = "<group>"; };
|
||||||
D55467B12A8D5E2600F4CE90 /* AppShortcuts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppShortcuts.swift; sourceTree = "<group>"; };
|
D55467B12A8D5E2600F4CE90 /* AppShortcuts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppShortcuts.swift; sourceTree = "<group>"; };
|
||||||
D55467C42A8D72C300F4CE90 /* ActiveAppsWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveAppsWidget.swift; sourceTree = "<group>"; };
|
D55467C42A8D72C300F4CE90 /* ActiveAppsWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveAppsWidget.swift; sourceTree = "<group>"; };
|
||||||
D56915052AD5D75B00A2B747 /* Regex+Permissions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Regex+Permissions.swift"; sourceTree = "<group>"; };
|
D56915052AD5D75B00A2B747 /* Regex+Permissions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Regex+Permissions.swift"; sourceTree = "<group>"; };
|
||||||
@@ -1753,6 +1755,7 @@
|
|||||||
D5B6F6A82AD75D01007EED5A /* ProcessInfo+Previews.swift */,
|
D5B6F6A82AD75D01007EED5A /* ProcessInfo+Previews.swift */,
|
||||||
D56915052AD5D75B00A2B747 /* Regex+Permissions.swift */,
|
D56915052AD5D75B00A2B747 /* Regex+Permissions.swift */,
|
||||||
D5B6F6A82AD75D01007EED5A /* ProcessInfo+Previews.swift */,
|
D5B6F6A82AD75D01007EED5A /* ProcessInfo+Previews.swift */,
|
||||||
|
D552EB052AF453F900A3AB4D /* URL+Normalized.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -3077,6 +3080,7 @@
|
|||||||
BF66EECD2501AECA007EE018 /* StoreAppPolicy.swift in Sources */,
|
BF66EECD2501AECA007EE018 /* StoreAppPolicy.swift in Sources */,
|
||||||
BF66EEE82501AED0007EE018 /* UserDefaults+AltStore.swift in Sources */,
|
BF66EEE82501AED0007EE018 /* UserDefaults+AltStore.swift in Sources */,
|
||||||
BF340E9A250AD39500A192CB /* ViewApp.intentdefinition in Sources */,
|
BF340E9A250AD39500A192CB /* ViewApp.intentdefinition in Sources */,
|
||||||
|
D552EB062AF453F900A3AB4D /* URL+Normalized.swift in Sources */,
|
||||||
BFAECC522501B0A400528F27 /* CodableError.swift in Sources */,
|
BFAECC522501B0A400528F27 /* CodableError.swift in Sources */,
|
||||||
D5F9821D2AB900060045751F /* AppScreenshot.swift in Sources */,
|
D5F9821D2AB900060045751F /* AppScreenshot.swift in Sources */,
|
||||||
BF66EE9E2501AEC1007EE018 /* Fetchable.swift in Sources */,
|
BF66EE9E2501AEC1007EE018 /* Fetchable.swift in Sources */,
|
||||||
|
|||||||
60
AltStoreCore/Extensions/URL+Normalized.swift
Normal file
60
AltStoreCore/Extensions/URL+Normalized.swift
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
//
|
||||||
|
// URL+Normalized.swift
|
||||||
|
// AltStoreCore
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 11/2/23.
|
||||||
|
// Copyright © 2023 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public extension URL
|
||||||
|
{
|
||||||
|
func normalized() throws -> String
|
||||||
|
{
|
||||||
|
// Based on https://encyclopedia.pub/entry/29841
|
||||||
|
|
||||||
|
guard var components = URLComponents(url: self, resolvingAgainstBaseURL: true) else { throw URLError(.badURL, userInfo: [NSURLErrorKey: self, NSURLErrorFailingURLErrorKey: self]) }
|
||||||
|
|
||||||
|
if components.scheme == nil && components.host == nil
|
||||||
|
{
|
||||||
|
// Special handling for URLs without explicit scheme & incorrectly assumed to have nil host (e.g. "altstore.io/my/path")
|
||||||
|
guard let updatedComponents = URLComponents(string: "https://" + self.absoluteString) else { throw URLError(.cannotFindHost, userInfo: [NSURLErrorKey: self, NSURLErrorFailingURLErrorKey: self]) }
|
||||||
|
components = updatedComponents
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Don't use percent encoding
|
||||||
|
guard let host = components.host else { throw URLError(.cannotFindHost, userInfo: [NSURLErrorKey: self, NSURLErrorFailingURLErrorKey: self]) }
|
||||||
|
|
||||||
|
// 2. Ignore scheme
|
||||||
|
var normalizedURL = host
|
||||||
|
|
||||||
|
// 3. Add port (if not default)
|
||||||
|
if let port = components.port, port != 80 && port != 443
|
||||||
|
{
|
||||||
|
normalizedURL += ":" + String(port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Add path without fragment or query parameters
|
||||||
|
// 5. Remove duplicate slashes
|
||||||
|
let path = components.path.replacingOccurrences(of: "//", with: "/") // Only remove duplicate slashes from path, not entire URL.
|
||||||
|
normalizedURL += path // path has leading `/`
|
||||||
|
|
||||||
|
// 6. Convert to lowercase
|
||||||
|
normalizedURL = normalizedURL.lowercased()
|
||||||
|
|
||||||
|
// 7. Remove trailing `/`
|
||||||
|
if normalizedURL.hasSuffix("/")
|
||||||
|
{
|
||||||
|
normalizedURL.removeLast()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. Remove leading "www"
|
||||||
|
if normalizedURL.hasPrefix("www.")
|
||||||
|
{
|
||||||
|
normalizedURL.removeFirst(4)
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizedURL
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -392,50 +392,8 @@ internal extension Source
|
|||||||
{
|
{
|
||||||
class func sourceID(from sourceURL: URL) throws -> String
|
class func sourceID(from sourceURL: URL) throws -> String
|
||||||
{
|
{
|
||||||
// Based on https://encyclopedia.pub/entry/29841
|
let sourceID = try sourceURL.normalized()
|
||||||
|
return sourceID
|
||||||
guard var components = URLComponents(url: sourceURL, resolvingAgainstBaseURL: true) else { throw URLError(.badURL, userInfo: [NSURLErrorKey: sourceURL]) }
|
|
||||||
|
|
||||||
if components.scheme == nil && components.host == nil
|
|
||||||
{
|
|
||||||
// Special handling for URLs without explicit scheme & incorrectly assumed to have nil host (e.g. "altstore.io/my/path")
|
|
||||||
guard let updatedComponents = URLComponents(string: "https://" + sourceURL.absoluteString) else { throw URLError(.cannotFindHost, userInfo: [NSURLErrorKey: sourceURL]) }
|
|
||||||
components = updatedComponents
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. Don't use percent encoding
|
|
||||||
guard let host = components.host else { throw URLError(.cannotFindHost, userInfo: [NSURLErrorKey: sourceURL]) }
|
|
||||||
|
|
||||||
// 2. Ignore scheme
|
|
||||||
var standardizedID = host
|
|
||||||
|
|
||||||
// 3. Add port (if not default)
|
|
||||||
if let port = components.port, port != 80 && port != 443
|
|
||||||
{
|
|
||||||
standardizedID += ":" + String(port)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Add path without fragment or query parameters
|
|
||||||
// 5. Remove duplicate slashes
|
|
||||||
let path = components.path.replacingOccurrences(of: "//", with: "/") // Only remove duplicate slashes from path, not entire URL.
|
|
||||||
standardizedID += path // path has leading `/`
|
|
||||||
|
|
||||||
// 6. Convert to lowercase
|
|
||||||
standardizedID = standardizedID.lowercased()
|
|
||||||
|
|
||||||
// 7. Remove trailing `/`
|
|
||||||
if standardizedID.hasSuffix("/")
|
|
||||||
{
|
|
||||||
standardizedID.removeLast()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8. Remove leading "www"
|
|
||||||
if standardizedID.hasPrefix("www.")
|
|
||||||
{
|
|
||||||
standardizedID.removeFirst(4)
|
|
||||||
}
|
|
||||||
|
|
||||||
return standardizedID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setFeaturedApps(_ featuredApps: [StoreApp]?)
|
func setFeaturedApps(_ featuredApps: [StoreApp]?)
|
||||||
|
|||||||
Reference in New Issue
Block a user