2024-03-20 17:21:16 +04:00

1037 lines
53 KiB
Swift

import Foundation
import Display
import SafariServices
import TelegramCore
import Postbox
import SwiftSignalKit
import MtProtoKit
import TelegramPresentationData
import TelegramUIPreferences
import AccountContext
import UrlEscaping
import PassportUI
import UrlHandling
import OpenInExternalAppUI
import BrowserUI
import OverlayStatusController
import PresentationDataUtils
public struct ParsedSecureIdUrl {
public let peerId: PeerId
public let scope: String
public let publicKey: String
public let callbackUrl: String
public let opaquePayload: Data
public let opaqueNonce: Data
}
public func parseProxyUrl(_ url: URL) -> ProxyServerSettings? {
guard let proxy = parseProxyUrl(url.absoluteString) else {
return nil
}
if let secret = proxy.secret, let _ = MTProxySecret.parseData(secret) {
return ProxyServerSettings(host: proxy.host, port: proxy.port, connection: .mtp(secret: secret))
} else {
return ProxyServerSettings(host: proxy.host, port: proxy.port, connection: .socks5(username: proxy.username, password: proxy.password))
}
}
public func parseSecureIdUrl(_ url: URL) -> ParsedSecureIdUrl? {
guard let query = url.query else {
return nil
}
if url.host == "passport" || url.host == "resolve" {
if let components = URLComponents(string: "/?" + query) {
var domain: String?
var botId: Int64?
var scope: String?
var publicKey: String?
var callbackUrl: String?
var opaquePayload = Data()
var opaqueNonce = Data()
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "domain" {
domain = value
} else if queryItem.name == "bot_id" {
botId = Int64(value)
} else if queryItem.name == "scope" {
scope = value
} else if queryItem.name == "public_key" {
publicKey = value
} else if queryItem.name == "callback_url" {
callbackUrl = value
} else if queryItem.name == "payload" {
if let data = value.data(using: .utf8) {
opaquePayload = data
}
} else if queryItem.name == "nonce" {
if let data = value.data(using: .utf8) {
opaqueNonce = data
}
}
}
}
}
let valid: Bool
if url.host == "resolve" {
if domain == "telegrampassport" {
valid = true
} else {
valid = false
}
} else {
valid = true
}
if valid {
if let botId = botId, let scope = scope, let publicKey = publicKey, let callbackUrl = callbackUrl {
if scope.hasPrefix("{") && scope.hasSuffix("}") {
opaquePayload = Data()
if opaqueNonce.isEmpty {
return nil
}
} else if opaquePayload.isEmpty {
return nil
}
return ParsedSecureIdUrl(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(botId)), scope: scope, publicKey: publicKey, callbackUrl: callbackUrl, opaquePayload: opaquePayload, opaqueNonce: opaqueNonce)
}
}
}
}
return nil
}
public func parseConfirmationCodeUrl(_ url: URL) -> Int? {
if url.pathComponents.count == 3 && url.pathComponents[1].lowercased() == "login" {
if let code = Int(url.pathComponents[2]) {
return code
}
}
if url.scheme == "tg" {
if let host = url.host, let query = url.query, let parsedUrl = parseInternalUrl(query: host + "?" + query) {
switch parsedUrl {
case let .confirmationCode(code):
return code
default:
break
}
}
}
return nil
}
func formattedConfirmationCode(_ code: Int) -> String {
let source = "\(code)"
let segmentLength = 3
var result = ""
for c in source {
if !result.isEmpty && result.count % segmentLength == 0 {
result.append("-")
}
result.append(c)
}
return result
}
func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, url: String, forceExternal: Bool, presentationData: PresentationData, navigationController: NavigationController?, dismissInput: @escaping () -> Void) {
if forceExternal || url.lowercased().hasPrefix("tel:") || url.lowercased().hasPrefix("calshow:") {
if url.lowercased().hasPrefix("tel:+888") {
context.sharedContext.presentGlobalController(textAlertController(context: context, title: nil, text: presentationData.strings.Conversation_CantPhoneCallAnonymousNumberError, actions: [
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
}),
], parseMarkdown: true), nil)
return
}
context.sharedContext.applicationBindings.openUrl(url)
return
}
var parsedUrlValue: URL?
var urlWithScheme = url
if !url.contains("://") && !url.hasPrefix("mailto:") {
urlWithScheme = "http://" + url
}
if let parsed = URL(string: urlWithScheme) {
parsedUrlValue = parsed
} else if let encoded = (urlWithScheme as NSString).addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed), let parsed = URL(string: encoded) {
parsedUrlValue = parsed
}
if let parsedUrlValue = parsedUrlValue, parsedUrlValue.scheme == "mailto" {
context.sharedContext.applicationBindings.openUrl(url)
return
}
guard let parsedUrl = parsedUrlValue else {
return
}
if let host = parsedUrl.host?.lowercased() {
if host == "itunes.apple.com" {
if context.sharedContext.applicationBindings.canOpenUrl(parsedUrl.absoluteString) {
context.sharedContext.applicationBindings.openUrl(url)
return
}
}
if host == "twitter.com" || host == "mobile.twitter.com" {
if context.sharedContext.applicationBindings.canOpenUrl("twitter://status") {
context.sharedContext.applicationBindings.openUrl(url)
return
}
} else if host == "instagram.com" {
if context.sharedContext.applicationBindings.canOpenUrl("instagram://photo") {
context.sharedContext.applicationBindings.openUrl(url)
return
}
}
}
let continueHandling: () -> Void = {
let handleResolvedUrl: (ResolvedUrl) -> Void = { resolved in
if case let .externalUrl(value) = resolved {
context.sharedContext.applicationBindings.openUrl(value)
} else {
context.sharedContext.openResolvedUrl(resolved, context: context, urlContext: .generic, navigationController: navigationController, forceExternal: false, openPeer: { peer, navigation in
switch navigation {
case .info:
if let infoController = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) {
context.sharedContext.applicationBindings.dismissNativeController()
navigationController?.pushViewController(infoController)
}
case let .chat(textInputState, subject, peekData):
context.sharedContext.applicationBindings.dismissNativeController()
if let navigationController = navigationController {
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer), subject: subject, updateTextInputState: !peer.id.isGroupOrChannel ? textInputState : nil, peekData: peekData))
}
case let .withBotStartPayload(payload):
context.sharedContext.applicationBindings.dismissNativeController()
if let navigationController = navigationController {
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer), botStart: payload))
}
case let .withAttachBot(attachBotStart):
context.sharedContext.applicationBindings.dismissNativeController()
if let navigationController = navigationController {
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer), attachBotStart: attachBotStart))
}
case let .withBotApp(botAppStart):
context.sharedContext.applicationBindings.dismissNativeController()
if let navigationController = navigationController {
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer), botAppStart: botAppStart))
}
default:
break
}
}, sendFile: nil,
sendSticker: nil,
requestMessageActionUrlAuth: nil,
joinVoiceChat: { peerId, invite, call in
}, present: { c, a in
context.sharedContext.applicationBindings.dismissNativeController()
c.presentationArguments = a
context.sharedContext.applicationBindings.getWindowHost()?.present(c, on: .root, blockInteraction: false, completion: {})
}, dismissInput: {
dismissInput()
}, contentContext: nil, progress: nil, completion: nil)
}
}
let handleInternalUrl: (String) -> Void = { url in
let _ = (context.sharedContext.resolveUrl(context: context, peerId: nil, url: url, skipUrlAuth: true)
|> deliverOnMainQueue).startStandalone(next: handleResolvedUrl)
}
if let scheme = parsedUrl.scheme, (scheme == "tg" || scheme == context.sharedContext.applicationBindings.appSpecificScheme) {
var convertedUrl: String?
if let query = parsedUrl.query {
if parsedUrl.host == "localpeer" {
if let components = URLComponents(string: "/?" + query) {
var peerId: PeerId?
var accountId: Int64?
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "id", let intValue = Int64(value) {
peerId = PeerId(intValue)
} else if queryItem.name == "accountId", let intValue = Int64(value) {
accountId = intValue
}
}
}
}
if let peerId = peerId, let accountId = accountId {
context.sharedContext.applicationBindings.dismissNativeController()
context.sharedContext.navigateToChat(accountId: AccountRecordId(rawValue: accountId), peerId: peerId, messageId: nil)
}
}
} else if parsedUrl.host == "join" {
if let components = URLComponents(string: "/?" + query) {
var invite: String?
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "invite" {
invite = value
}
}
}
}
if let invite = invite {
convertedUrl = "https://t.me/joinchat/\(invite)"
}
}
} else if parsedUrl.host == "addstickers" {
if let components = URLComponents(string: "/?" + query) {
var set: String?
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "set" {
set = value
}
}
}
}
if let set = set {
convertedUrl = "https://t.me/addstickers/\(set)"
}
}
} else if parsedUrl.host == "addemoji" {
if let components = URLComponents(string: "/?" + query) {
var set: String?
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "set" {
set = value
}
}
}
}
if let set = set {
convertedUrl = "https://t.me/addemoji/\(set)"
}
}
} else if parsedUrl.host == "invoice" {
if let components = URLComponents(string: "/?" + query) {
var slug: String?
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "slug" {
slug = value
}
}
}
}
if let slug = slug {
convertedUrl = "https://t.me/invoice/\(slug)"
}
}
} else if parsedUrl.host == "setlanguage" {
if let components = URLComponents(string: "/?" + query) {
var lang: String?
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "lang" {
lang = value
}
}
}
}
if let lang = lang {
convertedUrl = "https://t.me/setlanguage/\(lang)"
}
}
} else if parsedUrl.host == "msg" {
if let components = URLComponents(string: "/?" + query) {
var sharePhoneNumber: String?
var shareText: String?
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "to" {
sharePhoneNumber = value
} else if queryItem.name == "text" {
shareText = value
}
}
}
}
if sharePhoneNumber != nil || shareText != nil {
handleResolvedUrl(.share(url: nil, text: shareText, to: sharePhoneNumber))
return
}
}
} else if parsedUrl.host == "msg_url" {
if let components = URLComponents(string: "/?" + query) {
var shareUrl: String?
var shareText: String?
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "url" {
shareUrl = value
} else if queryItem.name == "text" {
shareText = value
}
}
}
}
if let shareUrl = shareUrl {
var resultUrl = "https://t.me/share/url?url=\(urlEncodedStringFromString(shareUrl))"
if let shareText = shareText {
resultUrl += "&text=\(urlEncodedStringFromString(shareText))"
}
convertedUrl = resultUrl
}
}
} else if parsedUrl.host == "socks" || parsedUrl.host == "proxy" {
if let components = URLComponents(string: "/?" + query) {
var server: String?
var port: String?
var user: String?
var pass: String?
var secret: String?
var secretHost: String?
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "server" || queryItem.name == "proxy" {
server = value
} else if queryItem.name == "port" {
port = value
} else if queryItem.name == "user" {
user = value
} else if queryItem.name == "pass" {
pass = value
} else if queryItem.name == "secret" {
secret = value
} else if queryItem.name == "host" {
secretHost = value
}
}
}
}
if let server = server, !server.isEmpty, let port = port, let _ = Int32(port) {
var result = "https://t.me/proxy?proxy=\(server)&port=\(port)"
if let user = user {
result += "&user=\((user as NSString).addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryValueAllowed) ?? "")"
if let pass = pass {
result += "&pass=\((pass as NSString).addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryValueAllowed) ?? "")"
}
}
if let secret = secret {
result += "&secret=\((secret as NSString).addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryValueAllowed) ?? "")"
}
if let secretHost = secretHost?.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryValueAllowed) {
result += "&host=\(secretHost)"
}
convertedUrl = result
}
}
} else if parsedUrl.host == "passport" || parsedUrl.host == "resolve" {
if let components = URLComponents(string: "/?" + query) {
var domain: String?
var botId: Int64?
var scope: String?
var publicKey: String?
var callbackUrl: String?
var opaquePayload = Data()
var opaqueNonce = Data()
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "domain" {
domain = value
} else if queryItem.name == "bot_id" {
botId = Int64(value)
} else if queryItem.name == "scope" {
scope = value
} else if queryItem.name == "public_key" {
publicKey = value
} else if queryItem.name == "callback_url" {
callbackUrl = value
} else if queryItem.name == "payload" {
if let data = value.data(using: .utf8) {
opaquePayload = data
}
} else if queryItem.name == "nonce" {
if let data = value.data(using: .utf8) {
opaqueNonce = data
}
}
}
}
}
let valid: Bool
if parsedUrl.host == "resolve" {
if domain == "telegrampassport" {
valid = true
} else {
valid = false
}
} else {
valid = true
}
if valid {
if let botId = botId, let scope = scope, let publicKey = publicKey {
if scope.hasPrefix("{") && scope.hasSuffix("}") {
opaquePayload = Data()
if opaqueNonce.isEmpty {
return
}
} else if opaquePayload.isEmpty {
return
}
if case .chat = urlContext {
return
}
let controller = SecureIdAuthController(context: context, mode: .form(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(botId)), scope: scope, publicKey: publicKey, callbackUrl: callbackUrl, opaquePayload: opaquePayload, opaqueNonce: opaqueNonce))
if let navigationController = navigationController {
context.sharedContext.applicationBindings.dismissNativeController()
navigationController.view.window?.endEditing(true)
context.sharedContext.applicationBindings.getWindowHost()?.present(controller, on: .root, blockInteraction: false, completion: {})
}
}
return
}
}
} else if parsedUrl.host == "user" {
if let components = URLComponents(string: "/?" + query) {
var id: String?
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "id" {
id = value
}
}
}
}
if let id = id, !id.isEmpty, let idValue = Int64(id), idValue > 0 {
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(idValue))))
|> deliverOnMainQueue).startStandalone(next: { peer in
if let peer = peer, let controller = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) {
navigationController?.pushViewController(controller)
}
})
return
}
}
} else if parsedUrl.host == "login" {
if let components = URLComponents(string: "/?" + query) {
var code: String?
var isToken: Bool = false
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "code" {
code = value
}
}
if queryItem.name == "token" {
isToken = true
}
}
}
if isToken {
context.sharedContext.presentGlobalController(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.AuthSessions_AddDevice_UrlLoginHint, actions: [
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
}),
], parseMarkdown: true), nil)
return
}
if let code = code {
convertedUrl = "https://t.me/login/\(code)"
}
}
} else if parsedUrl.host == "contact" {
if let components = URLComponents(string: "/?" + query) {
var token: String?
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "token" {
token = value
}
}
}
}
if let token = token {
convertedUrl = "https://t.me/contact/\(token)"
}
}
} else if parsedUrl.host == "confirmphone" {
if let components = URLComponents(string: "/?" + query) {
var phone: String?
var hash: String?
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "phone" {
phone = value
} else if queryItem.name == "hash" {
hash = value
}
}
}
}
if let phone = phone, let hash = hash {
convertedUrl = "https://t.me/confirmphone?phone=\(phone)&hash=\(hash)"
}
}
} else if parsedUrl.host == "bg" {
if let components = URLComponents(string: "/?" + query) {
var parameter: String?
var query: [String] = []
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "slug" {
parameter = value
} else if queryItem.name == "color" {
parameter = value
} else if queryItem.name == "gradient" {
parameter = value
} else if queryItem.name == "mode" {
query.append("mode=\(value)")
} else if queryItem.name == "bg_color" {
query.append("bg_color=\(value)")
} else if queryItem.name == "intensity" {
query.append("intensity=\(value)")
} else if queryItem.name == "rotation" {
query.append("rotation=\(value)")
}
}
}
}
var queryString = ""
if !query.isEmpty {
queryString = "?\(query.joined(separator: "&"))"
}
if let parameter = parameter {
convertedUrl = "https://t.me/bg/\(parameter)\(queryString)"
}
}
} else if parsedUrl.host == "addtheme" {
if let components = URLComponents(string: "/?" + query) {
var parameter: String?
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "slug" {
parameter = value
}
}
}
}
if let parameter = parameter {
convertedUrl = "https://t.me/addtheme/\(parameter)"
}
}
} else if parsedUrl.host == "privatepost" {
if let components = URLComponents(string: "/?" + query) {
var channelId: Int64?
var postId: Int32?
var threadId: Int64?
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "channel" {
channelId = Int64(value)
} else if queryItem.name == "post" {
postId = Int32(value)
} else if queryItem.name == "thread" {
threadId = Int64(value)
}
}
}
}
if let channelId = channelId {
if let postId = postId {
if let threadId = threadId {
convertedUrl = "https://t.me/c/\(channelId)/\(threadId)/\(postId)"
} else {
convertedUrl = "https://t.me/c/\(channelId)/\(postId)"
}
} else if let threadId = threadId {
convertedUrl = "https://t.me/c/\(channelId)/\(threadId)"
}
}
}
}
if parsedUrl.host == "resolve" {
if let components = URLComponents(string: "/?" + query) {
var phone: String?
var domain: String?
var start: String?
var startGroup: String?
var startChannel: String?
var admin: String?
var game: String?
var post: String?
var voiceChat: String?
var attach: String?
var startAttach: String?
var choose: String?
var threadId: Int64?
var appName: String?
var startApp: String?
var text: String?
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "phone" {
phone = value
} else if queryItem.name == "domain" {
domain = value
} else if queryItem.name == "start" {
start = value
} else if queryItem.name == "startgroup" {
startGroup = value
} else if queryItem.name == "admin" {
admin = value
} else if queryItem.name == "game" {
game = value
} else if queryItem.name == "post" {
post = value
} else if ["voicechat", "videochat", "livestream"].contains(queryItem.name) {
voiceChat = value
} else if queryItem.name == "attach" {
attach = value
} else if queryItem.name == "startattach" {
startAttach = value
} else if queryItem.name == "choose" {
choose = value
} else if queryItem.name == "thread" {
threadId = Int64(value)
} else if queryItem.name == "appname" {
appName = value
} else if queryItem.name == "startapp" {
startApp = value
} else if queryItem.name == "text" {
text = value
}
} else if ["voicechat", "videochat", "livestream"].contains(queryItem.name) {
voiceChat = ""
} else if queryItem.name == "startattach" {
startAttach = ""
} else if queryItem.name == "startgroup" {
startGroup = ""
} else if queryItem.name == "startchannel" {
startChannel = ""
}
}
}
if let phone = phone {
var result = "https://t.me/+\(phone)"
if let text = text {
result += "?text=\(text)"
}
convertedUrl = result
} else if let domain = domain {
var result = "https://t.me/\(domain)"
if let appName {
result += "/\(appName)"
}
if let startApp {
result += "?startapp=\(startApp)"
}
if let threadId {
result += "/\(threadId)"
if let post, let postValue = Int(post) {
result += "/\(postValue)"
}
} else {
if let post, let postValue = Int(post) {
result += "/\(postValue)"
}
}
if let start = start {
result += "?start=\(start)"
} else if let startGroup = startGroup {
if !startGroup.isEmpty {
result += "?startgroup=\(startGroup)"
} else {
result += "?startgroup"
}
if let admin = admin {
result += "&admin=\(admin)"
}
} else if let startChannel = startChannel {
if !startChannel.isEmpty {
result += "?startchannel=\(startChannel)"
} else {
result += "?startchannel"
}
if let admin = admin {
result += "&admin=\(admin)"
}
} else if let game = game {
result += "?game=\(game)"
} else if let voiceChat = voiceChat {
if !voiceChat.isEmpty {
result += "?voicechat=\(voiceChat)"
} else {
result += "?voicechat="
}
} else if let attach = attach {
result += "?attach=\(attach)"
}
if let startAttach = startAttach {
if attach == nil {
result += "?"
} else {
result += "&"
}
if !startAttach.isEmpty {
result += "startattach=\(startAttach)"
} else {
result += "startattach"
}
if let choose = choose {
result += "&choose=\(choose)"
}
}
if let text = text {
result += "?text=\(text)"
}
convertedUrl = result
}
}
} else if parsedUrl.host == "hostOverride" {
if let components = URLComponents(string: "/?" + query) {
var host: String?
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "host" {
host = value
}
}
}
}
if let host = host {
let _ = updateNetworkSettingsInteractively(postbox: context.account.postbox, network: context.account.network, { settings in
var settings = settings
settings.backupHostOverride = host
return settings
}).startStandalone()
return
}
}
} else if parsedUrl.host == "premium_offer" {
var reference: String?
if let components = URLComponents(string: "/?" + query) {
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "ref" {
reference = value
}
}
}
}
}
handleResolvedUrl(.premiumOffer(reference: reference))
} else if parsedUrl.host == "premium_multigift" {
var reference: String?
if let components = URLComponents(string: "/?" + query) {
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "ref" {
reference = value
}
}
}
}
}
handleResolvedUrl(.premiumMultiGift(reference: reference))
} else if parsedUrl.host == "addlist" {
if let components = URLComponents(string: "/?" + query) {
var slug: String?
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "slug" {
slug = value
}
}
}
}
if let slug = slug {
convertedUrl = "https://t.me/addlist/\(slug)"
}
}
} else if parsedUrl.host == "boost" {
if let components = URLComponents(string: "/?" + query) {
var domain: String?
var channel: Int64?
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "domain" {
domain = value
} else if queryItem.name == "channel" {
channel = Int64(value)
}
}
}
}
if let domain {
convertedUrl = "https://t.me/\(domain)?boost"
} else if let channel {
convertedUrl = "https://t.me/c/\(channel)?boost"
}
}
}
} else {
if parsedUrl.host == "importStickers" {
handleResolvedUrl(.importStickers)
} else if parsedUrl.host == "settings" {
if let path = parsedUrl.pathComponents.last {
var section: ResolvedUrlSettingsSection?
switch path {
case "themes":
section = .theme
case "devices":
section = .devices
case "password":
section = .twoStepAuth
case "enable_log":
section = .enableLog
default:
break
}
if let section = section {
handleResolvedUrl(.settings(section))
}
}
} else if parsedUrl.host == "premium_offer" {
handleResolvedUrl(.premiumOffer(reference: nil))
} else if parsedUrl.host == "restore_purchases" {
let statusController = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
context.sharedContext.presentGlobalController(statusController, nil)
context.inAppPurchaseManager?.restorePurchases(completion: { [weak statusController] result in
statusController?.dismiss()
let text: String?
switch result {
case let .succeed(serverProvided):
text = serverProvided ? nil : presentationData.strings.Premium_Restore_Success
case .failed:
text = presentationData.strings.Premium_Restore_ErrorUnknown
}
if let text = text {
let alertController = textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
context.sharedContext.presentGlobalController(alertController, nil)
}
})
}
}
if let convertedUrl = convertedUrl {
handleInternalUrl(convertedUrl)
}
return
}
if parsedUrl.scheme == "http" || parsedUrl.scheme == "https" {
if parsedUrl.host == "t.me" || parsedUrl.host == "telegram.me" {
handleInternalUrl(parsedUrl.absoluteString)
} else {
let settings = combineLatest(context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.webBrowserSettings, ApplicationSpecificSharedDataKeys.presentationPasscodeSettings]), context.sharedContext.accountManager.accessChallengeData())
|> take(1)
|> map { sharedData, accessChallengeData -> WebBrowserSettings in
let passcodeSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.presentationPasscodeSettings]?.get(PresentationPasscodeSettings.self) ?? PresentationPasscodeSettings.defaultSettings
var settings: WebBrowserSettings
if let current = sharedData.entries[ApplicationSpecificSharedDataKeys.webBrowserSettings]?.get(WebBrowserSettings.self) {
settings = current
} else {
settings = .defaultSettings
}
if accessChallengeData.data.isLockable {
if passcodeSettings.autolockTimeout != nil && settings.defaultWebBrowser == nil {
settings = WebBrowserSettings(defaultWebBrowser: "safari")
}
}
return settings
}
var isCompact = false
if let metrics = navigationController?.validLayout?.metrics, case .compact = metrics.widthClass {
isCompact = true
}
let _ = (settings
|> deliverOnMainQueue).startStandalone(next: { settings in
if settings.defaultWebBrowser == nil {
if !"".isEmpty && isCompact {
let controller = BrowserScreen(context: context, subject: .webPage(url: parsedUrl.absoluteString))
navigationController?.pushViewController(controller)
} else {
if let window = navigationController?.view.window {
let controller = SFSafariViewController(url: parsedUrl)
controller.preferredBarTintColor = presentationData.theme.rootController.navigationBar.opaqueBackgroundColor
controller.preferredControlTintColor = presentationData.theme.rootController.navigationBar.accentTextColor
window.rootViewController?.present(controller, animated: true)
} else {
context.sharedContext.applicationBindings.openUrl(parsedUrl.absoluteString)
}
}
} else {
let openInOptions = availableOpenInOptions(context: context, item: .url(url: url))
if let option = openInOptions.first(where: { $0.identifier == settings.defaultWebBrowser }) {
if case let .openUrl(url) = option.action() {
context.sharedContext.applicationBindings.openUrl(url)
} else {
context.sharedContext.applicationBindings.openUrl(url)
}
} else {
context.sharedContext.applicationBindings.openUrl(url)
}
}
})
}
} else {
context.sharedContext.applicationBindings.openUrl(url)
}
}
if parsedUrl.scheme == "http" || parsedUrl.scheme == "https" {
let nativeHosts = ["t.me", "telegram.me"]
if let host = parsedUrl.host, nativeHosts.contains(host) {
continueHandling()
} else {
context.sharedContext.applicationBindings.openUniversalUrl(url, TelegramApplicationOpenUrlCompletion(completion: { success in
if !success {
continueHandling()
}
}))
}
} else {
continueHandling()
}
}