summaryrefslogtreecommitdiffstats
path: root/Godeps/_workspace/src/github.com/mssola/user_agent
diff options
context:
space:
mode:
Diffstat (limited to 'Godeps/_workspace/src/github.com/mssola/user_agent')
-rw-r--r--Godeps/_workspace/src/github.com/mssola/user_agent/.travis.yml11
-rw-r--r--Godeps/_workspace/src/github.com/mssola/user_agent/LICENSE20
-rw-r--r--Godeps/_workspace/src/github.com/mssola/user_agent/all_test.go257
-rw-r--r--Godeps/_workspace/src/github.com/mssola/user_agent/bot.go121
-rw-r--r--Godeps/_workspace/src/github.com/mssola/user_agent/browser.go120
-rw-r--r--Godeps/_workspace/src/github.com/mssola/user_agent/operating_systems.go260
-rw-r--r--Godeps/_workspace/src/github.com/mssola/user_agent/user_agent.go169
7 files changed, 958 insertions, 0 deletions
diff --git a/Godeps/_workspace/src/github.com/mssola/user_agent/.travis.yml b/Godeps/_workspace/src/github.com/mssola/user_agent/.travis.yml
new file mode 100644
index 000000000..922091263
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/mssola/user_agent/.travis.yml
@@ -0,0 +1,11 @@
+language: go
+go:
+ - 1.0
+ - 1.1
+ - 1.2
+ - 1.3
+ - 1.4
+ - tip
+matrix:
+ allow_failures:
+ - go: tip
diff --git a/Godeps/_workspace/src/github.com/mssola/user_agent/LICENSE b/Godeps/_workspace/src/github.com/mssola/user_agent/LICENSE
new file mode 100644
index 000000000..21d3935c5
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/mssola/user_agent/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2012-2015 Miquel Sabaté Solà
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Godeps/_workspace/src/github.com/mssola/user_agent/all_test.go b/Godeps/_workspace/src/github.com/mssola/user_agent/all_test.go
new file mode 100644
index 000000000..6dca19d5c
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/mssola/user_agent/all_test.go
@@ -0,0 +1,257 @@
+// Copyright (C) 2012-2015 Miquel Sabaté Solà <mikisabate@gmail.com>
+// This file is licensed under the MIT license.
+// See the LICENSE file.
+
+package user_agent
+
+import (
+ "fmt"
+ "testing"
+)
+
+// Slice that contains all the tests. Each test is contained in a struct
+// that groups the name of the test and the User-Agent string to be tested.
+var uastrings = []struct {
+ name string
+ ua string
+}{
+ // Bots
+ {"GoogleBot", "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"},
+ {"GoogleBotSmartphone", "Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"},
+ {"BingBot", "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)"},
+ {"BaiduBot", "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"},
+ {"Twitterbot", "Twitterbot"},
+ {"YahooBot", "Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)"},
+ {"FacebookExternalHit", "facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)"},
+ {"FacebookPlatform", "facebookplatform/1.0 (+http://developers.facebook.com)"},
+ {"FaceBot", "Facebot"},
+
+ // Internet Explorer
+ {"IE10", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)"},
+ {"Tablet", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.2; ARM; Trident/6.0; Touch; .NET4.0E; .NET4.0C; Tablet PC 2.0)"},
+ {"Touch", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; ARM; Trident/6.0; Touch)"},
+ {"Phone", "Mozilla/4.0 (compatible; MSIE 7.0; Windows Phone OS 7.0; Trident/3.1; IEMobile/7.0; SAMSUNG; SGH-i917)"},
+ {"IE6", "Mozilla/4.0 (compatible; MSIE6.0; Windows NT 5.0; .NET CLR 1.1.4322)"},
+ {"IE8Compatibility", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.3; MS-RTC LM 8)"},
+ {"IE10Compatibility", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.3; MS-RTC LM 8)"},
+ {"IE11Win81", "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko"},
+ {"IE11Win7", "Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko"},
+ {"IE11b32Win7b64", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"},
+ {"IE11b32Win7b64MDDRJS", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; MDDRJS; rv:11.0) like Gecko"},
+ {"IE11Compatibility", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.3; Trident/7.0)"},
+
+ // Gecko
+ {"FirefoxMac", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0b8) Gecko/20100101 Firefox/4.0b8"},
+ {"FirefoxMacLoc", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13"},
+ {"FirefoxLinux", "Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/20100101 Firefox/17.0"},
+ {"FirefoxWin", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.14"},
+ {"Firefox29Win7", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0"},
+ {"CaminoMac", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en; rv:1.8.1.14) Gecko/20080409 Camino/1.6 (like Firefox/2.0.0.14)"},
+ {"Iceweasel", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1) Gecko/20061024 Iceweasel/2.0 (Debian-2.0+dfsg-1)"},
+ {"SeaMonkey", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.1.4) Gecko/20091017 SeaMonkey/2.0"},
+ {"AndroidFirefox", "Mozilla/5.0 (Android; Mobile; rv:17.0) Gecko/17.0 Firefox/17.0"},
+ {"AndroidFirefoxTablet", "Mozilla/5.0 (Android; Tablet; rv:26.0) Gecko/26.0 Firefox/26.0"},
+ {"FirefoxOS", "Mozilla/5.0 (Mobile; rv:26.0) Gecko/26.0 Firefox/26.0"},
+ {"FirefoxOSTablet", "Mozilla/5.0 (Tablet; rv:26.0) Gecko/26.0 Firefox/26.0"},
+ {"FirefoxWinXP", "Mozilla/5.0 (Windows NT 5.2; rv:31.0) Gecko/20100101 Firefox/31.0"},
+ {"FirefoxMRA", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:24.0) Gecko/20130405 MRA 5.5 (build 02842) Firefox/24.0 (.NET CLR 3.5.30729)"},
+
+ // Opera
+ {"OperaMac", "Opera/9.27 (Macintosh; Intel Mac OS X; U; en)"},
+ {"OperaWin", "Opera/9.27 (Windows NT 5.1; U; en)"},
+ {"OperaWinNoLocale", "Opera/9.80 (Windows NT 5.1) Presto/2.12.388 Version/12.10"},
+ {"OperaWin2Comment", "Opera/9.80 (Windows NT 6.0; WOW64) Presto/2.12.388 Version/12.15"},
+ {"OperaMinimal", "Opera/9.80"},
+ {"OperaFull", "Opera/9.80 (Windows NT 6.0; U; en) Presto/2.2.15 Version/10.10"},
+ {"OperaLinux", "Opera/9.80 (X11; Linux x86_64) Presto/2.12.388 Version/12.10"},
+ {"OperaAndroid", "Opera/9.80 (Android 4.2.1; Linux; Opera Mobi/ADR-1212030829) Presto/2.11.355 Version/12.10"},
+ {"OperaNested", "Opera/9.80 (Windows NT 5.1; MRA 6.0 (build 5831)) Presto/2.12.388 Version/12.10"},
+ {"OperaMRA", "Opera/9.80 (Windows NT 6.1; U; MRA 5.8 (build 4139); en) Presto/2.9.168 Version/11.50"},
+
+ // Other
+ {"Empty", ""},
+ {"Nil", "nil"},
+ {"Compatible", "Mozilla/4.0 (compatible)"},
+ {"Mozilla", "Mozilla/5.0"},
+ {"Amaya", "amaya/9.51 libwww/5.4.0"},
+ {"Rails", "Rails Testing"},
+ {"Python", "Python-urllib/2.7"},
+ {"Curl", "curl/7.28.1"},
+
+ // WebKit
+ {"ChromeLinux", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11"},
+ {"ChromeWin7", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.168 Safari/535.19"},
+ {"ChromeMinimal", "Mozilla/5.0 AppleWebKit/534.10 Chrome/8.0.552.215 Safari/534.10"},
+ {"ChromeMac", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.231 Safari/534.10"},
+ {"SafariMac", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16"},
+ {"SafariWin", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en) AppleWebKit/526.9 (KHTML, like Gecko) Version/4.0dp1 Safari/526.8"},
+ {"iPhone7", "Mozilla/5.0 (iPhone; CPU iPhone OS 7_0_3 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11B511 Safari/9537.53"},
+ {"iPhone", "Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/4A102 Safari/419"},
+ {"iPod", "Mozilla/5.0 (iPod; U; CPU like Mac OS X; en) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/4A102 Safari/419"},
+ {"iPad", "Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B367 Safari/531.21.10"},
+ {"webOS", "Mozilla/5.0 (webOS/1.4.0; U; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Version/1.0 Safari/532.2 Pre/1.1"},
+ {"Android", "Mozilla/5.0 (Linux; U; Android 1.5; de-; HTC Magic Build/PLAT-RC33) AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1"},
+ {"BlackBerry", "Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en) AppleWebKit/534.1+ (KHTML, Like Gecko) Version/6.0.0.141 Mobile Safari/534.1+"},
+ {"BB10", "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.3+ (KHTML, like Gecko) Version/10.0.9.388 Mobile Safari/537.3+"},
+ {"Ericsson", "Mozilla/5.0 (SymbianOS/9.4; U; Series60/5.0 Profile/MIDP-2.1 Configuration/CLDC-1.1) AppleWebKit/525 (KHTML, like Gecko) Version/3.0 Safari/525"},
+ {"ChromeAndroid", "Mozilla/5.0 (Linux; Android 4.2.1; Galaxy Nexus Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19"},
+ {"WebkitNoPlatform", "Mozilla/5.0 (en-us) AppleWebKit/525.13 (KHTML, like Gecko; Google Web Preview) Version/3.1 Safari/525.13"},
+ {"OperaWebkitMobile", "Mozilla/5.0 (Linux; Android 4.2.2; Galaxy Nexus Build/JDQ39) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.58 Mobile Safari/537.31 OPR/14.0.1074.57453"},
+ {"OperaWebkitDesktop", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.58 Safari/537.31 OPR/14.0.1074.57453"},
+ {"ChromeNothingAfterU", "Mozilla/5.0 (Linux; U) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.79 Safari/537.4"},
+ {"SafariOnSymbian", "Mozilla/5.0 (SymbianOS/9.1; U; [en-us]) AppleWebKit/413 (KHTML, like Gecko) Safari/413"},
+}
+
+// Slice of the expected results from the previous slice.
+var expected = []string{
+ // Bots
+ "Mozilla:5.0 Browser:Googlebot-2.1 Bot:true Mobile:false",
+ "Mozilla:5.0 Browser:Googlebot-2.1 Bot:true Mobile:true",
+ "Mozilla:5.0 Browser:bingbot-2.0 Bot:true Mobile:false",
+ "Mozilla:5.0 Browser:Baiduspider-2.0 Bot:true Mobile:false",
+ "Browser:Twitterbot Bot:true Mobile:false",
+ "Mozilla:5.0 Browser:Yahoo! Slurp Bot:true Mobile:false",
+ "Browser:facebookexternalhit-1.1 Bot:true Mobile:false",
+ "Browser:facebookplatform-1.0 Bot:true Mobile:false",
+ "Browser:Facebot Bot:true Mobile:false",
+
+ // Internet Explorer
+ "Mozilla:5.0 Platform:Windows OS:Windows 8 Browser:Internet Explorer-10.0 Engine:Trident Bot:false Mobile:false",
+ "Mozilla:4.0 Platform:Windows OS:Windows 8 Browser:Internet Explorer-10.0 Engine:Trident Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Windows OS:Windows 8 Browser:Internet Explorer-10.0 Engine:Trident Bot:false Mobile:false",
+ "Mozilla:4.0 Platform:Windows OS:Windows Phone OS 7.0 Browser:Internet Explorer-7.0 Engine:Trident Bot:false Mobile:true",
+ "Mozilla:4.0 Platform:Windows OS:Windows 2000 Browser:Internet Explorer-6.0 Engine:Trident Bot:false Mobile:false",
+ "Mozilla:4.0 Platform:Windows OS:Windows 7 Browser:Internet Explorer-8.0 Engine:Trident Bot:false Mobile:false",
+ "Mozilla:4.0 Platform:Windows OS:Windows 7 Browser:Internet Explorer-10.0 Engine:Trident Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Windows OS:Windows 8.1 Browser:Internet Explorer-11.0 Engine:Trident Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Windows OS:Windows 7 Browser:Internet Explorer-11.0 Engine:Trident Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Windows OS:Windows 7 Browser:Internet Explorer-11.0 Engine:Trident Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Windows OS:Windows 7 Browser:Internet Explorer-11.0 Engine:Trident Bot:false Mobile:false",
+ "Mozilla:4.0 Platform:Windows OS:Windows 8.1 Browser:Internet Explorer-7.0 Engine:Trident Bot:false Mobile:false",
+
+ // Gecko
+ "Mozilla:5.0 Platform:Macintosh OS:Intel Mac OS X 10.6 Browser:Firefox-4.0b8 Engine:Gecko-20100101 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Macintosh OS:Intel Mac OS X 10.6 Localization:en-US Browser:Firefox-3.6.13 Engine:Gecko-20101203 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:X11 OS:Linux x86_64 Browser:Firefox-17.0 Engine:Gecko-20100101 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Windows OS:Windows XP Localization:en-US Browser:Firefox-2.0.0.14 Engine:Gecko-20080404 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Windows OS:Windows 7 Browser:Firefox-29.0 Engine:Gecko-20100101 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Macintosh OS:Intel Mac OS X Localization:en Browser:Camino-1.6 Engine:Gecko-20080409 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:X11 OS:Linux i686 Localization:en-US Browser:Iceweasel-2.0 Engine:Gecko-20061024 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Macintosh OS:Intel Mac OS X 10.6 Localization:en-US Browser:SeaMonkey-2.0 Engine:Gecko-20091017 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Mobile OS:Android Browser:Firefox-17.0 Engine:Gecko-17.0 Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:Tablet OS:Android Browser:Firefox-26.0 Engine:Gecko-26.0 Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:Mobile OS:FirefoxOS Browser:Firefox-26.0 Engine:Gecko-26.0 Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:Tablet OS:FirefoxOS Browser:Firefox-26.0 Engine:Gecko-26.0 Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:Windows OS:Windows XP x64 Edition Browser:Firefox-31.0 Engine:Gecko-20100101 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Windows OS:Windows XP Localization:en-US Browser:Firefox-24.0 Engine:Gecko-20130405 Bot:false Mobile:false",
+
+ // Opera
+ "Platform:Macintosh OS:Intel Mac OS X Localization:en Browser:Opera-9.27 Engine:Presto Bot:false Mobile:false",
+ "Platform:Windows OS:Windows XP Localization:en Browser:Opera-9.27 Engine:Presto Bot:false Mobile:false",
+ "Platform:Windows OS:Windows XP Browser:Opera-9.80 Engine:Presto-2.12.388 Bot:false Mobile:false",
+ "Platform:Windows OS:Windows Vista Browser:Opera-9.80 Engine:Presto-2.12.388 Bot:false Mobile:false",
+ "Browser:Opera-9.80 Engine:Presto Bot:false Mobile:false",
+ "Platform:Windows OS:Windows Vista Localization:en Browser:Opera-9.80 Engine:Presto-2.2.15 Bot:false Mobile:false",
+ "Platform:X11 OS:Linux x86_64 Browser:Opera-9.80 Engine:Presto-2.12.388 Bot:false Mobile:false",
+ "Platform:Android 4.2.1 OS:Linux Browser:Opera-9.80 Engine:Presto-2.11.355 Bot:false Mobile:true",
+ "Platform:Windows OS:Windows XP Browser:Opera-9.80 Engine:Presto-2.12.388 Bot:false Mobile:false",
+ "Platform:Windows OS:Windows 7 Localization:en Browser:Opera-9.80 Engine:Presto-2.9.168 Bot:false Mobile:false",
+
+ // Other
+ "Bot:false Mobile:false",
+ "Browser:nil Bot:false Mobile:false",
+ "Browser:Mozilla-4.0 Bot:false Mobile:false",
+ "Browser:Mozilla-5.0 Bot:false Mobile:false",
+ "Browser:amaya-9.51 Engine:libwww-5.4.0 Bot:false Mobile:false",
+ "Browser:Rails Engine:Testing Bot:false Mobile:false",
+ "Browser:Python-urllib-2.7 Bot:false Mobile:false",
+ "Browser:curl-7.28.1 Bot:false Mobile:false",
+
+ // WebKit
+ "Mozilla:5.0 Platform:X11 OS:Linux x86_64 Browser:Chrome-23.0.1271.97 Engine:AppleWebKit-537.11 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Windows OS:Windows 7 Browser:Chrome-18.0.1025.168 Engine:AppleWebKit-535.19 Bot:false Mobile:false",
+ "Mozilla:5.0 Browser:Chrome-8.0.552.215 Engine:AppleWebKit-534.10 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Macintosh OS:Intel Mac OS X 10_6_5 Localization:en-US Browser:Chrome-8.0.552.231 Engine:AppleWebKit-534.10 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Macintosh OS:Intel Mac OS X 10_6_3 Localization:en-us Browser:Safari-5.0 Engine:AppleWebKit-533.16 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Windows OS:Windows XP Localization:en Browser:Safari-4.0dp1 Engine:AppleWebKit-526.9 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:iPhone OS:CPU iPhone OS 7_0_3 like Mac OS X Browser:Safari-7.0 Engine:AppleWebKit-537.51.1 Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:iPhone OS:CPU like Mac OS X Localization:en Browser:Safari-3.0 Engine:AppleWebKit-420.1 Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:iPod OS:CPU like Mac OS X Localization:en Browser:Safari-3.0 Engine:AppleWebKit-420.1 Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:iPad OS:CPU OS 3_2 like Mac OS X Localization:en-us Browser:Safari-4.0.4 Engine:AppleWebKit-531.21.10 Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:webOS OS:Palm Localization:en-US Browser:webOS-1.0 Engine:AppleWebKit-532.2 Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:Linux OS:Android 1.5 Localization:de- Browser:Android-3.1.2 Engine:AppleWebKit-528.5+ Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:BlackBerry OS:BlackBerry 9800 Localization:en Browser:BlackBerry-6.0.0.141 Engine:AppleWebKit-534.1+ Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:BlackBerry OS:BlackBerry Browser:BlackBerry-10.0.9.388 Engine:AppleWebKit-537.3+ Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:Symbian OS:SymbianOS/9.4 Browser:Symbian-3.0 Engine:AppleWebKit-525 Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:Linux OS:Android 4.2.1 Browser:Chrome-18.0.1025.166 Engine:AppleWebKit-535.19 Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:en-us Localization:en-us Browser:Safari-3.1 Engine:AppleWebKit-525.13 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Linux OS:Android 4.2.2 Browser:Opera-14.0.1074.57453 Engine:AppleWebKit-537.31 Bot:false Mobile:true",
+ "Mozilla:5.0 Platform:X11 OS:Linux x86_64 Browser:Opera-14.0.1074.57453 Engine:AppleWebKit-537.31 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Linux OS:Linux Browser:Chrome-22.0.1229.79 Engine:AppleWebKit-537.4 Bot:false Mobile:false",
+ "Mozilla:5.0 Platform:Symbian OS:SymbianOS/9.1 Browser:Symbian-413 Engine:AppleWebKit-413 Bot:false Mobile:true",
+}
+
+// Internal: beautify the UserAgent reference into a string so it can be
+// tested later on.
+//
+// ua - a UserAgent reference.
+//
+// Returns a string that contains the beautified representation.
+func beautify(ua *UserAgent) (s string) {
+ if len(ua.Mozilla()) > 0 {
+ s += "Mozilla:" + ua.Mozilla() + " "
+ }
+ if len(ua.Platform()) > 0 {
+ s += "Platform:" + ua.Platform() + " "
+ }
+ if len(ua.OS()) > 0 {
+ s += "OS:" + ua.OS() + " "
+ }
+ if len(ua.Localization()) > 0 {
+ s += "Localization:" + ua.Localization() + " "
+ }
+ str1, str2 := ua.Browser()
+ if len(str1) > 0 {
+ s += "Browser:" + str1
+ if len(str2) > 0 {
+ s += "-" + str2 + " "
+ } else {
+ s += " "
+ }
+ }
+ str1, str2 = ua.Engine()
+ if len(str1) > 0 {
+ s += "Engine:" + str1
+ if len(str2) > 0 {
+ s += "-" + str2 + " "
+ } else {
+ s += " "
+ }
+ }
+ s += "Bot:" + fmt.Sprintf("%v", ua.Bot()) + " "
+ s += "Mobile:" + fmt.Sprintf("%v", ua.Mobile())
+ return s
+}
+
+// The test suite.
+func TestUserAgent(t *testing.T) {
+ for i, tt := range uastrings {
+ ua := New(tt.ua)
+ got := beautify(ua)
+ if expected[i] != got {
+ t.Errorf("Test %v => %q, expected %q", tt.name, got, expected[i])
+ }
+ }
+}
+
+// Benchmark: it parses each User-Agent string on the uastrings slice b.N times.
+func BenchmarkUserAgent(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ b.StopTimer()
+ for _, tt := range uastrings {
+ ua := new(UserAgent)
+ b.StartTimer()
+ ua.Parse(tt.ua)
+ }
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/mssola/user_agent/bot.go b/Godeps/_workspace/src/github.com/mssola/user_agent/bot.go
new file mode 100644
index 000000000..cc993d8fe
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/mssola/user_agent/bot.go
@@ -0,0 +1,121 @@
+// Copyright (C) 2014-2015 Miquel Sabaté Solà <mikisabate@gmail.com>
+// This file is licensed under the MIT license.
+// See the LICENSE file.
+
+package user_agent
+
+import (
+ "regexp"
+ "strings"
+)
+
+// Get the name of the bot from the website that may be in the given comment. If
+// there is no website in the comment, then an empty string is returned.
+func getFromSite(comment []string) string {
+ if len(comment) == 0 {
+ return ""
+ }
+
+ // Where we should check the website.
+ idx := 2
+ if len(comment) < 3 {
+ idx = 0
+ }
+
+ // Pick the site.
+ re := regexp.MustCompile("http://.+\\.\\w+")
+ results := re.FindStringSubmatch(comment[idx])
+ if len(results) == 1 {
+ // If it's a simple comment, just return the name of the site.
+ if idx == 0 {
+ return results[0]
+ }
+
+ // This is a large comment, usually the name will be in the previous
+ // field of the comment.
+ return strings.TrimSpace(comment[1])
+ }
+ return ""
+}
+
+// Returns true if the info that we currently have corresponds to the Google
+// mobile bot. This function also modifies some attributes in the receiver
+// accordingly.
+func (p *UserAgent) googleBot() bool {
+ // This is a hackish way to detect Google's mobile bot.
+ if strings.Index(p.ua, "Googlebot") != -1 {
+ p.platform = ""
+ p.undecided = true
+ }
+ return p.undecided
+}
+
+// Set the attributes of the receiver as given by the parameters. All the other
+// parameters are set to empty.
+func (p *UserAgent) setSimple(name, version string, bot bool) {
+ p.bot = bot
+ if !bot {
+ p.mozilla = ""
+ }
+ p.browser.Name = name
+ p.browser.Version = version
+ p.browser.Engine = ""
+ p.browser.EngineVersion = ""
+ p.os = ""
+ p.localization = ""
+}
+
+// Fix some values for some weird browsers.
+func (p *UserAgent) fixOther(sections []section) {
+ if len(sections) > 0 {
+ p.browser.Name = sections[0].name
+ p.browser.Version = sections[0].version
+ p.mozilla = ""
+ }
+}
+
+// Check if we're dealing with a bot or with some weird browser. If that is the
+// case, the receiver will be modified accordingly.
+func (p *UserAgent) checkBot(sections []section) {
+ // If there's only one element, and it's doesn't have the Mozilla string,
+ // check whether this is a bot or not.
+ if len(sections) == 1 && sections[0].name != "Mozilla" {
+ p.mozilla = ""
+
+ // Check whether the name has some suspicious "bot" in his name.
+ reg, _ := regexp.Compile("(?i)bot")
+ if reg.Match([]byte(sections[0].name)) {
+ p.setSimple(sections[0].name, "", true)
+ return
+ }
+
+ // Tough luck, let's try to see if it has a website in his comment.
+ if name := getFromSite(sections[0].comment); name != "" {
+ // First of all, this is a bot. Moreover, since it doesn't have the
+ // Mozilla string, we can assume that the name and the version are
+ // the ones from the first section.
+ p.setSimple(sections[0].name, sections[0].version, true)
+ return
+ }
+
+ // At this point we are sure that this is not a bot, but some weirdo.
+ p.setSimple(sections[0].name, sections[0].version, false)
+ } else {
+ // Let's iterate over the available comments and check for a website.
+ for _, v := range sections {
+ if name := getFromSite(v.comment); name != "" {
+ // Ok, we've got a bot name.
+ results := strings.SplitN(name, "/", 2)
+ version := ""
+ if len(results) == 2 {
+ version = results[1]
+ }
+ p.setSimple(results[0], version, true)
+ return
+ }
+ }
+
+ // We will assume that this is some other weird browser.
+ p.fixOther(sections)
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/mssola/user_agent/browser.go b/Godeps/_workspace/src/github.com/mssola/user_agent/browser.go
new file mode 100644
index 000000000..9fb27e5f3
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/mssola/user_agent/browser.go
@@ -0,0 +1,120 @@
+// Copyright (C) 2012-2015 Miquel Sabaté Solà <mikisabate@gmail.com>
+// This file is licensed under the MIT license.
+// See the LICENSE file.
+
+package user_agent
+
+import (
+ "regexp"
+ "strings"
+)
+
+// A struct containing all the information that we might be
+// interested from the browser.
+type Browser struct {
+ // The name of the browser's engine.
+ Engine string
+
+ // The version of the browser's engine.
+ EngineVersion string
+
+ // The name of the browser.
+ Name string
+
+ // The version of the browser.
+ Version string
+}
+
+// Extract all the information that we can get from the User-Agent string
+// about the browser and update the receiver with this information.
+//
+// The function receives just one argument "sections", that contains the
+// sections from the User-Agent string after being parsed.
+func (p *UserAgent) detectBrowser(sections []section) {
+ slen := len(sections)
+
+ if sections[0].name == "Opera" {
+ p.mozilla = ""
+ p.browser.Name = "Opera"
+ p.browser.Version = sections[0].version
+ p.browser.Engine = "Presto"
+ if slen > 1 {
+ p.browser.EngineVersion = sections[1].version
+ }
+ } else if slen > 1 {
+ engine := sections[1]
+ p.browser.Engine = engine.name
+ p.browser.EngineVersion = engine.version
+ if slen > 2 {
+ p.browser.Version = sections[2].version
+ if engine.name == "AppleWebKit" {
+ if sections[slen-1].name == "OPR" {
+ p.browser.Name = "Opera"
+ p.browser.Version = sections[slen-1].version
+ } else if sections[2].name == "Chrome" {
+ p.browser.Name = "Chrome"
+ } else {
+ p.browser.Name = "Safari"
+ }
+ } else if engine.name == "Gecko" {
+ name := sections[2].name
+ if name == "MRA" && slen > 4 {
+ name = sections[4].name
+ p.browser.Version = sections[4].version
+ }
+ p.browser.Name = name
+ } else if engine.name == "like" && sections[2].name == "Gecko" {
+ // This is the new user agent from Internet Explorer 11.
+ p.browser.Engine = "Trident"
+ p.browser.Name = "Internet Explorer"
+ reg, _ := regexp.Compile("^rv:(.+)$")
+ for _, c := range sections[0].comment {
+ version := reg.FindStringSubmatch(c)
+ if len(version) > 0 {
+ p.browser.Version = version[1]
+ return
+ }
+ }
+ p.browser.Version = ""
+ }
+ }
+ } else if slen == 1 && len(sections[0].comment) > 1 {
+ comment := sections[0].comment
+ if comment[0] == "compatible" && strings.HasPrefix(comment[1], "MSIE") {
+ p.browser.Engine = "Trident"
+ p.browser.Name = "Internet Explorer"
+ // The MSIE version may be reported as the compatibility version.
+ // For IE 8 through 10, the Trident token is more accurate.
+ // http://msdn.microsoft.com/en-us/library/ie/ms537503(v=vs.85).aspx#VerToken
+ for _, v := range comment {
+ if strings.HasPrefix(v, "Trident/") {
+ switch v[8:] {
+ case "4.0":
+ p.browser.Version = "8.0"
+ case "5.0":
+ p.browser.Version = "9.0"
+ case "6.0":
+ p.browser.Version = "10.0"
+ }
+ break
+ }
+ }
+ // If the Trident token is not provided, fall back to MSIE token.
+ if p.browser.Version == "" {
+ p.browser.Version = strings.TrimSpace(comment[1][4:])
+ }
+ }
+ }
+}
+
+// Returns two strings. The first string is the name of the engine and the
+// second one is the version of the engine.
+func (p *UserAgent) Engine() (string, string) {
+ return p.browser.Engine, p.browser.EngineVersion
+}
+
+// Returns two strings. The first string is the name of the browser and the
+// second one is the version of the browser.
+func (p *UserAgent) Browser() (string, string) {
+ return p.browser.Name, p.browser.Version
+}
diff --git a/Godeps/_workspace/src/github.com/mssola/user_agent/operating_systems.go b/Godeps/_workspace/src/github.com/mssola/user_agent/operating_systems.go
new file mode 100644
index 000000000..2771b57e2
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/mssola/user_agent/operating_systems.go
@@ -0,0 +1,260 @@
+// Copyright (C) 2012-2015 Miquel Sabaté Solà <mikisabate@gmail.com>
+// This file is licensed under the MIT license.
+// See the LICENSE file.
+
+package user_agent
+
+import "strings"
+
+// Normalize the name of the operating system. By now, this just
+// affects to Windows.
+//
+// Returns a string containing the normalized name for the Operating System.
+func normalizeOS(name string) string {
+ sp := strings.SplitN(name, " ", 3)
+ if len(sp) != 3 {
+ return name
+ }
+
+ switch sp[2] {
+ case "5.0":
+ return "Windows 2000"
+ case "5.01":
+ return "Windows 2000, Service Pack 1 (SP1)"
+ case "5.1":
+ return "Windows XP"
+ case "5.2":
+ return "Windows XP x64 Edition"
+ case "6.0":
+ return "Windows Vista"
+ case "6.1":
+ return "Windows 7"
+ case "6.2":
+ return "Windows 8"
+ case "6.3":
+ return "Windows 8.1"
+ case "6.4":
+ return "Windows 10"
+ }
+ return name
+}
+
+// Guess the OS, the localization and if this is a mobile device for a
+// Webkit-powered browser.
+//
+// The first argument p is a reference to the current UserAgent and the second
+// argument is a slice of strings containing the comment.
+func webkit(p *UserAgent, comment []string) {
+ if p.platform == "webOS" {
+ p.browser.Name = p.platform
+ p.os = "Palm"
+ if len(comment) > 2 {
+ p.localization = comment[2]
+ }
+ p.mobile = true
+ } else if p.platform == "Symbian" {
+ p.mobile = true
+ p.browser.Name = p.platform
+ p.os = comment[0]
+ } else if p.platform == "Linux" {
+ p.mobile = true
+ if p.browser.Name == "Safari" {
+ p.browser.Name = "Android"
+ }
+ if len(comment) > 1 {
+ if comment[1] == "U" {
+ if len(comment) > 2 {
+ p.os = comment[2]
+ } else {
+ p.mobile = false
+ p.os = comment[0]
+ }
+ } else {
+ p.os = comment[1]
+ }
+ }
+ if len(comment) > 3 {
+ p.localization = comment[3]
+ }
+ } else if len(comment) > 0 {
+ if len(comment) > 3 {
+ p.localization = comment[3]
+ }
+ if strings.HasPrefix(comment[0], "Windows NT") {
+ p.os = normalizeOS(comment[0])
+ } else if len(comment) < 2 {
+ p.localization = comment[0]
+ } else if len(comment) < 3 {
+ if !p.googleBot() {
+ p.os = normalizeOS(comment[1])
+ }
+ } else {
+ p.os = normalizeOS(comment[2])
+ }
+ if p.platform == "BlackBerry" {
+ p.browser.Name = p.platform
+ if p.os == "Touch" {
+ p.os = p.platform
+ }
+ }
+ }
+}
+
+// Guess the OS, the localization and if this is a mobile device
+// for a Gecko-powered browser.
+//
+// The first argument p is a reference to the current UserAgent and the second
+// argument is a slice of strings containing the comment.
+func gecko(p *UserAgent, comment []string) {
+ if len(comment) > 1 {
+ if comment[1] == "U" {
+ if len(comment) > 2 {
+ p.os = normalizeOS(comment[2])
+ } else {
+ p.os = normalizeOS(comment[1])
+ }
+ } else {
+ if p.platform == "Android" {
+ p.mobile = true
+ p.platform, p.os = normalizeOS(comment[1]), p.platform
+ } else if comment[0] == "Mobile" || comment[0] == "Tablet" {
+ p.mobile = true
+ p.os = "FirefoxOS"
+ } else {
+ if p.os == "" {
+ p.os = normalizeOS(comment[1])
+ }
+ }
+ }
+ if len(comment) > 3 {
+ p.localization = comment[3]
+ }
+ }
+}
+
+// Guess the OS, the localization and if this is a mobile device
+// for Internet Explorer.
+//
+// The first argument p is a reference to the current UserAgent and the second
+// argument is a slice of strings containing the comment.
+func trident(p *UserAgent, comment []string) {
+ // Internet Explorer only runs on Windows.
+ p.platform = "Windows"
+
+ // The OS can be set before to handle a new case in IE11.
+ if p.os == "" {
+ if len(comment) > 2 {
+ p.os = normalizeOS(comment[2])
+ } else {
+ p.os = "Windows NT 4.0"
+ }
+ }
+
+ // Last but not least, let's detect if it comes from a mobile device.
+ for _, v := range comment {
+ if strings.HasPrefix(v, "IEMobile") {
+ p.mobile = true
+ return
+ }
+ }
+}
+
+// Guess the OS, the localization and if this is a mobile device
+// for Opera.
+//
+// The first argument p is a reference to the current UserAgent and the second
+// argument is a slice of strings containing the comment.
+func opera(p *UserAgent, comment []string) {
+ slen := len(comment)
+
+ if strings.HasPrefix(comment[0], "Windows") {
+ p.platform = "Windows"
+ p.os = normalizeOS(comment[0])
+ if slen > 2 {
+ if slen > 3 && strings.HasPrefix(comment[2], "MRA") {
+ p.localization = comment[3]
+ } else {
+ p.localization = comment[2]
+ }
+ }
+ } else {
+ if strings.HasPrefix(comment[0], "Android") {
+ p.mobile = true
+ }
+ p.platform = comment[0]
+ if slen > 1 {
+ p.os = comment[1]
+ if slen > 3 {
+ p.localization = comment[3]
+ }
+ } else {
+ p.os = comment[0]
+ }
+ }
+}
+
+// Given the comment of the first section of the UserAgent string,
+// get the platform.
+func getPlatform(comment []string) string {
+ if len(comment) > 0 {
+ if comment[0] != "compatible" {
+ if strings.HasPrefix(comment[0], "Windows") {
+ return "Windows"
+ } else if strings.HasPrefix(comment[0], "Symbian") {
+ return "Symbian"
+ } else if strings.HasPrefix(comment[0], "webOS") {
+ return "webOS"
+ } else if comment[0] == "BB10" {
+ return "BlackBerry"
+ }
+ return comment[0]
+ }
+ }
+ return ""
+}
+
+// Detect some properties of the OS from the given section.
+func (p *UserAgent) detectOS(s section) {
+ if s.name == "Mozilla" {
+ // Get the platform here. Be aware that IE11 provides a new format
+ // that is not backwards-compatible with previous versions of IE.
+ p.platform = getPlatform(s.comment)
+ if p.platform == "Windows" && len(s.comment) > 0 {
+ p.os = normalizeOS(s.comment[0])
+ }
+
+ // And finally get the OS depending on the engine.
+ switch p.browser.Engine {
+ case "":
+ p.undecided = true
+ case "Gecko":
+ gecko(p, s.comment)
+ case "AppleWebKit":
+ webkit(p, s.comment)
+ case "Trident":
+ trident(p, s.comment)
+ }
+ } else if s.name == "Opera" {
+ if len(s.comment) > 0 {
+ opera(p, s.comment)
+ }
+ } else {
+ // Check whether this is a bot or just a weird browser.
+ p.undecided = true
+ }
+}
+
+// Returns a string containing the platform..
+func (p *UserAgent) Platform() string {
+ return p.platform
+}
+
+// Returns a string containing the name of the Operating System.
+func (p *UserAgent) OS() string {
+ return p.os
+}
+
+// Returns a string containing the localization.
+func (p *UserAgent) Localization() string {
+ return p.localization
+}
diff --git a/Godeps/_workspace/src/github.com/mssola/user_agent/user_agent.go b/Godeps/_workspace/src/github.com/mssola/user_agent/user_agent.go
new file mode 100644
index 000000000..df1aa3b78
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/mssola/user_agent/user_agent.go
@@ -0,0 +1,169 @@
+// Copyright (C) 2012-2015 Miquel Sabaté Solà <mikisabate@gmail.com>
+// This file is licensed under the MIT license.
+// See the LICENSE file.
+
+// Package user_agent implements an HTTP User Agent string parser. It defines
+// the type UserAgent that contains all the information from the parsed string.
+// It also implements the Parse function and getters for all the relevant
+// information that has been extracted from a parsed User Agent string.
+package user_agent
+
+import (
+ "strings"
+)
+
+// A section contains the name of the product, its version and
+// an optional comment.
+type section struct {
+ name string
+ version string
+ comment []string
+}
+
+// The UserAgent struct contains all the info that can be extracted
+// from the User-Agent string.
+type UserAgent struct {
+ ua string
+ mozilla string
+ platform string
+ os string
+ localization string
+ browser Browser
+ bot bool
+ mobile bool
+ undecided bool
+}
+
+// Read from the given string until the given delimiter or the
+// end of the string have been reached.
+//
+// The first argument is the user agent string being parsed. The second
+// argument is a reference pointing to the current index of the user agent
+// string. The delimiter argument specifies which character is the delimiter
+// and the cat argument determines whether nested '(' should be ignored or not.
+//
+// Returns an array of bytes containing what has been read.
+func readUntil(ua string, index *int, delimiter byte, cat bool) []byte {
+ var buffer []byte
+
+ i := *index
+ catalan := 0
+ for ; i < len(ua); i = i + 1 {
+ if ua[i] == delimiter {
+ if catalan == 0 {
+ *index = i + 1
+ return buffer
+ }
+ catalan--
+ } else if cat && ua[i] == '(' {
+ catalan++
+ }
+ buffer = append(buffer, ua[i])
+ }
+ *index = i + 1
+ return buffer
+}
+
+// Parse the given product, that is, just a name or a string
+// formatted as Name/Version.
+//
+// It returns two strings. The first string is the name of the product and the
+// second string contains the version of the product.
+func parseProduct(product []byte) (string, string) {
+ prod := strings.SplitN(string(product), "/", 2)
+ if len(prod) == 2 {
+ return prod[0], prod[1]
+ }
+ return string(product), ""
+}
+
+// Parse a section. A section is typically formatted as follows
+// "Name/Version (comment)". Both, the comment and the version are optional.
+//
+// The first argument is the user agent string being parsed. The second
+// argument is a reference pointing to the current index of the user agent
+// string.
+//
+// Returns a section containing the information that we could extract
+// from the last parsed section.
+func parseSection(ua string, index *int) (s section) {
+ buffer := readUntil(ua, index, ' ', false)
+
+ s.name, s.version = parseProduct(buffer)
+ if *index < len(ua) && ua[*index] == '(' {
+ *index++
+ buffer = readUntil(ua, index, ')', true)
+ s.comment = strings.Split(string(buffer), "; ")
+ *index++
+ }
+ return s
+}
+
+// Initialize the parser.
+func (p *UserAgent) initialize() {
+ p.ua = ""
+ p.mozilla = ""
+ p.platform = ""
+ p.os = ""
+ p.localization = ""
+ p.browser.Engine = ""
+ p.browser.EngineVersion = ""
+ p.browser.Name = ""
+ p.browser.Version = ""
+ p.bot = false
+ p.mobile = false
+ p.undecided = false
+}
+
+// Parse the given User-Agent string and get the resulting UserAgent object.
+//
+// Returns an UserAgent object that has been initialized after parsing
+// the given User-Agent string.
+func New(ua string) *UserAgent {
+ o := &UserAgent{}
+ o.Parse(ua)
+ return o
+}
+
+// Parse the given User-Agent string. After calling this function, the
+// receiver will be setted up with all the information that we've extracted.
+func (p *UserAgent) Parse(ua string) {
+ var sections []section
+
+ p.initialize()
+ p.ua = ua
+ for index, limit := 0, len(ua); index < limit; {
+ s := parseSection(ua, &index)
+ if !p.mobile && s.name == "Mobile" {
+ p.mobile = true
+ }
+ sections = append(sections, s)
+ }
+
+ if len(sections) > 0 {
+ p.mozilla = sections[0].version
+
+ p.detectBrowser(sections)
+ p.detectOS(sections[0])
+
+ if p.undecided {
+ p.checkBot(sections)
+ }
+ }
+}
+
+// Returns the mozilla version (it's how the User Agent string begins:
+// "Mozilla/5.0 ...", unless we're dealing with Opera, of course).
+func (p *UserAgent) Mozilla() string {
+ return p.mozilla
+}
+
+// Returns true if it's a bot, false otherwise.
+func (p *UserAgent) Bot() bool {
+ return p.bot
+}
+
+// Returns true if it's a mobile device, false otherwise.
+func (p *UserAgent) Mobile() bool {
+ return p.mobile
+}