Skip to content

Instantly share code, notes, and snippets.

@PimCoumans
Last active October 4, 2022 05:33
Show Gist options
  • Save PimCoumans/1f605b96e6598837c842e08408fb0028 to your computer and use it in GitHub Desktop.
Save PimCoumans/1f605b96e6598837c842e08408fb0028 to your computer and use it in GitHub Desktop.
Finding your external Synology URL though the undocumented QuickConnect API
import Foundation
// Based on example by Thomas Theunen: https://thomastheunen.eu/2017/07/04/diving-into-synology-quickconnect-and-creating-a-javascript-library/
struct QuickConnect {
private static let endpointURL = URL(string: "https://global.quickconnect.to/Serv.php")!
enum Error: Swift.Error {
case noAddressFound
case noResponse
case invalidData(decodingError: Swift.Error, jsonString: String)
}
private struct IDRequestParameters: Encodable {
let version = 1
let command = "get_server_info"
let stop_when_error = false
let stop_when_success = false
let id: String
let serverID: String
}
private struct ServerResponse: Decodable {
struct Service: Decodable {
let pingpong: String
let pingpongDesc: [String]
let port: Int
}
let service: Service
func url(https: Bool) -> URL? {
guard let ipAddress = service.pingpongDesc.first, service.pingpong == "CONNECTED" else {
return nil
}
return URL(string: "\(https ? "https" : "http")://\(ipAddress)")
}
}
private static let encoder: JSONEncoder = {
let encoder = JSONEncoder()
return encoder
}()
private static let decoder: JSONDecoder = {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
return decoder
}()
static func externalAddress(for quickConnectID: String, handler: @escaping (Result<URL>) -> Swift.Void) {
let parameters = [
IDRequestParameters(id: "dsm_portal_https", serverID: quickConnectID),
IDRequestParameters(id: "dsm_portal", serverID: quickConnectID)
]
var request = URLRequest(url: endpointURL)
request.httpMethod = "POST"
request.httpBody = try? encoder.encode(parameters)
URLSession.shared.dataTask(with: request) { (data, response, error) in
do {
guard let data = data else {
throw error ?? Error.noResponse
}
let response: [ServerResponse]
do {
response = try decoder.decode([ServerResponse].self, from: data)
} catch {
let jsonString = String(data: data, encoding: .utf8)!
throw Error.invalidData(decodingError: error, jsonString: jsonString)
}
if let address = response.first?.url(https: true) {
DispatchQueue.main.async {
handler(.success(address))
}
} else if let address = response.last?.url(https: false) {
DispatchQueue.main.async {
handler(.success(address))
}
} else {
throw Error.noAddressFound
}
}
catch {
DispatchQueue.main.async {
handler(.failure(error))
}
}
}.resume()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment