From ffe1cc37021cbf891de9df645feb8438d5faf3d5 Mon Sep 17 00:00:00 2001 From: Ruslan Semagin Date: Fri, 17 Nov 2023 16:45:31 +0300 Subject: [PATCH 01/16] Using constants, corrected typos and added some shorthand methods --- .gitignore | 3 + is.go | 15 ++++ ua.go | 197 +++++++++++++++++++++++++++-------------------------- ua_test.go | 1 + 4 files changed, 121 insertions(+), 95 deletions(-) diff --git a/.gitignore b/.gitignore index 8078d86..c58450a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +.idea/ +.vscode/ +.DS_Store old.go old_test.go file.txt diff --git a/is.go b/is.go index a3bf5b3..3fff5a4 100644 --- a/is.go +++ b/is.go @@ -30,6 +30,11 @@ func (ua UserAgent) IsChromeOS() bool { return ua.OS == ChromeOS || ua.OS == "CrOS" } +// IsBlackberryOS shorthand function to check if OS == BlackBerry +func (ua UserAgent) IsBlackberryOS() bool { + return ua.OS == BlackBerry +} + // IsOpera shorthand function to check if Name == Opera func (ua UserAgent) IsOpera() bool { return ua.Name == Opera @@ -65,6 +70,11 @@ func (ua UserAgent) IsEdge() bool { return ua.Name == Edge } +// IsBlackBerry shorthand function to check if Name == BlackBerry +func (ua UserAgent) IsBlackBerry() bool { + return ua.Name == BlackBerry +} + // IsGooglebot shorthand function to check if Name == Googlebot func (ua UserAgent) IsGooglebot() bool { return ua.Name == Googlebot @@ -80,6 +90,11 @@ func (ua UserAgent) IsFacebookbot() bool { return ua.Name == FacebookExternalHit } +// IsYandexbot shorthand function to check if Name == YandexBot +func (ua UserAgent) IsYandexbot() bool { + return ua.Name == YandexBot +} + // IsUnknown returns true if the package can't determine the user agent reliably. // Fields like Name, OS, etc. might still have values. func (ua UserAgent) IsUnknown() bool { diff --git a/ua.go b/ua.go index a292f9c..0771191 100644 --- a/ua.go +++ b/ua.go @@ -25,15 +25,18 @@ type UserAgent struct { // Constants for browsers and operating systems for easier comparison const ( - Windows = "Windows" - WindowsPhone = "Windows Phone" - Android = "Android" - MacOS = "macOS" - IOS = "iOS" - Linux = "Linux" - FreeBSD = "FreeBSD" - ChromeOS = "ChromeOS" - BlackBerry = "BlackBerry" + Windows = "Windows" + WindowsPhone = "Windows Phone" + WindowsNT = "Windows NT" + WindowsPhoneOS = "Windows Phone OS" + Android = "Android" + MacOS = "macOS" + IOS = "iOS" + Linux = "Linux" + FreeBSD = "FreeBSD" + ChromeOS = "ChromeOS" + BlackBerry = "BlackBerry" + CrOS = "CrOS" Opera = "Opera" OperaMini = "Opera Mini" @@ -45,6 +48,10 @@ const ( Safari = "Safari" Edge = "Edge" Vivaldi = "Vivaldi" + MobileSafari = "Mobile Safari" + NetFront = "NetFront" + Mozilla = "Mozilla" + Msie = "MSIE" GoogleAdsBot = "Google Ads Bot" Googlebot = "Googlebot" @@ -52,10 +59,16 @@ const ( FacebookExternalHit = "facebookexternalhit" Applebot = "Applebot" Bingbot = "Bingbot" + YandexBot = "YandexBot" + YandexAdNet = "YandexAdNet" FacebookApp = "Facebook App" InstagramApp = "Instagram App" TiktokApp = "TikTok App" + + Version = "Version" + Mobile = "Mobile" + Tablet = "Tablet" ) // Parse user agent string returning UserAgent struct @@ -75,15 +88,13 @@ func Parse(userAgent string) UserAgent { } } - //fmt.Printf("%+v\n", tokens) - // OS lookup switch { - case tokens.exists("Android"): + case tokens.exists(Android): ua.OS = Android var osIndex int osIndex, ua.OSVersion = tokens.getIndexValue(Android) - ua.Tablet = strings.Contains(strings.ToLower(ua.String), "tablet") + ua.Tablet = strings.Contains(strings.ToLower(ua.String), strings.ToLower(Tablet)) ua.Device = tokens.findAndroidDevice(osIndex) case tokens.exists("iPhone"): @@ -98,14 +109,14 @@ func Parse(userAgent string) UserAgent { ua.Device = "iPad" ua.Tablet = true - case tokens.exists("Windows NT"): + case tokens.exists(WindowsNT): ua.OS = Windows - ua.OSVersion = tokens.get("Windows NT") + ua.OSVersion = tokens.get(WindowsNT) ua.Desktop = true - case tokens.exists("Windows Phone OS"): + case tokens.exists(WindowsPhoneOS): ua.OS = WindowsPhone - ua.OSVersion = tokens.get("Windows Phone OS") + ua.OSVersion = tokens.get(WindowsPhoneOS) ua.Mobile = true case tokens.exists("Macintosh"): @@ -113,33 +124,33 @@ func Parse(userAgent string) UserAgent { ua.OSVersion = tokens.findMacOSVersion() ua.Desktop = true - case tokens.exists("Linux"): + case tokens.exists(Linux): ua.OS = Linux ua.OSVersion = tokens.get(Linux) ua.Desktop = true - case tokens.exists("FreeBSD"): + case tokens.exists(FreeBSD): ua.OS = FreeBSD ua.OSVersion = tokens.get(FreeBSD) ua.Desktop = true - case tokens.exists("CrOS"): + case tokens.exists(CrOS): ua.OS = ChromeOS - ua.OSVersion = tokens.get("CrOS") + ua.OSVersion = tokens.get(CrOS) ua.Desktop = true - case tokens.exists("BlackBerry"): + case tokens.exists(BlackBerry): ua.OS = BlackBerry - ua.OSVersion = tokens.get("BlackBerry") + ua.OSVersion = tokens.get(BlackBerry) ua.Mobile = true } switch { - case tokens.exists("Googlebot"): + case tokens.exists(Googlebot): ua.Name = Googlebot ua.Version = tokens.get(Googlebot) ua.Bot = true - ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari") + ua.Mobile = tokens.existsAny(Mobile, MobileSafari) case tokens.existsAny("GoogleProber", "GoogleProducer"): if name := tokens.findBestMatch(false); name != "" { @@ -147,14 +158,14 @@ func Parse(userAgent string) UserAgent { } ua.Bot = true - case tokens.exists("Applebot"): + case tokens.exists(Applebot): ua.Name = Applebot ua.Version = tokens.get(Applebot) ua.Bot = true - ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari") + ua.Mobile = tokens.existsAny(Mobile, MobileSafari) ua.OS = "" - case tokens.get("Opera Mini") != "": + case tokens.get(OperaMini) != "": ua.Name = OperaMini ua.Version = tokens.get(OperaMini) ua.Mobile = true @@ -162,84 +173,91 @@ func Parse(userAgent string) UserAgent { case tokens.get("OPR") != "": ua.Name = Opera ua.Version = tokens.get("OPR") - ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari") + ua.Mobile = tokens.existsAny(Mobile, MobileSafari) case tokens.get("OPT") != "": ua.Name = OperaTouch ua.Version = tokens.get("OPT") - ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari") + ua.Mobile = tokens.existsAny(Mobile, MobileSafari) // Opera on iOS case tokens.get("OPiOS") != "": ua.Name = Opera ua.Version = tokens.get("OPiOS") - ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari") + ua.Mobile = tokens.existsAny(Mobile, MobileSafari) // Chrome on iOS case tokens.get("CriOS") != "": ua.Name = Chrome ua.Version = tokens.get("CriOS") - ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari") + ua.Mobile = tokens.existsAny(Mobile, MobileSafari) // Firefox on iOS case tokens.get("FxiOS") != "": ua.Name = Firefox ua.Version = tokens.get("FxiOS") - ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari") + ua.Mobile = tokens.existsAny(Mobile, MobileSafari) - case tokens.get("Firefox") != "": + case tokens.get(Firefox) != "": ua.Name = Firefox ua.Version = tokens.get(Firefox) - ua.Mobile = tokens.exists("Mobile") - ua.Tablet = tokens.exists("Tablet") + ua.Mobile = tokens.exists(Mobile) + ua.Tablet = tokens.exists(Tablet) - case tokens.get("Vivaldi") != "": + case tokens.get(Vivaldi) != "": ua.Name = Vivaldi ua.Version = tokens.get(Vivaldi) - case tokens.exists("MSIE"): + case tokens.exists(Msie): ua.Name = InternetExplorer - ua.Version = tokens.get("MSIE") + ua.Version = tokens.get(Msie) case tokens.get("EdgiOS") != "": ua.Name = Edge ua.Version = tokens.get("EdgiOS") - ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari") + ua.Mobile = tokens.existsAny(Mobile, MobileSafari) - case tokens.get("Edge") != "": + case tokens.get(Edge) != "": ua.Name = Edge - ua.Version = tokens.get("Edge") - ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari") + ua.Version = tokens.get(Edge) + ua.Mobile = tokens.existsAny(Mobile, MobileSafari) case tokens.get("Edg") != "": ua.Name = Edge ua.Version = tokens.get("Edg") - ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari") + ua.Mobile = tokens.existsAny(Mobile, MobileSafari) case tokens.get("EdgA") != "": ua.Name = Edge ua.Version = tokens.get("EdgA") - ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari") + ua.Mobile = tokens.existsAny(Mobile, MobileSafari) case tokens.get("bingbot") != "": ua.Name = Bingbot ua.Version = tokens.get("bingbot") - ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari") + ua.Mobile = tokens.existsAny(Mobile, MobileSafari) - case tokens.get("YandexBot") != "": - ua.Name = "YandexBot" - ua.Version = tokens.get("YandexBot") - ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari") + case tokens.get(YandexBot) != "": + ua.Name = YandexBot + ua.Version = tokens.get(YandexBot) + ua.Mobile = tokens.existsAny(Mobile, MobileSafari) + ua.Bot = true + + case tokens.get(YandexAdNet) != "": + ua.Name = YandexAdNet + ua.Version = tokens.get(YandexAdNet) + ua.Mobile = tokens.existsAny(Mobile, MobileSafari) + ua.Bot = true case tokens.get("SamsungBrowser") != "": ua.Name = "Samsung Browser" ua.Version = tokens.get("SamsungBrowser") - ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari") + ua.Mobile = tokens.existsAny(Mobile, MobileSafari) case tokens.get("HeadlessChrome") != "": ua.Name = HeadlessChrome ua.Version = tokens.get("HeadlessChrome") - ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari") + ua.Mobile = tokens.existsAny(Mobile, MobileSafari) ua.Bot = true case tokens.existsAny("AdsBot-Google-Mobile", "Mediapartners-Google", "AdsBot-Google"): @@ -279,18 +297,18 @@ func Parse(userAgent string) UserAgent { case tokens.get("HuaweiBrowser") != "": ua.Name = "Huawei Browser" ua.Version = tokens.get("HuaweiBrowser") - ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari") + ua.Mobile = tokens.existsAny(Mobile, MobileSafari) - case tokens.exists("BlackBerry"): - ua.Name = "BlackBerry" - ua.Version = tokens.get("Version") + case tokens.exists(BlackBerry): + ua.Name = BlackBerry + ua.Version = tokens.get(Version) - case tokens.exists("NetFront"): - ua.Name = "NetFront" - ua.Version = tokens.get("NetFront") + case tokens.exists(NetFront): + ua.Name = NetFront + ua.Version = tokens.get(NetFront) ua.Mobile = true - // if chrome and Safari defined, find any other token sent descr + // if Chrome and Safari defined, find any other token sent descr case tokens.exists(Chrome) && tokens.exists(Safari): name := tokens.findBestMatch(true) if name != "" { @@ -300,30 +318,30 @@ func Parse(userAgent string) UserAgent { } fallthrough - case tokens.exists("Chrome"): + case tokens.exists(Chrome): ua.Name = Chrome - ua.Version = tokens.get("Chrome") - ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari") + ua.Version = tokens.get(Chrome) + ua.Mobile = tokens.existsAny(Mobile, MobileSafari) case tokens.exists("Brave Chrome"): ua.Name = Chrome ua.Version = tokens.get("Brave Chrome") - ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari") + ua.Mobile = tokens.existsAny(Mobile, MobileSafari) - case tokens.exists("Safari"): + case tokens.exists(Safari): ua.Name = Safari - v := tokens.get("Version") + v := tokens.get(Version) if v != "" { ua.Version = v } else { - ua.Version = tokens.get("Safari") + ua.Version = tokens.get(Safari) } - ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari") + ua.Mobile = tokens.existsAny(Mobile, MobileSafari) default: - if ua.OS == "Android" && tokens.get("Version") != "" { + if ua.IsAndroid() && tokens.get(Version) != "" { ua.Name = "Android browser" - ua.Version = tokens.get("Version") + ua.Version = tokens.get(Version) ua.Mobile = true } else { if name := tokens.findBestMatch(false); name != "" { @@ -335,7 +353,7 @@ func Parse(userAgent string) UserAgent { ua.Bot = strings.Contains(strings.ToLower(ua.Name), "bot") // If mobile flag has already been set, don't override it. if !ua.Mobile { - ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari") + ua.Mobile = tokens.existsAny(Mobile, MobileSafari) } } } @@ -349,7 +367,7 @@ func Parse(userAgent string) UserAgent { ua.Mobile = false } - // if not already bot, check some popular bots and wether URL is set + // if not already bot, check some popular bots and whether URL is set if !ua.Bot { ua.Bot = ua.URL != "" } @@ -384,7 +402,7 @@ func parse(userAgent string) properties { if val.Len() == 0 { // only if value don't exists var ver string - s, ver = checkVer(s) // determin version string and split + s, ver = checkVer(s) // determine version string and split clients.add(s, ver) } else { clients.add(s, strings.TrimSpace(val.String())) @@ -403,7 +421,6 @@ func parse(userAgent string) properties { bua := []byte(userAgent) for i, c := range bua { - //fmt.Println(string(c), c) switch { case c == 41: // ) addToken() @@ -434,7 +451,7 @@ func parse(userAgent string) properties { // If the following character is not a space, change to a space. buff.WriteByte(' ') } - // Otherwise don't write as its probably a badly formatted key value separator. + // Otherwise don't write as it's probably a badly formatted key value separator. case slash && c == 32: addToken() @@ -469,10 +486,8 @@ func checkVer(s string) (name, v string) { return s, "" } - //v = s[i+1:] - switch s[:i] { - case "Linux", "Windows NT", "Windows Phone OS", "MSIE", "Android": + case Linux, WindowsNT, WindowsPhoneOS, Msie, Android: return s[:i], s[i+1:] case "CrOS x86_64", "CrOS aarch64", "CrOS armv7l": j := strings.LastIndex(s[:i], " ") @@ -480,20 +495,12 @@ func checkVer(s string) (name, v string) { default: return s, "" } - - // for _, c := range v { - // if (c >= 48 && c <= 57) || c == 46 { - // } else { - // return s, "" - // } - // } - // return s[:i], s[i+1:] } -// ignore retursn true if token should be ignored +// ignore returns true if token should be ignored func ignore(s string) bool { switch s { - case "KHTML, like Gecko", "U", "compatible", "Mozilla", "WOW64", "en", "en-us", "en-gb", "ru-ru", "Browser": + case "KHTML, like Gecko", "U", "compatible", Mozilla, "WOW64", "en", "en-us", "en-gb", "ru-ru", "Browser": return true default: return false @@ -607,9 +614,9 @@ func (p properties) findBestMatch(withVerOnly bool) string { for i := 0; i < n; i++ { for _, prop := range p.list { switch prop.Key { - case Chrome, Firefox, Safari, "Version", "Mobile", "Mobile Safari", "Mozilla", "AppleWebKit", "Windows NT", "Windows Phone OS", Android, "Macintosh", Linux, "GSA", "CrOS", "Tablet": + case Chrome, Firefox, Safari, Version, Mobile, MobileSafari, Mozilla, "AppleWebKit", WindowsNT, WindowsPhoneOS, Android, "Macintosh", Linux, "GSA", CrOS, Tablet: default: - // don' pick if starts with number + // don't pick if starts with number if len(prop.Key) != 0 && prop.Key[0] >= 48 && prop.Key[0] <= 57 { break } @@ -641,15 +648,15 @@ func (p *properties) findAndroidDevice(startIndex int) string { if len(p.list) > i+1 { dev := p.list[i+1].Key if len(dev) == 2 || (len(dev) == 5 && dev[2] == '-') { - // probably langage tag (en-us etc..), ignore and continue loop + // probably language tag (en-us etc..), ignore and continue loop continue } switch dev { - case Chrome, Firefox, Safari, "Opera Mini", "Presto", "Version", "Mobile", "Mobile Safari", "Mozilla", "AppleWebKit", "Windows NT", "Windows Phone OS", Android, "Macintosh", Linux, "CrOS": - // ignore this tokens, not device names + case Chrome, Firefox, Safari, OperaMini, "Presto", Version, Mobile, MobileSafari, Mozilla, "AppleWebKit", WindowsNT, WindowsPhoneOS, Android, "Macintosh", Linux, CrOS: + // ignore these tokens, not device names default: - if strings.Contains(strings.ToLower(dev), "tablet") { - p.list[i+1].Key = "Tablet" // leave Tablet tag for later table detection + if strings.Contains(strings.ToLower(dev), strings.ToLower(Tablet)) { + p.list[i+1].Key = Tablet // leave Tablet tag for later table detection } else { p.list = append(p.list[:i+1], p.list[i+2:]...) } diff --git a/ua_test.go b/ua_test.go index fd88446..3a7e50a 100644 --- a/ua_test.go +++ b/ua_test.go @@ -90,6 +90,7 @@ var testTable = [][]string{ {"Mozilla/5.0 (compatible; Yahoo Ad monitoring; https://help.yahoo.com/kb/yahoo-ad-monitoring-SLN24857.html) cnv.aws-prod---sieve.hlfs-rest_client/1681346790-0", "Yahoo Ad monitoring", "", "bot", ""}, {"GoogleProber", "GoogleProber", "", "bot", ""}, {"GoogleProducer; (+http://goo.gl/7y4SX)", "GoogleProducer", "", "bot", ""}, + {"Mozilla/5.0 (compatible; YandexAdNet/1.0; +http://yandex.com/bots) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.268", "YandexAdNet", "1.0", "bot", ""}, // Google ads bots {"Mozilla/5.0 (Linux; Android 4.0.0; Galaxy Nexus Build/IMM76B) AppleWebKit/537.36 (KHTML, like Gecko; Mediapartners-Google) Chrome/104.0.0.0 Mobile Safari/537.36", ua.GoogleAdsBot, "", "bot", ua.Android}, From 8798139826233fa1947ea27c0018ba7ec28b9fc4 Mon Sep 17 00:00:00 2001 From: Mark Wolfe Date: Mon, 29 Apr 2024 11:34:20 +1000 Subject: [PATCH 02/16] Adding support for Bytespider fixes #26 More information here https://darkvisitors.com/agents/bytespider, not authoritive but gives some insights into possible role of this bot. --- ua.go | 7 +++++++ ua_test.go | 2 ++ 2 files changed, 9 insertions(+) diff --git a/ua.go b/ua.go index a292f9c..842e353 100644 --- a/ua.go +++ b/ua.go @@ -147,6 +147,13 @@ func Parse(userAgent string) UserAgent { } ua.Bot = true + case tokens.existsAny("Bytespider", "Bytespider"): + if name := tokens.findBestMatch(false); name != "" { + ua.Name = name + ua.OS = "" + } + ua.Bot = true + case tokens.exists("Applebot"): ua.Name = Applebot ua.Version = tokens.get(Applebot) diff --git a/ua_test.go b/ua_test.go index fd88446..085c823 100644 --- a/ua_test.go +++ b/ua_test.go @@ -90,6 +90,8 @@ var testTable = [][]string{ {"Mozilla/5.0 (compatible; Yahoo Ad monitoring; https://help.yahoo.com/kb/yahoo-ad-monitoring-SLN24857.html) cnv.aws-prod---sieve.hlfs-rest_client/1681346790-0", "Yahoo Ad monitoring", "", "bot", ""}, {"GoogleProber", "GoogleProber", "", "bot", ""}, {"GoogleProducer; (+http://goo.gl/7y4SX)", "GoogleProducer", "", "bot", ""}, + {"Mozilla/5.0 (compatible; Bytespider; spider-feedback@bytedance.com) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.0.0 Safari/537.36", "Bytespider", "", "bot", ""}, + {"Mozilla/5.0 (Linux; Android 5.0) AppleWebKit/537.36 (KHTML, like Gecko) Mobile Safari/537.36 (compatible; Bytespider; spider-feedback@bytedance.com)", "Bytespider", "", "bot", ""}, // Google ads bots {"Mozilla/5.0 (Linux; Android 4.0.0; Galaxy Nexus Build/IMM76B) AppleWebKit/537.36 (KHTML, like Gecko; Mediapartners-Google) Chrome/104.0.0.0 Mobile Safari/537.36", ua.GoogleAdsBot, "", "bot", ua.Android}, From 973f9e1cfbb73ccd70b4414c43bf6e3f5f273c13 Mon Sep 17 00:00:00 2001 From: liuwenlong Date: Mon, 22 Jul 2024 00:10:11 +0800 Subject: [PATCH 03/16] Support Harmony --- .idea/.gitignore | 8 ++++++++ .idea/codeStyles/codeStyleConfig.xml | 5 +++++ .idea/modules.xml | 8 ++++++++ .idea/useragent.iml | 9 +++++++++ .idea/vcs.xml | 6 ++++++ ua.go | 10 ++++++++-- ua_test.go | 5 +++++ 7 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/useragent.iml create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..a55e7a1 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..b85bfeb --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/useragent.iml b/.idea/useragent.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/useragent.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/ua.go b/ua.go index a292f9c..44b1a2d 100644 --- a/ua.go +++ b/ua.go @@ -34,6 +34,7 @@ const ( FreeBSD = "FreeBSD" ChromeOS = "ChromeOS" BlackBerry = "BlackBerry" + Harmony = "Harmony" Opera = "Opera" OperaMini = "Opera Mini" @@ -132,6 +133,11 @@ func Parse(userAgent string) UserAgent { ua.OS = BlackBerry ua.OSVersion = tokens.get("BlackBerry") ua.Mobile = true + + case tokens.exists("OpenHarmony"): + ua.OS = Harmony + ua.OSVersion = tokens.get("OpenHarmony") + ua.Mobile = true } switch { @@ -472,7 +478,7 @@ func checkVer(s string) (name, v string) { //v = s[i+1:] switch s[:i] { - case "Linux", "Windows NT", "Windows Phone OS", "MSIE", "Android": + case "Linux", "Windows NT", "Windows Phone OS", "MSIE", "Android", "OpenHarmony": return s[:i], s[i+1:] case "CrOS x86_64", "CrOS aarch64", "CrOS armv7l": j := strings.LastIndex(s[:i], " ") @@ -607,7 +613,7 @@ func (p properties) findBestMatch(withVerOnly bool) string { for i := 0; i < n; i++ { for _, prop := range p.list { switch prop.Key { - case Chrome, Firefox, Safari, "Version", "Mobile", "Mobile Safari", "Mozilla", "AppleWebKit", "Windows NT", "Windows Phone OS", Android, "Macintosh", Linux, "GSA", "CrOS", "Tablet": + case Chrome, Firefox, Safari, "Version", "Mobile", "Mobile Safari", "Mozilla", "AppleWebKit", "Windows NT", "Windows Phone OS", Android, "Macintosh", Linux, "GSA", "CrOS", "Tablet", "OpenHarmony": default: // don' pick if starts with number if len(prop.Key) != 0 && prop.Key[0] >= 48 && prop.Key[0] <= 57 { diff --git a/ua_test.go b/ua_test.go index fd88446..f514c01 100644 --- a/ua_test.go +++ b/ua_test.go @@ -153,6 +153,8 @@ var testTable = [][]string{ //{`${jndi:ldap://log4shell-generic-8ZnJfq2XFL3GWyaLyOpT${lower:ten}.w.nessus.org/nessus}`, "", "mobile", ua.Android}, // + {"Mozilla/5.0 (Phone; OpenHarmony 5.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 ArkWeb/4.1.6.1 Mobile", "ArkWeb", "4.1.6.1", "mobile", ua.Harmony, ""}, + // // ${jndi:ldap://log4shell-generic-8ZnJfq2XFL3GWyaLyOpT${lower:ten}.w.nessus.org/nessus} @@ -258,6 +260,9 @@ func ExampleParse() { "Mozilla/5.0 (Linux; U; Android 4.3; en-us; GT-I9300 Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", "Mozilla/5.0 (Linux; Android 6.0.1; SAMSUNG SM-A310F/A310FXXU2BQB1 Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/5.4 Chrome/51.0.2704.106 Mobile Safari/537.36", + + // Harmony + "Mozilla/5.0 (Phone; OpenHarmony 5.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 ArkWeb/4.1.6.1 Mobile", } for _, s := range userAgents { From b32824df66fc5f7a0dd44160430835e14b24b4d1 Mon Sep 17 00:00:00 2001 From: liuwenlong Date: Mon, 22 Jul 2024 00:23:37 +0800 Subject: [PATCH 04/16] Add .idea to .gitignore --- .gitignore | 1 + .idea/.gitignore | 8 -------- .idea/codeStyles/codeStyleConfig.xml | 5 ----- .idea/modules.xml | 8 -------- .idea/useragent.iml | 9 --------- .idea/vcs.xml | 6 ------ 6 files changed, 1 insertion(+), 36 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/codeStyles/codeStyleConfig.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/useragent.iml delete mode 100644 .idea/vcs.xml diff --git a/.gitignore b/.gitignore index 8078d86..b8960c6 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ user_agents.txt ua2.go settings.json benchmarks.txt +.idea \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b8..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index a55e7a1..0000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index b85bfeb..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/useragent.iml b/.idea/useragent.iml deleted file mode 100644 index 5e764c4..0000000 --- a/.idea/useragent.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 9b786e15fcde567ec2301a8a119f46543c39722b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milo=C5=A1=20Mileusni=C4=87?= Date: Thu, 12 Sep 2024 20:33:33 +0200 Subject: [PATCH 05/16] Update .gitignore --- .gitignore | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index b8960c6..9e68d87 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,10 @@ +.idea/ +.vscode/ +.DS_Store old.go old_test.go file.txt user_agents.txt ua2.go settings.json -benchmarks.txt -.idea \ No newline at end of file +benchmarks.txt \ No newline at end of file From cd25ea4b821b05aab7d66140ba163285cee05999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milo=C5=A1=20Mileusni=C4=87?= Date: Thu, 12 Sep 2024 20:47:05 +0200 Subject: [PATCH 06/16] Blackberry, Yandex --- is.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/is.go b/is.go index a3bf5b3..3fff5a4 100644 --- a/is.go +++ b/is.go @@ -30,6 +30,11 @@ func (ua UserAgent) IsChromeOS() bool { return ua.OS == ChromeOS || ua.OS == "CrOS" } +// IsBlackberryOS shorthand function to check if OS == BlackBerry +func (ua UserAgent) IsBlackberryOS() bool { + return ua.OS == BlackBerry +} + // IsOpera shorthand function to check if Name == Opera func (ua UserAgent) IsOpera() bool { return ua.Name == Opera @@ -65,6 +70,11 @@ func (ua UserAgent) IsEdge() bool { return ua.Name == Edge } +// IsBlackBerry shorthand function to check if Name == BlackBerry +func (ua UserAgent) IsBlackBerry() bool { + return ua.Name == BlackBerry +} + // IsGooglebot shorthand function to check if Name == Googlebot func (ua UserAgent) IsGooglebot() bool { return ua.Name == Googlebot @@ -80,6 +90,11 @@ func (ua UserAgent) IsFacebookbot() bool { return ua.Name == FacebookExternalHit } +// IsYandexbot shorthand function to check if Name == YandexBot +func (ua UserAgent) IsYandexbot() bool { + return ua.Name == YandexBot +} + // IsUnknown returns true if the package can't determine the user agent reliably. // Fields like Name, OS, etc. might still have values. func (ua UserAgent) IsUnknown() bool { From a6d0a6d8cf1e60a1b29f4360e08be354e20ebac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milo=C5=A1=20Mileusni=C4=87?= Date: Thu, 12 Sep 2024 20:53:20 +0200 Subject: [PATCH 07/16] Add getAny func --- ua.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ua.go b/ua.go index e1cde3b..7082073 100644 --- a/ua.go +++ b/ua.go @@ -572,6 +572,17 @@ func (p properties) existsAny(keys ...string) bool { return false } +func (p properties) getAny(keys ...string) (key, value string) { + for _, k := range keys { + for _, prop := range p.list { + if prop.Key == k { + return prop.Key, prop.Value + } + } + } + return "", "" +} + func (p properties) findMacOSVersion() string { for _, token := range p.list { if strings.Contains(token.Key, "OS") { From 9ddd585730f88f8fb0a39f47bcb39462f7c85e73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milo=C5=A1=20Mileusni=C4=87?= Date: Thu, 12 Sep 2024 22:50:49 +0200 Subject: [PATCH 08/16] Use values --- ua.go | 4 ++-- version.go | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/ua.go b/ua.go index 7082073..a348cc2 100644 --- a/ua.go +++ b/ua.go @@ -385,8 +385,8 @@ func Parse(userAgent string) UserAgent { } } - parseVersion(ua.Version, &ua.VersionNo) - parseVersion(ua.OSVersion, &ua.OSVersionNo) + ua.VersionNo = parseVersion(ua.Version) + ua.OSVersionNo = parseVersion(ua.OSVersion) return ua } diff --git a/version.go b/version.go index 30c0eb1..88f546c 100644 --- a/version.go +++ b/version.go @@ -12,24 +12,26 @@ type VersionNo struct { Patch int } -func parseVersion(ver string, verno *VersionNo) { +// parseVersion parse version string into Major.Minor.Patch struct +func parseVersion(ver string) (verno VersionNo) { var err error parts := strings.Split(ver, ".") if len(parts) > 0 { if verno.Major, err = strconv.Atoi(parts[0]); err != nil { - return + return verno } } if len(parts) > 1 { if verno.Minor, err = strconv.Atoi(parts[1]); err != nil { - return + return verno } if len(parts) > 2 { if verno.Patch, err = strconv.Atoi(parts[2]); err != nil { - return + return verno } } } + return verno } // VersionNoShort return version string in format . From 343162a9f5b01cc167b706e245719b257bf9694d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milo=C5=A1=20Mileusni=C4=87?= Date: Thu, 12 Sep 2024 22:52:01 +0200 Subject: [PATCH 09/16] Use tablet constant --- ua.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ua.go b/ua.go index a348cc2..70b98c2 100644 --- a/ua.go +++ b/ua.go @@ -70,6 +70,8 @@ const ( Version = "Version" Mobile = "Mobile" Tablet = "Tablet" + + tablet = "tablet" ) // Parse user agent string returning UserAgent struct @@ -95,7 +97,7 @@ func Parse(userAgent string) UserAgent { ua.OS = Android var osIndex int osIndex, ua.OSVersion = tokens.getIndexValue(Android) - ua.Tablet = strings.Contains(strings.ToLower(ua.String), strings.ToLower(Tablet)) + ua.Tablet = strings.Contains(strings.ToLower(ua.String), tablet) ua.Device = tokens.findAndroidDevice(osIndex) case tokens.exists("iPhone"): @@ -672,7 +674,7 @@ func (p *properties) findAndroidDevice(startIndex int) string { case Chrome, Firefox, Safari, OperaMini, "Presto", Version, Mobile, MobileSafari, Mozilla, "AppleWebKit", WindowsNT, WindowsPhoneOS, Android, "Macintosh", Linux, CrOS: // ignore these tokens, not device names default: - if strings.Contains(strings.ToLower(dev), strings.ToLower(Tablet)) { + if strings.Contains(strings.ToLower(dev), tablet) { p.list[i+1].Key = Tablet // leave Tablet tag for later table detection } else { p.list = append(p.list[:i+1], p.list[i+2:]...) From 704ef69fc782c68db08d437c68d5896e028f3301 Mon Sep 17 00:00:00 2001 From: Daniils Petrovs Date: Fri, 13 Sep 2024 00:10:48 +0300 Subject: [PATCH 10/16] Add better detection for Samsung Browser Closes Add Samsung Browser to be recognised as Android device #29 --- ua.go | 4 +++- ua_test.go | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ua.go b/ua.go index e1cde3b..424d25d 100644 --- a/ua.go +++ b/ua.go @@ -53,6 +53,7 @@ const ( NetFront = "NetFront" Mozilla = "Mozilla" Msie = "MSIE" + SamsungBrowser = "Samsung Browser" GoogleAdsBot = "Google Ads Bot" Googlebot = "Googlebot" @@ -256,9 +257,10 @@ func Parse(userAgent string) UserAgent { ua.Bot = true case tokens.get("SamsungBrowser") != "": - ua.Name = "Samsung Browser" + ua.Name = SamsungBrowser ua.Version = tokens.get("SamsungBrowser") ua.Mobile = tokens.existsAny(Mobile, MobileSafari) + ua.OS = Android case tokens.get("HeadlessChrome") != "": ua.Name = HeadlessChrome diff --git a/ua_test.go b/ua_test.go index f514c01..4d2aba7 100644 --- a/ua_test.go +++ b/ua_test.go @@ -54,6 +54,7 @@ var testTable = [][]string{ {"Mozilla/5.0 (Linux; U; Android 11; ru-ru; Redmi Note 10S Build/RP1A.200720.011) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/89.0.4389.116 Mobile Safari/537.36 XiaoMi/MiuiBrowser/12.13.2-gn", "Miui Browser", "12.13.2-gn", "mobile", ua.Android, "Redmi Note 10S"}, {"Mozilla/5.0 (Linux; Android 10; MED-LX9N; HMSCore 6.6.0.311) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.105 HuaweiBrowser/12.1.0.303 Mobile Safari/537.36", "Huawei Browser", "12.1.0.303", "mobile", "Android"}, + {"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/22.0 Chrome/111.0.5563.116 Safari/537.36", ua.SamsungBrowser, "22.0", "mobile", ua.Android}, // useragent, name, version, mobile, os {"Mozilla/5.0 (Linux; Android 9; ONEPLUS A6003) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.99 Mobile Safari/537.36", ua.Chrome, "71.0.3578.99", "mobile", ua.Android}, From 13c98cc313c7f0ff530f9d71dc2501a2be99e9e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milo=C5=A1=20Mileusni=C4=87?= Date: Fri, 13 Sep 2024 02:07:22 +0200 Subject: [PATCH 11/16] Refactoring --- version.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/version.go b/version.go index 88f546c..1a487d5 100644 --- a/version.go +++ b/version.go @@ -20,15 +20,15 @@ func parseVersion(ver string) (verno VersionNo) { if verno.Major, err = strconv.Atoi(parts[0]); err != nil { return verno } - } - if len(parts) > 1 { - if verno.Minor, err = strconv.Atoi(parts[1]); err != nil { - return verno - } - if len(parts) > 2 { - if verno.Patch, err = strconv.Atoi(parts[2]); err != nil { + if len(parts) > 1 { + if verno.Minor, err = strconv.Atoi(parts[1]); err != nil { return verno } + if len(parts) > 2 { + if verno.Patch, err = strconv.Atoi(parts[2]); err != nil { + return verno + } + } } } return verno From 30a76164486cb0f3f6c2cc1f2fa58594ac6eea76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milo=C5=A1=20Mileusni=C4=87?= Date: Fri, 13 Sep 2024 02:08:20 +0200 Subject: [PATCH 12/16] Performance improvements --- ua.go | 60 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/ua.go b/ua.go index 70b98c2..e4267f9 100644 --- a/ua.go +++ b/ua.go @@ -81,15 +81,7 @@ func Parse(userAgent string) UserAgent { } tokens := parse(userAgent) - - // check is there URL - for i, token := range tokens.list { - if strings.HasPrefix(token.Key, "http://") || strings.HasPrefix(token.Key, "https://") { - ua.URL = token.Key - tokens.list = append(tokens.list[:i], tokens.list[i+1:]...) - break - } - } + ua.URL = tokens.url // OS lookup switch { @@ -393,32 +385,42 @@ func Parse(userAgent string) UserAgent { return ua } +// var buffPool = sync.Pool{New: func() interface{} { +// return bytes.NewBuffer(make([]byte, 0, 30)) +// }} + func parse(userAgent string) properties { clients := properties{ list: make([]property, 0, 8), } slash := false isURL := false - var buff, val bytes.Buffer + // buff := buffPool.Get().(*bytes.Buffer) + // val := buffPool.Get().(*bytes.Buffer) + // buff.Reset() + // val.Reset() + + buff := bytes.NewBuffer(make([]byte, 0, 30)) + val := bytes.NewBuffer(make([]byte, 0, 30)) + addToken := func() { if buff.Len() != 0 { - s := strings.TrimSpace(buff.String()) + s := string(bytes.TrimSpace(buff.Bytes())) if !ignore(s) { if isURL { - s = strings.TrimPrefix(s, "+") + clients.url = strings.TrimPrefix(s, "+") + return } - - if val.Len() == 0 { // only if value don't exists - var ver string - s, ver = checkVer(s) // determine version string and split - clients.add(s, ver) + if val.Len() == 0 { + // if value don't exists, try to get version from the token + clients.list = append(clients.list, checkVer(s)) } else { - clients.add(s, strings.TrimSpace(val.String())) + clients.list = append(clients.list, property{Key: s, Value: string(bytes.TrimSpace(val.Bytes()))}) } } } - buff.Reset() - val.Reset() + // buff.Reset() + // val.Reset() slash = false isURL = false } @@ -428,7 +430,6 @@ func parse(userAgent string) properties { bua := []byte(userAgent) for i, c := range bua { - switch { case c == 41: // ) addToken() @@ -485,23 +486,25 @@ func parse(userAgent string) properties { } addToken() + // buffPool.Put(buff) + // buffPool.Put(val) return clients } -func checkVer(s string) (name, v string) { +func checkVer(s string) property { i := strings.LastIndex(s, " ") if i == -1 { - return s, "" + return property{Key: s, Value: ""} } switch s[:i] { case Linux, WindowsNT, WindowsPhoneOS, Msie, Android, "OpenHarmony": - return s[:i], s[i+1:] + return property{Key: s[:i], Value: s[i+1:]} case "CrOS x86_64", "CrOS aarch64", "CrOS armv7l": j := strings.LastIndex(s[:i], " ") - return s[:j], s[j+1 : i] + return property{Key: s[:j], Value: s[j+1 : i]} default: - return s, "" + return property{Key: s, Value: ""} } } @@ -521,10 +524,7 @@ type property struct { } type properties struct { list []property -} - -func (p *properties) add(key, value string) { - p.list = append(p.list, property{Key: key, Value: value}) + url string } func (p properties) get(key string) string { From df8f29e87801b4ea58c18e2251d8f8fc879af97d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milo=C5=A1=20Mileusni=C4=87?= Date: Fri, 13 Sep 2024 02:22:34 +0200 Subject: [PATCH 13/16] Move func --- ua_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ua_test.go b/ua_test.go index f514c01..18a395a 100644 --- a/ua_test.go +++ b/ua_test.go @@ -212,11 +212,6 @@ func TestParse(t *testing.T) { } } -func TestSingle(t *testing.T) { - agent := ua.Parse("SonyEricssonK310iv/R4DA Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1 UP.Link/6.3.1.13.0") - fmt.Printf("\n%+v\n", agent) -} - var testUA ua.UserAgent func BenchmarkUserAgent(b *testing.B) { @@ -227,6 +222,11 @@ func BenchmarkUserAgent(b *testing.B) { } } +func TestSingle(t *testing.T) { + agent := ua.Parse("SonyEricssonK310iv/R4DA Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1 UP.Link/6.3.1.13.0") + fmt.Printf("\n%+v\n", agent) +} + func ExampleParse() { userAgents := []string{ // Mac From 5331aef4b5b414f49f80fde9b56e6fb829c30d1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milo=C5=A1=20Mileusni=C4=87?= Date: Fri, 13 Sep 2024 02:22:47 +0200 Subject: [PATCH 14/16] Bug-fix --- ua.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ua.go b/ua.go index e4267f9..f343d18 100644 --- a/ua.go +++ b/ua.go @@ -419,8 +419,8 @@ func parse(userAgent string) properties { } } } - // buff.Reset() - // val.Reset() + buff.Reset() + val.Reset() slash = false isURL = false } From af0d3cbc18c5cf21d79351077fc163fbacdcc214 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milo=C5=A1=20Mileusni=C4=87?= Date: Fri, 13 Sep 2024 02:37:02 +0200 Subject: [PATCH 15/16] Refactoring --- ua.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ua.go b/ua.go index f343d18..000812c 100644 --- a/ua.go +++ b/ua.go @@ -80,7 +80,7 @@ func Parse(userAgent string) UserAgent { String: userAgent, } - tokens := parse(userAgent) + tokens := parse([]byte(userAgent)) ua.URL = tokens.url // OS lookup @@ -389,7 +389,7 @@ func Parse(userAgent string) UserAgent { // return bytes.NewBuffer(make([]byte, 0, 30)) // }} -func parse(userAgent string) properties { +func parse(userAgent []byte) properties { clients := properties{ list: make([]property, 0, 8), } @@ -428,8 +428,7 @@ func parse(userAgent string) properties { parOpen := false braOpen := false - bua := []byte(userAgent) - for i, c := range bua { + for i, c := range userAgent { switch { case c == 41: // ) addToken() @@ -456,7 +455,7 @@ func parse(userAgent string) properties { if bytes.HasSuffix(buff.Bytes(), []byte("http")) || bytes.HasSuffix(buff.Bytes(), []byte("https")) { // If we are part of a URL just write the character. buff.WriteByte(c) - } else if i != len(bua)-1 && bua[i+1] != ' ' { + } else if i != len(userAgent)-1 && userAgent[i+1] != ' ' { // If the following character is not a space, change to a space. buff.WriteByte(' ') } @@ -469,7 +468,7 @@ func parse(userAgent string) properties { val.WriteByte(c) case c == 47 && !isURL: // / - if i != len(bua)-1 && bua[i+1] == 47 && (bytes.HasSuffix(buff.Bytes(), []byte("http:")) || bytes.HasSuffix(buff.Bytes(), []byte("https:"))) { + if i != len(userAgent)-1 && userAgent[i+1] == 47 && (bytes.HasSuffix(buff.Bytes(), []byte("http:")) || bytes.HasSuffix(buff.Bytes(), []byte("https:"))) { buff.WriteByte(c) isURL = true } else { From 8bf5b3ac2462bfc85897fa8c1afcf81d90eda947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milo=C5=A1=20Mileusni=C4=87?= Date: Fri, 13 Sep 2024 03:04:44 +0200 Subject: [PATCH 16/16] Bytespider fix --- ua.go | 8 +++----- ua_test.go | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/ua.go b/ua.go index b7883e3..4769b81 100644 --- a/ua.go +++ b/ua.go @@ -159,11 +159,9 @@ func Parse(userAgent string) UserAgent { } ua.Bot = true - case tokens.existsAny("Bytespider", "Bytespider"): - if name := tokens.findBestMatch(false); name != "" { - ua.Name = name - ua.OS = "" - } + case tokens.exists("Bytespider"): + ua.Name = "Bytespider" + ua.Mobile = tokens.exists("Mobile Safari") ua.Bot = true case tokens.exists(Applebot): diff --git a/ua_test.go b/ua_test.go index df51ef4..2f1cec8 100644 --- a/ua_test.go +++ b/ua_test.go @@ -92,7 +92,7 @@ var testTable = [][]string{ {"GoogleProber", "GoogleProber", "", "bot", ""}, {"GoogleProducer; (+http://goo.gl/7y4SX)", "GoogleProducer", "", "bot", ""}, {"Mozilla/5.0 (compatible; Bytespider; spider-feedback@bytedance.com) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.0.0 Safari/537.36", "Bytespider", "", "bot", ""}, - {"Mozilla/5.0 (Linux; Android 5.0) AppleWebKit/537.36 (KHTML, like Gecko) Mobile Safari/537.36 (compatible; Bytespider; spider-feedback@bytedance.com)", "Bytespider", "", "bot", ""}, + {"Mozilla/5.0 (Linux; Android 5.0) AppleWebKit/537.36 (KHTML, like Gecko) Mobile Safari/537.36 (compatible; Bytespider; spider-feedback@bytedance.com)", "Bytespider", "", "bot", ua.Android}, // Google ads bots {"Mozilla/5.0 (Linux; Android 4.0.0; Galaxy Nexus Build/IMM76B) AppleWebKit/537.36 (KHTML, like Gecko; Mediapartners-Google) Chrome/104.0.0.0 Mobile Safari/537.36", ua.GoogleAdsBot, "", "bot", ua.Android},