import Foundation /// Which ipx-web the app shows. Asked for on first launch; shaking the phone asks again. /// `-ipx.server ` on the launch arguments overrides it for that run (UserDefaults' argument /// domain), which is how a test points the app somewhere else. enum Server { private static let key = "ipx.server" static let suggested = "https://ipodderx.sdf1.net" static var url: URL? { get { UserDefaults.standard.string(forKey: key).flatMap(normalise) } set { UserDefaults.standard.set(newValue?.absoluteString, forKey: key) } } /// What someone typed, as an address: a bare host gets https. Plain http is allowed only on /// your own network, which is all NSAllowsLocalNetworking lets through, so it is refused here /// rather than failing later and looking like the server is down. static func normalise(_ text: String) -> URL? { var s = text.trimmingCharacters(in: .whitespacesAndNewlines) if s.isEmpty { return nil } if !s.contains("://") { s = "https://" + s } guard let u = URL(string: s), let host = u.host, !host.isEmpty else { return nil } switch u.scheme { case "https": return u case "http": return isLocal(host) ? u : nil default: return nil } } static func isLocal(_ host: String) -> Bool { if host == "localhost" || host.hasSuffix(".local") { return true } let p = host.split(separator: ".").compactMap { Int($0) } guard p.count == 4 else { return false } return p[0] == 10 || p[0] == 127 || (p[0] == 192 && p[1] == 168) || (p[0] == 172 && (16...31).contains(p[1])) } /// A path the page sent (`/media/42`), against the server. static func resolve(_ path: String) -> URL? { guard let base = url else { return nil } return URL(string: path, relativeTo: base)?.absoluteURL } }