-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathuseragent.go
158 lines (141 loc) · 3.06 KB
/
useragent.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package useragent
import (
"bytes"
"strings"
)
// UserAgent ...
type UserAgent struct {
Browser Browser
OS Os
Device Device
clients map[string]string
uaString string
}
var ignore = map[string]struct{}{
"KHTML, like Gecko": struct{}{},
"U": struct{}{},
"compatible": struct{}{},
"WOW64": struct{}{},
}
func checkVer(s string) (name, v string) {
i := strings.LastIndex(s, " ")
if i == -1 {
return s, ""
}
switch s[:i] {
case "Linux", "Windows NT", "Windows Phone OS", "MSIE", "Android":
return s[:i], s[i+1:]
default:
return s, ""
}
}
func NewUAParser(ua string) *UserAgent {
return &UserAgent{
uaString: ua,
}
}
func (ua *UserAgent) Parse() {
clients := make(map[string]string, 0)
slash := false
isURL := false
var buff, val bytes.Buffer
addToken := func() {
if buff.Len() != 0 {
s := strings.TrimSpace(buff.String())
if _, ign := ignore[s]; !ign {
if isURL {
s = strings.TrimPrefix(s, "+")
}
if val.Len() == 0 { // only if value don't exists
var ver string
s, ver = checkVer(s) // determin version string and split
clients[s] = ver
} else {
clients[s] = strings.TrimSpace(val.String())
}
}
}
buff.Reset()
val.Reset()
slash = false
isURL = false
}
parOpen := false
bua := []byte(ua.uaString)
for i, c := range bua {
switch {
case c == 41: // )
addToken()
parOpen = false
case parOpen && c == 59: // ;
addToken()
case c == 40: // (
addToken()
parOpen = true
case slash && c == 32:
addToken()
case slash:
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:"))) {
buff.WriteByte(c)
isURL = true
} else {
slash = true
}
// case c == 32:
// addToken()
default:
buff.WriteByte(c)
}
}
addToken()
ua.clients = clients
ua.postParseClients()
ua.guessBrowser()
ua.guessOS()
ua.guessDevice()
}
func (ua *UserAgent) postParseClients() {
for key, value := range ua.clients {
newKey, newValue := ua.postParseToken(key) //ignoring value)
if newKey != "" {
ua.clients[strings.ToLower(strings.TrimSpace(newKey))] = strings.ToLower(newValue)
}
ua.clients[strings.ToLower(key)] = strings.ToLower(value)
}
}
func (ua *UserAgent) postParseToken(token string) (k, v string) {
separatorIndex := 0
strBytes := []byte(token)
for i := 0; i < len(strBytes)-1; i++ {
// ignore freeBSD/icecat
if isCharOrCapital(strBytes[i]) && !isCharOrCapital(strBytes[i+1]) {
separatorIndex = i
break
}
}
if separatorIndex != 0 {
return token[0:separatorIndex], token[separatorIndex+1 : len(token)]
}
return "", ""
}
func (ua *UserAgent) exists(token string) bool {
if _, ok := ua.clients[token]; !ok {
return false
}
return true
}
func (ua *UserAgent) getValue(token string) string {
val, _ := ua.clients[token]
return val
}
func isCharOrCapital(c byte) bool {
if c <= '9' && c >= '0' {
return false
}
if c <= 'Z' && c >= 'A' {
return false
}
return true
}