From 84d2482ddbff9564c9ad75b2d30af66e3ddfd44d Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Thu, 12 May 2016 15:08:58 -0400 Subject: Updating go depencancies. Switching to go1.6 vendoring (#2949) --- vendor/github.com/mssola/user_agent/.travis.yml | 12 + vendor/github.com/mssola/user_agent/LICENSE | 20 ++ vendor/github.com/mssola/user_agent/README.md | 51 ++++ vendor/github.com/mssola/user_agent/bot.go | 123 +++++++++ vendor/github.com/mssola/user_agent/browser.go | 132 ++++++++++ .../mssola/user_agent/operating_systems.go | 281 +++++++++++++++++++++ vendor/github.com/mssola/user_agent/user_agent.go | 174 +++++++++++++ 7 files changed, 793 insertions(+) create mode 100644 vendor/github.com/mssola/user_agent/.travis.yml create mode 100644 vendor/github.com/mssola/user_agent/LICENSE create mode 100644 vendor/github.com/mssola/user_agent/README.md create mode 100644 vendor/github.com/mssola/user_agent/bot.go create mode 100644 vendor/github.com/mssola/user_agent/browser.go create mode 100644 vendor/github.com/mssola/user_agent/operating_systems.go create mode 100644 vendor/github.com/mssola/user_agent/user_agent.go (limited to 'vendor/github.com/mssola/user_agent') diff --git a/vendor/github.com/mssola/user_agent/.travis.yml b/vendor/github.com/mssola/user_agent/.travis.yml new file mode 100644 index 000000000..33c596acb --- /dev/null +++ b/vendor/github.com/mssola/user_agent/.travis.yml @@ -0,0 +1,12 @@ +language: go +go: + - 1.0 + - 1.1 + - 1.2 + - 1.3 + - 1.4 + - 1.5 + - tip +matrix: + allow_failures: + - go: tip diff --git a/vendor/github.com/mssola/user_agent/LICENSE b/vendor/github.com/mssola/user_agent/LICENSE new file mode 100644 index 000000000..2784cdcac --- /dev/null +++ b/vendor/github.com/mssola/user_agent/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2012-2016 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/vendor/github.com/mssola/user_agent/README.md b/vendor/github.com/mssola/user_agent/README.md new file mode 100644 index 000000000..5c5e77276 --- /dev/null +++ b/vendor/github.com/mssola/user_agent/README.md @@ -0,0 +1,51 @@ + +# UserAgent [![Build Status](https://travis-ci.org/mssola/user_agent.png?branch=master)](https://travis-ci.org/mssola/user_agent) [![GoDoc](https://godoc.org/github.com/mssola/user_agent?status.png)](http://godoc.org/github.com/mssola/user_agent) + + +UserAgent is a Go library that parses HTTP User Agents. + +## Usage + +~~~ go +package main + +import ( + "fmt" + + "github.com/mssola/user_agent" +) + +func main() { + // The "New" function will create a new UserAgent object and it will parse + // the given string. If you need to parse more strings, you can re-use + // this object and call: ua.Parse("another string") + ua := user_agent.New("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11"); + + fmt.Printf("%v\n", ua.Mobile()) // => false + fmt.Printf("%v\n", ua.Bot()) // => false + fmt.Printf("%v\n", ua.Mozilla()) // => "5.0" + + fmt.Printf("%v\n", ua.Platform()) // => "X11" + fmt.Printf("%v\n", ua.OS()) // => "Linux x86_64" + + name, version := ua.Engine() + fmt.Printf("%v\n", name) // => "AppleWebKit" + fmt.Printf("%v\n", version) // => "537.11" + + name, version = ua.Browser() + fmt.Printf("%v\n", name) // => "Chrome" + fmt.Printf("%v\n", version) // => "23.0.1271.97" + + // Let's see an example with a bot. + + ua.Parse("Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)") + + fmt.Printf("%v\n", ua.Bot()) // => true + + name, version = ua.Browser() + fmt.Printf("%v\n", name) // => Googlebot + fmt.Printf("%v\n", version) // => 2.1 +} +~~~ + +Copyright © 2012-2016 Miquel Sabaté Solà, released under the MIT License. diff --git a/vendor/github.com/mssola/user_agent/bot.go b/vendor/github.com/mssola/user_agent/bot.go new file mode 100644 index 000000000..2b118d661 --- /dev/null +++ b/vendor/github.com/mssola/user_agent/bot.go @@ -0,0 +1,123 @@ +// Copyright (C) 2014-2016 Miquel Sabaté Solà +// This file is licensed under the MIT license. +// See the LICENSE file. + +package user_agent + +import ( + "regexp" + "strings" +) + +var botFromSiteRegexp = regexp.MustCompile("http://.+\\.\\w+") + +// 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. + results := botFromSiteRegexp.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 = "" + } +} + +var botRegex = regexp.MustCompile("(?i)(bot|crawler|sp(i|y)der|search|worm|fetch|nutch)") + +// 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" or "crawler" in his name. + if botRegex.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/vendor/github.com/mssola/user_agent/browser.go b/vendor/github.com/mssola/user_agent/browser.go new file mode 100644 index 000000000..17a243c36 --- /dev/null +++ b/vendor/github.com/mssola/user_agent/browser.go @@ -0,0 +1,132 @@ +// Copyright (C) 2012-2016 Miquel Sabaté Solà +// This file is licensed under the MIT license. +// See the LICENSE file. + +package user_agent + +import ( + "regexp" + "strings" +) + +var ie11Regexp = regexp.MustCompile("^rv:(.+)$") + +// 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.browser.Name = "Opera" + p.browser.Version = sections[0].version + p.browser.Engine = "Presto" + if slen > 1 { + p.browser.EngineVersion = sections[1].version + } + } else if sections[0].name == "Dalvik" { + // When Dalvik VM is in use, there is no browser info attached to ua. + // Although browser is still a Mozilla/5.0 compatible. + p.mozilla = "5.0" + } 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" { + switch sections[slen-1].name { + case "Edge": + p.browser.Name = "Edge" + p.browser.Version = sections[slen-1].version + p.browser.Engine = "EdgeHTML" + p.browser.EngineVersion = "" + case "OPR": + p.browser.Name = "Opera" + p.browser.Version = sections[slen-1].version + default: + 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" + for _, c := range sections[0].comment { + version := ie11Regexp.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/vendor/github.com/mssola/user_agent/operating_systems.go b/vendor/github.com/mssola/user_agent/operating_systems.go new file mode 100644 index 000000000..c4720a75a --- /dev/null +++ b/vendor/github.com/mssola/user_agent/operating_systems.go @@ -0,0 +1,281 @@ +// Copyright (C) 2012-2016 Miquel Sabaté Solà +// 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 NT. +// +// 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 || sp[1] != "NT" { + 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 "10.0": + 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] + } + } +} + +// Guess the OS. Android browsers send Dalvik as the user agent in the +// request header. +// +// The first argument p is a reference to the current UserAgent and the second +// argument is a slice of strings containing the comment. +func dalvik(p *UserAgent, comment []string) { + slen := len(comment) + + if strings.HasPrefix(comment[0], "Linux") { + p.platform = comment[0] + if slen > 2 { + p.os = comment[2] + } + p.mobile = true + } +} + +// 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 if s.name == "Dalvik" { + if len(s.comment) > 0 { + dalvik(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/vendor/github.com/mssola/user_agent/user_agent.go b/vendor/github.com/mssola/user_agent/user_agent.go new file mode 100644 index 000000000..18a673eef --- /dev/null +++ b/vendor/github.com/mssola/user_agent/user_agent.go @@ -0,0 +1,174 @@ +// Copyright (C) 2012-2016 Miquel Sabaté Solà +// 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 { + if sections[0].name == "Mozilla" { + 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 +} + +// Returns the original given user agent. +func (p *UserAgent) UA() string { + return p.ua +} -- cgit v1.2.3-1-g7c22