-
Notifications
You must be signed in to change notification settings - Fork 2.9k
/
UserAgent.swift
128 lines (99 loc) · 5.35 KB
/
UserAgent.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import AVFoundation
import UIKit
open class UserAgent {
private static var defaults = UserDefaults(suiteName: AppInfo.sharedContainerIdentifier)!
private static func clientUserAgent(prefix: String) -> String {
return "\(prefix)/\(AppInfo.appVersion)b\(AppInfo.buildNumber) " +
"(\(DeviceInfo.deviceModel()); iPhone OS \(UIDevice.current.systemVersion)) (\(DeviceInfo.appName()))"
}
open static var syncUserAgent: String {
return clientUserAgent(prefix: "Firefox-iOS-Sync")
}
open static var tokenServerClientUserAgent: String {
return clientUserAgent(prefix: "Firefox-iOS-Token")
}
open static var fxaUserAgent: String {
return clientUserAgent(prefix: "Firefox-iOS-FxA")
}
/**
* Use this if you know that a value must have been computed before your
* code runs, or you don't mind failure.
*/
open static func cachedUserAgent(checkiOSVersion: Bool = true,
checkFirefoxVersion: Bool = true,
checkFirefoxBuildNumber: Bool = true) -> String? {
let currentiOSVersion = UIDevice.current.systemVersion
let lastiOSVersion = defaults.string(forKey: "LastDeviceSystemVersionNumber")
let currentFirefoxBuildNumber = AppInfo.buildNumber
let currentFirefoxVersion = AppInfo.appVersion
let lastFirefoxVersion = defaults.string(forKey: "LastFirefoxVersionNumber")
let lastFirefoxBuildNumber = defaults.string(forKey: "LastFirefoxBuildNumber")
if let firefoxUA = defaults.string(forKey: "UserAgent") {
if (!checkiOSVersion || (lastiOSVersion == currentiOSVersion))
&& (!checkFirefoxVersion || (lastFirefoxVersion == currentFirefoxVersion)
&& (!checkFirefoxBuildNumber || (lastFirefoxBuildNumber == currentFirefoxBuildNumber))) {
return firefoxUA
}
}
return nil
}
/**
* This will typically return quickly, but can require creation of a UIWebView.
* As a result, it must be called on the UI thread.
*/
open static func defaultUserAgent() -> String {
assert(Thread.current.isMainThread, "This method must be called on the main thread.")
if let firefoxUA = UserAgent.cachedUserAgent(checkiOSVersion: true) {
return firefoxUA
}
let webView = UIWebView()
let appVersion = AppInfo.appVersion
let buildNumber = AppInfo.buildNumber
let currentiOSVersion = UIDevice.current.systemVersion
defaults.set(currentiOSVersion, forKey: "LastDeviceSystemVersionNumber")
defaults.set(appVersion, forKey: "LastFirefoxVersionNumber")
defaults.set(buildNumber, forKey: "LastFirefoxBuildNumber")
let userAgent = webView.stringByEvaluatingJavaScript(from: "navigator.userAgent")!
// Extract the WebKit version and use it as the Safari version.
let webKitVersionRegex = try! NSRegularExpression(pattern: "AppleWebKit/([^ ]+) ", options: [])
let match = webKitVersionRegex.firstMatch(in: userAgent, options:[],
range: NSRange(location: 0, length: userAgent.characters.count))
if match == nil {
print("Error: Unable to determine WebKit version in UA.")
return userAgent // Fall back to Safari's.
}
let webKitVersion = (userAgent as NSString).substring(with: match!.rangeAt(1))
// Insert "FxiOS/<version>" before the Mobile/ section.
let mobileRange = (userAgent as NSString).range(of: "Mobile/")
if mobileRange.location == NSNotFound {
print("Error: Unable to find Mobile section in UA.")
return userAgent // Fall back to Safari's.
}
let mutableUA = NSMutableString(string: userAgent)
mutableUA.insert("FxiOS/\(appVersion)b\(AppInfo.buildNumber) ", at: mobileRange.location)
let firefoxUA = "\(mutableUA) Safari/\(webKitVersion)"
defaults.set(firefoxUA, forKey: "UserAgent")
return firefoxUA
}
open static func desktopUserAgent() -> String {
let userAgent = NSMutableString(string: defaultUserAgent())
// Spoof platform section
let platformRegex = try! NSRegularExpression(pattern: "\\([^\\)]+\\)", options: [])
guard let platformMatch = platformRegex.firstMatch(in: userAgent as String, options:[], range: NSRange(location: 0, length: userAgent.length)) else {
print("Error: Unable to determine platform in UA.")
return String(userAgent)
}
userAgent.replaceCharacters(in: platformMatch.range, with: "(Macintosh; Intel Mac OS X 10_11_1)")
// Strip mobile section
let mobileRegex = try! NSRegularExpression(pattern: " FxiOS/[^ ]+ Mobile/[^ ]+", options: [])
guard let mobileMatch = mobileRegex.firstMatch(in: userAgent as String, options:[], range: NSRange(location: 0, length: userAgent.length)) else {
print("Error: Unable to find Mobile section in UA.")
return String(userAgent)
}
userAgent.replaceCharacters(in: mobileMatch.range, with: "")
return String(userAgent)
}
}