mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-05-08 04:20:59 -05:00
Bump reva deps (#8412)
* bump dependencies Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * bump reva and add config options Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> --------- Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
This commit is contained in:
committed by
GitHub
parent
c92ebf4b46
commit
5ed57cc09a
+3
@@ -2,3 +2,6 @@ old.go
|
||||
old_test.go
|
||||
file.txt
|
||||
user_agents.txt
|
||||
ua2.go
|
||||
settings.json
|
||||
benchmarks.txt
|
||||
|
||||
+2
-2
@@ -9,9 +9,9 @@ Use `useragent.Parse(userAgent string)` function to parse browser's and bot's us
|
||||
|
||||
## Status
|
||||
|
||||
Still need some work on detecting Andorid device names.
|
||||
Stable. I use it on high traffic websites on every single request, as well on my [Lite Analytics](https://liteanalytics.com/) service.
|
||||
|
||||
Fill free to report an issue for any User-Agent string not recognized or misinterpreted.
|
||||
I constantly improve user agents detection and performance. Fill free to report an issue for any User-Agent string not recognized or misinterpreted.
|
||||
|
||||
## Installation <a id="installation"></a>
|
||||
```
|
||||
|
||||
+6
@@ -79,3 +79,9 @@ func (ua UserAgent) IsTwitterbot() bool {
|
||||
func (ua UserAgent) IsFacebookbot() bool {
|
||||
return ua.Name == FacebookExternalHit
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return !ua.Mobile && !ua.Tablet && !ua.Desktop && !ua.Bot
|
||||
}
|
||||
|
||||
+194
-40
@@ -8,25 +8,19 @@ import (
|
||||
|
||||
// UserAgent struct containing all data extracted from parsed user-agent string
|
||||
type UserAgent struct {
|
||||
Name string
|
||||
Version string
|
||||
OS string
|
||||
OSVersion string
|
||||
Device string
|
||||
Mobile bool
|
||||
Tablet bool
|
||||
Desktop bool
|
||||
Bot bool
|
||||
URL string
|
||||
String string
|
||||
}
|
||||
|
||||
var ignore = map[string]struct{}{
|
||||
"KHTML, like Gecko": {},
|
||||
"U": {},
|
||||
"compatible": {},
|
||||
"Mozilla": {},
|
||||
"WOW64": {},
|
||||
VersionNo VersionNo
|
||||
OSVersionNo VersionNo
|
||||
URL string
|
||||
String string
|
||||
Name string
|
||||
Version string
|
||||
OS string
|
||||
OSVersion string
|
||||
Device string
|
||||
Mobile bool
|
||||
Tablet bool
|
||||
Desktop bool
|
||||
Bot bool
|
||||
}
|
||||
|
||||
// Constants for browsers and operating systems for easier comparison
|
||||
@@ -37,7 +31,9 @@ const (
|
||||
MacOS = "macOS"
|
||||
IOS = "iOS"
|
||||
Linux = "Linux"
|
||||
FreeBSD = "FreeBSD"
|
||||
ChromeOS = "ChromeOS"
|
||||
BlackBerry = "BlackBerry"
|
||||
|
||||
Opera = "Opera"
|
||||
OperaMini = "Opera Mini"
|
||||
@@ -56,6 +52,10 @@ const (
|
||||
FacebookExternalHit = "facebookexternalhit"
|
||||
Applebot = "Applebot"
|
||||
Bingbot = "Bingbot"
|
||||
|
||||
FacebookApp = "Facebook App"
|
||||
InstagramApp = "Instagram App"
|
||||
TiktokApp = "TikTok App"
|
||||
)
|
||||
|
||||
// Parse user agent string returning UserAgent struct
|
||||
@@ -75,18 +75,16 @@ func Parse(userAgent string) UserAgent {
|
||||
}
|
||||
}
|
||||
|
||||
//fmt.Printf("%+v\n", tokens)
|
||||
|
||||
// OS lookup
|
||||
switch {
|
||||
case tokens.exists("Android"):
|
||||
ua.OS = Android
|
||||
ua.OSVersion = tokens.get(Android)
|
||||
for _, token := range tokens.list {
|
||||
s := token.Key
|
||||
if strings.HasSuffix(s, "Build") {
|
||||
ua.Device = strings.TrimSpace(s[:len(s)-5])
|
||||
ua.Tablet = strings.Contains(strings.ToLower(ua.Device), "tablet")
|
||||
}
|
||||
}
|
||||
var osIndex int
|
||||
osIndex, ua.OSVersion = tokens.getIndexValue(Android)
|
||||
ua.Tablet = strings.Contains(strings.ToLower(ua.String), "tablet")
|
||||
ua.Device = tokens.findAndroidDevice(osIndex)
|
||||
|
||||
case tokens.exists("iPhone"):
|
||||
ua.OS = IOS
|
||||
@@ -120,24 +118,35 @@ func Parse(userAgent string) UserAgent {
|
||||
ua.OSVersion = tokens.get(Linux)
|
||||
ua.Desktop = true
|
||||
|
||||
case tokens.exists("FreeBSD"):
|
||||
ua.OS = FreeBSD
|
||||
ua.OSVersion = tokens.get(FreeBSD)
|
||||
ua.Desktop = true
|
||||
|
||||
case tokens.exists("CrOS"):
|
||||
ua.OS = ChromeOS
|
||||
ua.OSVersion = tokens.get("CrOS")
|
||||
ua.Desktop = true
|
||||
|
||||
case tokens.exists("BlackBerry"):
|
||||
ua.OS = BlackBerry
|
||||
ua.OSVersion = tokens.get("BlackBerry")
|
||||
ua.Mobile = true
|
||||
}
|
||||
|
||||
// for s, val := range sys {
|
||||
// fmt.Println(s, "--", val)
|
||||
// }
|
||||
|
||||
switch {
|
||||
|
||||
case tokens.exists("Googlebot"):
|
||||
ua.Name = Googlebot
|
||||
ua.Version = tokens.get(Googlebot)
|
||||
ua.Bot = true
|
||||
ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari")
|
||||
|
||||
case tokens.existsAny("GoogleProber", "GoogleProducer"):
|
||||
if name := tokens.findBestMatch(false); name != "" {
|
||||
ua.Name = name
|
||||
}
|
||||
ua.Bot = true
|
||||
|
||||
case tokens.exists("Applebot"):
|
||||
ua.Name = Applebot
|
||||
ua.Version = tokens.get(Applebot)
|
||||
@@ -233,11 +242,16 @@ func Parse(userAgent string) UserAgent {
|
||||
ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari")
|
||||
ua.Bot = true
|
||||
|
||||
case tokens.exists("AdsBot-Google-Mobile") || tokens.exists("Mediapartners-Google") || tokens.exists("AdsBot-Google"):
|
||||
case tokens.existsAny("AdsBot-Google-Mobile", "Mediapartners-Google", "AdsBot-Google"):
|
||||
ua.Name = GoogleAdsBot
|
||||
ua.Bot = true
|
||||
ua.Mobile = ua.IsAndroid() || ua.IsIOS()
|
||||
|
||||
case tokens.exists("Yahoo Ad monitoring"):
|
||||
ua.Name = "Yahoo Ad monitoring"
|
||||
ua.Bot = true
|
||||
ua.Mobile = ua.IsAndroid() || ua.IsIOS()
|
||||
|
||||
case tokens.exists("XiaoMi"):
|
||||
miui := tokens.get("XiaoMi")
|
||||
if strings.HasPrefix(miui, "MiuiBrowser") {
|
||||
@@ -246,11 +260,36 @@ func Parse(userAgent string) UserAgent {
|
||||
ua.Mobile = true
|
||||
}
|
||||
|
||||
case tokens.exists("FBAN"):
|
||||
ua.Name = FacebookApp
|
||||
ua.Version = tokens.get("FBAN")
|
||||
|
||||
case tokens.exists("FB_IAB"):
|
||||
ua.Name = FacebookApp
|
||||
ua.Version = tokens.get("FBAV")
|
||||
|
||||
case tokens.startsWith("Instagram"):
|
||||
ua.Name = InstagramApp
|
||||
ua.Version = tokens.findInstagramVersion()
|
||||
|
||||
case tokens.exists("BytedanceWebview"):
|
||||
ua.Name = TiktokApp
|
||||
ua.Version = tokens.get("app_version")
|
||||
|
||||
case tokens.get("HuaweiBrowser") != "":
|
||||
ua.Name = "Huawei Browser"
|
||||
ua.Version = tokens.get("HuaweiBrowser")
|
||||
ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari")
|
||||
|
||||
case tokens.exists("BlackBerry"):
|
||||
ua.Name = "BlackBerry"
|
||||
ua.Version = tokens.get("Version")
|
||||
|
||||
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
|
||||
case tokens.exists(Chrome) && tokens.exists(Safari):
|
||||
name := tokens.findBestMatch(true)
|
||||
@@ -294,16 +333,23 @@ func Parse(userAgent string) UserAgent {
|
||||
ua.Name = ua.String
|
||||
}
|
||||
ua.Bot = strings.Contains(strings.ToLower(ua.Name), "bot")
|
||||
ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari")
|
||||
// If mobile flag has already been set, don't override it.
|
||||
if !ua.Mobile {
|
||||
ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ua.IsAndroid() {
|
||||
ua.Mobile = true
|
||||
}
|
||||
|
||||
// if tablet, switch mobile to off
|
||||
if ua.Tablet {
|
||||
ua.Mobile = false
|
||||
}
|
||||
|
||||
// if not already bot, check some popular bots and weather URL is set
|
||||
// if not already bot, check some popular bots and wether URL is set
|
||||
if !ua.Bot {
|
||||
ua.Bot = ua.URL != ""
|
||||
}
|
||||
@@ -315,6 +361,9 @@ func Parse(userAgent string) UserAgent {
|
||||
}
|
||||
}
|
||||
|
||||
parseVersion(ua.Version, &ua.VersionNo)
|
||||
parseVersion(ua.OSVersion, &ua.OSVersionNo)
|
||||
|
||||
return ua
|
||||
}
|
||||
|
||||
@@ -328,7 +377,7 @@ func parse(userAgent string) properties {
|
||||
addToken := func() {
|
||||
if buff.Len() != 0 {
|
||||
s := strings.TrimSpace(buff.String())
|
||||
if _, ign := ignore[s]; !ign {
|
||||
if !ignore(s) {
|
||||
if isURL {
|
||||
s = strings.TrimPrefix(s, "+")
|
||||
}
|
||||
@@ -349,6 +398,7 @@ func parse(userAgent string) properties {
|
||||
}
|
||||
|
||||
parOpen := false
|
||||
braOpen := false
|
||||
|
||||
bua := []byte(userAgent)
|
||||
for i, c := range bua {
|
||||
@@ -359,13 +409,33 @@ func parse(userAgent string) properties {
|
||||
addToken()
|
||||
parOpen = false
|
||||
|
||||
case parOpen && c == 59: // ;
|
||||
case (parOpen || braOpen) && c == 59: // ;
|
||||
addToken()
|
||||
|
||||
case c == 59: // ;
|
||||
addToken()
|
||||
|
||||
case c == 40: // (
|
||||
addToken()
|
||||
parOpen = true
|
||||
|
||||
case c == 91: // [
|
||||
addToken()
|
||||
braOpen = true
|
||||
case c == 93: // ]
|
||||
addToken()
|
||||
braOpen = false
|
||||
|
||||
case c == 58: // :
|
||||
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] != ' ' {
|
||||
// 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.
|
||||
|
||||
case slash && c == 32:
|
||||
addToken()
|
||||
|
||||
@@ -377,7 +447,11 @@ func parse(userAgent string) properties {
|
||||
buff.WriteByte(c)
|
||||
isURL = true
|
||||
} else {
|
||||
slash = true
|
||||
if ignore(buff.String()) {
|
||||
buff.Reset()
|
||||
} else {
|
||||
slash = true
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
@@ -400,7 +474,7 @@ func checkVer(s string) (name, v string) {
|
||||
switch s[:i] {
|
||||
case "Linux", "Windows NT", "Windows Phone OS", "MSIE", "Android":
|
||||
return s[:i], s[i+1:]
|
||||
case "CrOS x86_64", "CrOS aarch64":
|
||||
case "CrOS x86_64", "CrOS aarch64", "CrOS armv7l":
|
||||
j := strings.LastIndex(s[:i], " ")
|
||||
return s[:j], s[j+1 : i]
|
||||
default:
|
||||
@@ -416,6 +490,16 @@ func checkVer(s string) (name, v string) {
|
||||
// return s[:i], s[i+1:]
|
||||
}
|
||||
|
||||
// ignore retursn 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":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
type property struct {
|
||||
Key string
|
||||
Value string
|
||||
@@ -437,6 +521,15 @@ func (p properties) get(key string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p properties) getIndexValue(key string) (int, string) {
|
||||
for i, prop := range p.list {
|
||||
if prop.Key == key {
|
||||
return i, prop.Value
|
||||
}
|
||||
}
|
||||
return -1, ""
|
||||
}
|
||||
|
||||
func (p properties) exists(key string) bool {
|
||||
for _, prop := range p.list {
|
||||
if prop.Key == key {
|
||||
@@ -446,6 +539,15 @@ func (p properties) exists(key string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// func (p properties) existsIgnoreCase(key string) bool {
|
||||
// for _, prop := range p.list {
|
||||
// if strings.EqualFold(prop.Key, key) {
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
// return false
|
||||
// }
|
||||
|
||||
func (p properties) existsAny(keys ...string) bool {
|
||||
for _, k := range keys {
|
||||
for _, prop := range p.list {
|
||||
@@ -471,6 +573,29 @@ func (p properties) findMacOSVersion() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p properties) startsWith(value string) bool {
|
||||
for _, prop := range p.list {
|
||||
if strings.HasPrefix(prop.Key, value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p properties) findInstagramVersion() string {
|
||||
for _, token := range p.list {
|
||||
if strings.HasPrefix(token.Key, "Instagram") {
|
||||
if ver := findVersion(token.Value); ver != "" {
|
||||
return ver
|
||||
} else if ver = findVersion(token.Key); ver != "" {
|
||||
return ver
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// findBestMatch from the rest of the bunch
|
||||
// in first cycle only return key with version value
|
||||
// if withVerValue is false, do another cycle and return any token
|
||||
@@ -482,8 +607,12 @@ 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":
|
||||
case Chrome, Firefox, Safari, "Version", "Mobile", "Mobile Safari", "Mozilla", "AppleWebKit", "Windows NT", "Windows Phone OS", Android, "Macintosh", Linux, "GSA", "CrOS", "Tablet":
|
||||
default:
|
||||
// don' pick if starts with number
|
||||
if len(prop.Key) != 0 && prop.Key[0] >= 48 && prop.Key[0] <= 57 {
|
||||
break
|
||||
}
|
||||
if i == 0 {
|
||||
if prop.Value != "" { // in first check, only return keys with value
|
||||
return prop.Key
|
||||
@@ -505,3 +634,28 @@ func findVersion(s string) string {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// findAndroidDevice in tokens
|
||||
func (p *properties) findAndroidDevice(startIndex int) string {
|
||||
for i := startIndex; i < startIndex+1; i++ {
|
||||
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
|
||||
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
|
||||
default:
|
||||
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:]...)
|
||||
}
|
||||
return strings.TrimSpace(strings.TrimSuffix(dev, "Build"))
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
package useragent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type VersionNo struct {
|
||||
Major int
|
||||
Minor int
|
||||
Patch int
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
if len(parts) > 1 {
|
||||
if verno.Minor, err = strconv.Atoi(parts[1]); err != nil {
|
||||
return
|
||||
}
|
||||
if len(parts) > 2 {
|
||||
if verno.Patch, err = strconv.Atoi(parts[2]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// VersionNoShort return version string in format <Major>.<Minor>
|
||||
func (ua UserAgent) VersionNoShort() string {
|
||||
if ua.VersionNo.Major == 0 && ua.VersionNo.Minor == 0 && ua.VersionNo.Patch == 0 {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%d.%d", ua.VersionNo.Major, ua.VersionNo.Minor)
|
||||
}
|
||||
|
||||
// VersionNoFull returns version string in format <Major>.<Minor>.<Patch>
|
||||
func (ua UserAgent) VersionNoFull() string {
|
||||
if ua.VersionNo.Major == 0 && ua.VersionNo.Minor == 0 && ua.VersionNo.Patch == 0 {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%d.%d.%d", ua.VersionNo.Major, ua.VersionNo.Minor, ua.VersionNo.Patch)
|
||||
}
|
||||
|
||||
// OSVersionNoShort returns OS version string in format <Major>.<Minor>
|
||||
func (ua UserAgent) OSVersionNoShort() string {
|
||||
if ua.OSVersionNo.Major == 0 && ua.OSVersionNo.Minor == 0 && ua.OSVersionNo.Patch == 0 {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%d.%d", ua.OSVersionNo.Major, ua.OSVersionNo.Minor)
|
||||
}
|
||||
|
||||
// OSVersionNoFull returns OS version string in format <Major>.<Minor>.<Patch>
|
||||
func (ua UserAgent) OSVersionNoFull() string {
|
||||
if ua.OSVersionNo.Major == 0 && ua.OSVersionNo.Minor == 0 && ua.OSVersionNo.Patch == 0 {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%d.%d.%d", ua.OSVersionNo.Major, ua.OSVersionNo.Minor, ua.OSVersionNo.Patch)
|
||||
}
|
||||
Reference in New Issue
Block a user