line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Mobile::UserAgent;
|
2
|
|
|
|
|
|
|
#
|
3
|
|
|
|
|
|
|
# Copyright (C) 2005 Craig Manley. All rights reserved.
|
4
|
|
|
|
|
|
|
#
|
5
|
|
|
|
|
|
|
# This library is free software; you can redistribute it and/or modify
|
6
|
|
|
|
|
|
|
# it under the same terms as Perl itself. There is NO warranty; not even for
|
7
|
|
|
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
8
|
|
|
|
|
|
|
#
|
9
|
|
|
|
|
|
|
# $Id: UserAgent.pm,v 1.5 2005/09/25 14:29:18 cmanley Exp $
|
10
|
|
|
|
|
|
|
#
|
11
|
3
|
|
|
3
|
|
35016
|
use strict;
|
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
135
|
|
12
|
3
|
|
|
3
|
|
17
|
use Carp;
|
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
27843
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our $VERSION = sprintf "%d.%02d", q$Revision: 1.5 $ =~ m/ (\d+) \. (\d+) /xg;
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
# Contructor
|
19
|
|
|
|
|
|
|
# Parameter: optional user-agent string
|
20
|
|
|
|
|
|
|
sub new {
|
21
|
1991
|
|
|
1991
|
1
|
1304952
|
my $proto = shift;
|
22
|
1991
|
|
33
|
|
|
8983
|
my $useragent = shift || $ENV{'HTTP_USER_AGENT'};
|
23
|
1991
|
50
|
|
|
|
17847
|
unless(defined($useragent)) {
|
24
|
0
|
|
|
|
|
0
|
croak("Environment variable HTTP_USER_AGENT is missing!\n");
|
25
|
|
|
|
|
|
|
}
|
26
|
1991
|
|
33
|
|
|
9851
|
my $class = ref($proto) || $proto;
|
27
|
1991
|
|
|
|
|
30839
|
my $self = {
|
28
|
|
|
|
|
|
|
'useragent' => $useragent,
|
29
|
|
|
|
|
|
|
'is_standard' => 0,
|
30
|
|
|
|
|
|
|
'is_imode' => 0,
|
31
|
|
|
|
|
|
|
'is_mozilla' => 0,
|
32
|
|
|
|
|
|
|
'is_rubbish' => 0,
|
33
|
|
|
|
|
|
|
'is_series60' => undef,
|
34
|
|
|
|
|
|
|
};
|
35
|
1991
|
|
|
|
|
21279
|
bless($self,$class);
|
36
|
1991
|
|
|
|
|
4475
|
my $hashref = $self->_parseUserAgent($useragent);
|
37
|
1991
|
50
|
|
|
|
4713
|
if (defined($hashref)) {
|
38
|
1991
|
|
|
|
|
5626
|
$self->{'vendor'} = $hashref->{'vendor'};
|
39
|
1991
|
|
|
|
|
10222
|
$self->{'model'} = $hashref->{'model'};
|
40
|
1991
|
|
|
|
|
6662
|
$self->{'version'} = $hashref->{'version'};
|
41
|
1991
|
|
|
|
|
3589
|
$self->{'imode_cache'} = $hashref->{'imode_cache'};
|
42
|
1991
|
|
|
|
|
3404
|
$self->{'screendims'} = $hashref->{'screendims'};
|
43
|
|
|
|
|
|
|
}
|
44
|
1991
|
|
|
|
|
8829
|
return $self;
|
45
|
|
|
|
|
|
|
}
|
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
# Protected class method.
|
51
|
|
|
|
|
|
|
# Parses a standard mobile user agent string with the format vendor-model/version.
|
52
|
|
|
|
|
|
|
# If no match can be made, undef is returned.
|
53
|
|
|
|
|
|
|
# If a match is made, a hash ref is returned containing the compulsory
|
54
|
|
|
|
|
|
|
# keys "vendor" and "model", and the optional keys "version", and "screendims".
|
55
|
|
|
|
|
|
|
#
|
56
|
|
|
|
|
|
|
# Below are a few samples of these user agent strings:
|
57
|
|
|
|
|
|
|
#
|
58
|
|
|
|
|
|
|
# Nokia8310/1.0 (05.57)
|
59
|
|
|
|
|
|
|
# NokiaN-Gage/1.0 SymbianOS/6.1 Series60/1.2 Profile/MIDP-1.0 Configuration/CLDC-1.0
|
60
|
|
|
|
|
|
|
# SAGEM-myX-6/1.0 UP.Browser/6.1.0.6.1.c.3 (GUI) MMP/1.0 UP.Link/1.1
|
61
|
|
|
|
|
|
|
# SAMSUNG-SGH-A300/1.0 UP/4.1.19k
|
62
|
|
|
|
|
|
|
# SEC-SGHE710/1.0
|
63
|
|
|
|
|
|
|
#
|
64
|
|
|
|
|
|
|
# Parameter: user-agent string.
|
65
|
|
|
|
|
|
|
# Returns: hash ref or undef.
|
66
|
|
|
|
|
|
|
sub _parseUserAgentStandard {
|
67
|
1991
|
|
|
1991
|
|
9734
|
my $proto = shift;
|
68
|
1991
|
|
|
|
|
3826
|
my $useragent = shift;
|
69
|
|
|
|
|
|
|
# Standard vendor-model/version user agents
|
70
|
1991
|
100
|
|
|
|
16062
|
unless ($useragent =~ /^
|
71
|
|
|
|
|
|
|
# Match the vendor-model combination (this goes into $1)....
|
72
|
|
|
|
|
|
|
(
|
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
# Match known vendor names (this goes into $2)...
|
75
|
|
|
|
|
|
|
(ACER|Alcatel|AUDIOVOX|BlackBerry|CDM|Ericsson|LG\b|LGE|Motorola|MOT|NEC|Nokia|Panasonic|PANTECH|PT|QCI|SAGEM|SAMSUNG|SEC|Sanyo|Sendo|SHARP|SIE|SonyEricsson|Telit|Telit_Mobile_Terminals|TSM)
|
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# optionally followed by an irrelevant space or '-' character...
|
78
|
|
|
|
|
|
|
[- ]?
|
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
# followed by the model name (this goes into $3)...
|
81
|
|
|
|
|
|
|
([^\/\s\_]+)
|
82
|
|
|
|
|
|
|
)
|
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
# Match possible version information after the slash seperator (this goes into $5)...
|
85
|
|
|
|
|
|
|
(\/(\S+))?
|
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
/x) {
|
88
|
193
|
|
|
|
|
646
|
return undef;
|
89
|
|
|
|
|
|
|
}
|
90
|
1798
|
|
|
|
|
13855
|
my $both = $1;
|
91
|
1798
|
|
|
|
|
3051
|
my $vendor = $2;
|
92
|
1798
|
|
|
|
|
2856
|
my $model = $3;
|
93
|
1798
|
|
|
|
|
2734
|
my $version = $5;
|
94
|
|
|
|
|
|
|
# Fixup vendors and models.
|
95
|
1798
|
100
|
33
|
|
|
24498
|
if ($vendor eq 'ACER') {
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
96
|
1
|
|
|
|
|
3
|
$vendor = 'Acer';
|
97
|
|
|
|
|
|
|
}
|
98
|
|
|
|
|
|
|
elsif ($vendor eq 'AUDIOVOX') {
|
99
|
4
|
|
|
|
|
6
|
$vendor = 'Audiovox';
|
100
|
|
|
|
|
|
|
}
|
101
|
|
|
|
|
|
|
elsif ($vendor eq 'CDM') {
|
102
|
4
|
|
|
|
|
7
|
$vendor = 'Audiovox';
|
103
|
4
|
|
|
|
|
9
|
$model = "CDM-$model";
|
104
|
|
|
|
|
|
|
}
|
105
|
|
|
|
|
|
|
elsif ($vendor eq 'Ericsson') {
|
106
|
67
|
50
|
|
|
|
165
|
if ($model eq 'T68_NIL') {
|
107
|
0
|
|
|
|
|
0
|
$model = 'T68';
|
108
|
|
|
|
|
|
|
}
|
109
|
|
|
|
|
|
|
}
|
110
|
|
|
|
|
|
|
elsif (substr($vendor,0,2) eq 'LG') {
|
111
|
30
|
|
|
|
|
38
|
$vendor = 'LG';
|
112
|
30
|
100
|
|
|
|
74
|
if ($model =~ /^([A-Za-z\d]+)-/) { # LGE510W-V137-AU4.2
|
113
|
1
|
|
|
|
|
3
|
$model = $1;
|
114
|
|
|
|
|
|
|
}
|
115
|
|
|
|
|
|
|
}
|
116
|
|
|
|
|
|
|
elsif ($vendor eq 'MOT') {
|
117
|
277
|
|
|
|
|
355
|
$vendor = 'Motorola';
|
118
|
277
|
|
|
|
|
636
|
$model =~ s/[\._]$//;
|
119
|
|
|
|
|
|
|
}
|
120
|
|
|
|
|
|
|
elsif (($vendor eq 'PT') || ($vendor eq 'PANTECH')) {
|
121
|
0
|
|
|
|
|
0
|
$vendor = 'Pantech';
|
122
|
|
|
|
|
|
|
}
|
123
|
|
|
|
|
|
|
elsif ($vendor eq 'PHILIPS') {
|
124
|
0
|
|
|
|
|
0
|
$model = uc($model);
|
125
|
|
|
|
|
|
|
}
|
126
|
|
|
|
|
|
|
elsif ($vendor eq 'SAGEM') {
|
127
|
80
|
50
|
|
|
|
582
|
if ($model eq '-') {
|
128
|
0
|
|
|
|
|
0
|
return undef;
|
129
|
|
|
|
|
|
|
}
|
130
|
|
|
|
|
|
|
}
|
131
|
|
|
|
|
|
|
elsif ($vendor eq 'SEC') {
|
132
|
63
|
|
|
|
|
87
|
$vendor = 'SAMSUNG';
|
133
|
63
|
|
|
|
|
115
|
$model =~ s/\*.*$//g;
|
134
|
|
|
|
|
|
|
}
|
135
|
|
|
|
|
|
|
elsif ($vendor eq 'SIE') {
|
136
|
240
|
|
|
|
|
408
|
$vendor = 'Siemens';
|
137
|
|
|
|
|
|
|
}
|
138
|
|
|
|
|
|
|
elsif ($vendor eq 'Telit_Mobile_Terminals') {
|
139
|
1
|
|
|
|
|
4
|
$vendor = 'Telit';
|
140
|
|
|
|
|
|
|
}
|
141
|
|
|
|
|
|
|
elsif ($vendor eq 'TSM') {
|
142
|
10
|
|
|
|
|
16
|
$vendor = 'Vitelcom';
|
143
|
10
|
|
|
|
|
13
|
$model = $both;
|
144
|
|
|
|
|
|
|
}
|
145
|
1798
|
|
|
|
|
10521
|
my %result = ('vendor' => $vendor,
|
146
|
|
|
|
|
|
|
'model' => $model);
|
147
|
1798
|
100
|
|
|
|
4411
|
if (defined($version)) {
|
148
|
1670
|
|
|
|
|
3542
|
$result{'version'} = $version;
|
149
|
|
|
|
|
|
|
}
|
150
|
1798
|
|
|
|
|
8433
|
return \%result;
|
151
|
|
|
|
|
|
|
}
|
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
# Protected class method.
|
156
|
|
|
|
|
|
|
# Parses an i-mode user agent string.
|
157
|
|
|
|
|
|
|
# If no match can be made, undef is returned.
|
158
|
|
|
|
|
|
|
# If a match is made, a hash ref is returned containing the compulsory
|
159
|
|
|
|
|
|
|
# keys "vendor" and "model", and the optional keys "version", "imode_cache",
|
160
|
|
|
|
|
|
|
# and "screendims".
|
161
|
|
|
|
|
|
|
#
|
162
|
|
|
|
|
|
|
# Below are a few samples of these user agent strings:
|
163
|
|
|
|
|
|
|
#
|
164
|
|
|
|
|
|
|
# portalmmm/1.0 m21i-10(c10)
|
165
|
|
|
|
|
|
|
# portalmmm/1.0 n21i-10(c10)
|
166
|
|
|
|
|
|
|
# portalmmm/1.0 n21i-10(;ser123456789012345;icc1234567890123456789F)
|
167
|
|
|
|
|
|
|
# portalmmm/2.0 N400i(c20;TB)
|
168
|
|
|
|
|
|
|
# portalmmm/2.0 P341i(c10;TB)
|
169
|
|
|
|
|
|
|
# portalmmm/2.0 L341i(c10;TB)
|
170
|
|
|
|
|
|
|
# portalmmm/2.0 S341i(c10;TB)
|
171
|
|
|
|
|
|
|
# portalmmm/2.0 SI400i(c10;TB)
|
172
|
|
|
|
|
|
|
# DoCoMo/1.0/modelname
|
173
|
|
|
|
|
|
|
# DoCoMo/1.0/modelname/cache
|
174
|
|
|
|
|
|
|
# DoCoMo/1.0/modelname/cache/unique_id_information
|
175
|
|
|
|
|
|
|
# DoCoMo/2.0 modelname(cache;individual_identification_information)
|
176
|
|
|
|
|
|
|
#
|
177
|
|
|
|
|
|
|
# Parameter: user-agent string.
|
178
|
|
|
|
|
|
|
# Returns: hash ref or undef.
|
179
|
|
|
|
|
|
|
sub _parseUserAgentImode {
|
180
|
160
|
|
|
160
|
|
195
|
my $proto = shift;
|
181
|
160
|
|
|
|
|
215
|
my $useragent = shift;
|
182
|
160
|
|
|
|
|
2098
|
my %vendors = (
|
183
|
|
|
|
|
|
|
'D' => 'Mitsubishi',
|
184
|
|
|
|
|
|
|
'ER' => 'Ericsson',
|
185
|
|
|
|
|
|
|
'F' => 'Fujitsu',
|
186
|
|
|
|
|
|
|
'KO' => 'Kokusai', # Hitachi
|
187
|
|
|
|
|
|
|
'L' => 'LG',
|
188
|
|
|
|
|
|
|
'M' => 'Mitsubishi',
|
189
|
|
|
|
|
|
|
'P' => 'Panasonic', # Matsushita
|
190
|
|
|
|
|
|
|
'N' => 'NEC',
|
191
|
|
|
|
|
|
|
'NM' => 'Nokia',
|
192
|
|
|
|
|
|
|
'R' => 'Japan Radio',
|
193
|
|
|
|
|
|
|
'S' => 'SAMSUNG', # because of the other vendor codes starting with S below, the regex must try to match them first.
|
194
|
|
|
|
|
|
|
'SG' => 'SAGEM',
|
195
|
|
|
|
|
|
|
'SH' => 'Sharp',
|
196
|
|
|
|
|
|
|
'SI' => 'Siemens',
|
197
|
|
|
|
|
|
|
'SO' => 'Sony',
|
198
|
|
|
|
|
|
|
'TS' => 'Toshiba');
|
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
# Standard i-mode user agents
|
201
|
160
|
|
|
|
|
1839
|
my $pattern = '^(portalmmm|DoCoMo)\/(\d+\.\d+) ((' . join('|', reverse sort keys(%vendors)) . ')[\w\-]+) ?\((c(\d+))?';
|
202
|
160
|
100
|
|
|
|
1412
|
if ($useragent =~ /$pattern/i) {
|
203
|
29
|
|
|
|
|
88
|
my $vendor = $vendors{uc($4)};
|
204
|
29
|
|
|
|
|
64
|
my $model = $3;
|
205
|
29
|
|
|
|
|
45
|
my $version = $2;
|
206
|
29
|
|
|
|
|
31
|
my $cache;
|
207
|
29
|
100
|
66
|
|
|
164
|
if (defined($6) && length($6)) {
|
208
|
26
|
|
|
|
|
62
|
$cache = $6 + 0;
|
209
|
|
|
|
|
|
|
}
|
210
|
|
|
|
|
|
|
else {
|
211
|
3
|
|
|
|
|
5
|
$cache = 5;
|
212
|
|
|
|
|
|
|
}
|
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
# Chop off trailing cache size from model name (e.g. N21i-10 becomes N21i).
|
215
|
29
|
50
|
|
|
|
59
|
if (defined($model)) {
|
216
|
29
|
|
|
|
|
85
|
$model =~ s/-\d+$//;
|
217
|
|
|
|
|
|
|
}
|
218
|
|
|
|
|
|
|
|
219
|
29
|
|
|
|
|
311
|
return {'vendor' => $vendor,
|
220
|
|
|
|
|
|
|
'model' => $model,
|
221
|
|
|
|
|
|
|
'version' => $version,
|
222
|
|
|
|
|
|
|
'imode_cache' => $cache};
|
223
|
|
|
|
|
|
|
}
|
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
# DoCoMo HTML i-mode user agents
|
226
|
131
|
|
|
|
|
6815
|
$pattern = '^DoCoMo\/(\d+\.\d+)\/((' . join('|', keys(%vendors)) . ')[\w\.\-\_]+)(\/c(\d+))?';
|
227
|
131
|
100
|
|
|
|
34780
|
if ($useragent =~ /$pattern/i) {
|
228
|
|
|
|
|
|
|
# HTML 1.0: DoCoMo/1.0/modelname
|
229
|
|
|
|
|
|
|
# HTML 2.0: DoCoMo/1.0/modelname/cache
|
230
|
|
|
|
|
|
|
# HTML 3.0: DoCoMo/1.0/modelname/cache/unique_id_information
|
231
|
69
|
|
|
|
|
508
|
my %result = ('vendor' => $vendors{uc($3)},
|
232
|
|
|
|
|
|
|
'model' => $2,
|
233
|
|
|
|
|
|
|
'version' => $1);
|
234
|
69
|
50
|
33
|
|
|
233
|
if (defined($6) && length($6)) {
|
235
|
0
|
|
|
|
|
0
|
$result{'imode_cache'} = $5 + 0;
|
236
|
|
|
|
|
|
|
}
|
237
|
|
|
|
|
|
|
else {
|
238
|
69
|
|
|
|
|
130
|
$result{'imode_cache'} = 5;
|
239
|
|
|
|
|
|
|
}
|
240
|
69
|
|
|
|
|
554
|
return \%result;
|
241
|
|
|
|
|
|
|
}
|
242
|
62
|
|
|
|
|
432
|
return undef;
|
243
|
|
|
|
|
|
|
}
|
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
# Protected class method.
|
248
|
|
|
|
|
|
|
# Parses a Mozilla (so called) compatible user agent string.
|
249
|
|
|
|
|
|
|
# If no match can be made, undef is returned.
|
250
|
|
|
|
|
|
|
# If a match is made, a hash ref is returned containing the compulsory
|
251
|
|
|
|
|
|
|
# keys "vendor" and "model", and the optional keys "version", and "screendims".
|
252
|
|
|
|
|
|
|
#
|
253
|
|
|
|
|
|
|
# Below are a few samples of these user agent strings:
|
254
|
|
|
|
|
|
|
#
|
255
|
|
|
|
|
|
|
# Mozilla/4.1 (compatible; MSIE 5.0; Symbian OS; Nokia 3650;424) Opera 6.10 [en]
|
256
|
|
|
|
|
|
|
# Mozilla/4.0 (compatible; MSIE 6.0; Nokia7650) ReqwirelessWeb/2.0.0.0
|
257
|
|
|
|
|
|
|
# Mozilla/1.22 (compatible; MMEF20; Cellphone; Sony CMD-Z5)
|
258
|
|
|
|
|
|
|
# Mozilla/1.22 (compatible; MMEF20; Cellphone; Sony CMD-Z5;Pz063e+wt16)
|
259
|
|
|
|
|
|
|
# Mozilla/2.0 (compatible; MSIE 3.02; Windows CE; PPC; 240x320)
|
260
|
|
|
|
|
|
|
# mozilla/4.0 (compatible;MSIE 4.01; Windows CE;PPC;240X320) UP.Link/5.1.1.5
|
261
|
|
|
|
|
|
|
# Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; PPC; 240x320)
|
262
|
|
|
|
|
|
|
# Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; SmartPhone; 176x220)
|
263
|
|
|
|
|
|
|
# Mozilla/2.0 (compatible; MSIE 3.02; Windows CE; 240x320; PPC)
|
264
|
|
|
|
|
|
|
# Mozilla/2.0 (compatible; MSIE 3.02; Windows CE; Smartphone; 176x220; Mio8380; Smartphone; 176x220)
|
265
|
|
|
|
|
|
|
# Mozilla/4.0 (MobilePhone SCP-8100/US/1.0) NetFront/3.0 MMP/2.0
|
266
|
|
|
|
|
|
|
# Mozilla/2.0(compatible; MSIE 3.02; Windows CE; Smartphone; 176x220)
|
267
|
|
|
|
|
|
|
# Mozilla/4.1 (compatible; MSIE 5.0; Symbian OS Series 60 42) Opera 6.0 [fr]
|
268
|
|
|
|
|
|
|
# Mozilla/SMB3(Z105)/Samsung UP.Link/5.1.1.5
|
269
|
|
|
|
|
|
|
#
|
270
|
|
|
|
|
|
|
# Parameter: user-agent string.
|
271
|
|
|
|
|
|
|
# Returns: hash ref or undef.
|
272
|
|
|
|
|
|
|
sub _parseUserAgentMozilla {
|
273
|
193
|
|
|
193
|
|
234
|
my $proto = shift;
|
274
|
193
|
|
|
|
|
218
|
my $useragent = shift;
|
275
|
|
|
|
|
|
|
# SAMSUNG browsers
|
276
|
193
|
100
|
|
|
|
429
|
if ($useragent =~ /^Mozilla\/SMB3\((Z105)\)\/(Samsung)/) {
|
277
|
2
|
|
|
|
|
22
|
return {'vendor' => uc($2), 'model' => $1};
|
278
|
|
|
|
|
|
|
}
|
279
|
|
|
|
|
|
|
# Extract the string between the brackets.
|
280
|
191
|
100
|
|
|
|
565
|
unless($useragent =~ /^Mozilla\/\d+\.\d+\s*\(([^\)]+)\)/i) {
|
281
|
160
|
|
|
|
|
415
|
return undef;
|
282
|
|
|
|
|
|
|
}
|
283
|
31
|
|
|
|
|
296
|
my @parts = split(/\s*;\s*/, $1); # split string between brackets on ';' seperator.
|
284
|
|
|
|
|
|
|
# Micro$oft PPC and Smartphone browsers. Unfortunately, one day, if history repeats itself, this will probably be the only user-agent check necessary.
|
285
|
31
|
100
|
66
|
|
|
229
|
if ((@parts >= 4) && ($parts[0] eq 'compatible') && ($parts[2] eq 'Windows CE')) {
|
|
|
|
100
|
|
|
|
|
286
|
9
|
|
|
|
|
35
|
my %result = ('vendor' => 'Microsoft');
|
287
|
9
|
100
|
100
|
|
|
75
|
if (($parts[3] eq 'PPC') || (lc($parts[3]) eq 'smartphone')) {
|
|
|
50
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
288
|
8
|
|
|
|
|
18
|
$result{'model'} = 'SmartPhone';
|
289
|
8
|
50
|
33
|
|
|
64
|
if ((@parts >= 5) && ($parts[4] =~ /^\d{1,4}x\d{1,4}$/i)) {
|
290
|
8
|
|
|
|
|
25
|
$result{'screendims'} = lc($parts[4]);
|
291
|
|
|
|
|
|
|
}
|
292
|
|
|
|
|
|
|
}
|
293
|
|
|
|
|
|
|
elsif ((@parts >= 5) && (($parts[4] eq 'PPC') || (lc($parts[4]) eq 'smartphone'))) {
|
294
|
1
|
|
|
|
|
4
|
$result{'model'} = 'SmartPhone';
|
295
|
1
|
50
|
|
|
|
8
|
if ($parts[3] =~ /^\d{1,4}x\d{1,4}$/i) {
|
296
|
1
|
|
|
|
|
4
|
$result{'screendims'} = lc($parts[3]);
|
297
|
|
|
|
|
|
|
}
|
298
|
|
|
|
|
|
|
}
|
299
|
9
|
50
|
|
|
|
25
|
if (exists($result{'model'})) {
|
300
|
9
|
|
|
|
|
46
|
return \%result;
|
301
|
|
|
|
|
|
|
}
|
302
|
|
|
|
|
|
|
}
|
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
# Nokia's with Opera browsers or SonyEricssons.
|
305
|
22
|
50
|
66
|
|
|
193
|
if ((@parts >= 4) && ($parts[0] eq 'compatible') && ($parts[3] =~ /^(Nokia|Sony)\s*(\S+)$/)) {
|
|
|
|
66
|
|
|
|
|
306
|
18
|
|
|
|
|
31
|
my $vendor = $1;
|
307
|
18
|
|
|
|
|
29
|
my $model = $2;
|
308
|
18
|
50
|
|
|
|
34
|
if ($vendor eq 'Sony') {
|
309
|
18
|
|
|
|
|
26
|
$vendor = 'SonyEricsson';
|
310
|
|
|
|
|
|
|
}
|
311
|
18
|
|
|
|
|
108
|
return {'vendor' => $vendor, 'model' => $model};
|
312
|
|
|
|
|
|
|
}
|
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
# SANYO browsers
|
315
|
4
|
50
|
33
|
|
|
69
|
if (@parts && ($parts[0] =~ /^MobilePhone ([^\/]+)\/([A-Z]+\/)?(\d+\.\d+)$/)) { # MobilePhone PM-8200/US/1.0
|
316
|
4
|
|
|
|
|
31
|
return {'vendor' => 'Sanyo', 'model' => $1, 'version' => $3};
|
317
|
|
|
|
|
|
|
}
|
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
# Nokias with ReqwirelessWeb browser
|
320
|
0
|
0
|
0
|
|
|
0
|
if ((@parts >= 3) && ($parts[0] eq 'compatible') && ($parts[1] =~ /^(Nokia)\s*(\S+)$/)) {
|
|
|
|
0
|
|
|
|
|
321
|
0
|
|
|
|
|
0
|
return {'vendor' => $1, 'model' => $2};
|
322
|
|
|
|
|
|
|
}
|
323
|
0
|
|
|
|
|
0
|
return undef;
|
324
|
|
|
|
|
|
|
}
|
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
# Protected class method.
|
329
|
|
|
|
|
|
|
# Parses a non-standard mobile user agent string.
|
330
|
|
|
|
|
|
|
# If no match can be made, undef is returned.
|
331
|
|
|
|
|
|
|
# If a match is made, a hash ref is returned containing the compulsory
|
332
|
|
|
|
|
|
|
# keys "vendor" and "model", and the optional keys "version", and "screendims".
|
333
|
|
|
|
|
|
|
#
|
334
|
|
|
|
|
|
|
# Below are a few samples of these user agent strings:
|
335
|
|
|
|
|
|
|
#
|
336
|
|
|
|
|
|
|
# LGE/U8150/1.0 Profile/MIDP-2.0 Configuration/CLDC-1.0
|
337
|
|
|
|
|
|
|
# PHILIPS855 ObigoInternetBrowser/2.0
|
338
|
|
|
|
|
|
|
# PHILIPS 535 / Obigo Internet Browser 2.0
|
339
|
|
|
|
|
|
|
# PHILIPS-FISIO 620/3
|
340
|
|
|
|
|
|
|
# PHILIPS-Fisio311/2.1
|
341
|
|
|
|
|
|
|
# PHILIPS-FISIO311/2.1
|
342
|
|
|
|
|
|
|
# PHILIPS-Xenium9@9 UP/4.1.16r
|
343
|
|
|
|
|
|
|
# PHILIPS-XENIUM 9@9/2.1
|
344
|
|
|
|
|
|
|
# PHILIPS-Xenium 9@9++/3.14
|
345
|
|
|
|
|
|
|
# PHILIPS-Ozeo UP/4
|
346
|
|
|
|
|
|
|
# PHILIPS-V21WAP UP/4
|
347
|
|
|
|
|
|
|
# PHILIPS-Az@lis288 UP/4.1.19m
|
348
|
|
|
|
|
|
|
# PHILIPS-SYSOL2/3.11 UP.Browser/5.0.1.11
|
349
|
|
|
|
|
|
|
# Vitelcom-Feature Phone1.0 UP.Browser/5.0.2.2(GUI
|
350
|
|
|
|
|
|
|
# ReqwirelessWeb/2.0.0 MIDP-1.0 CLDC-1.0 Nokia3650
|
351
|
|
|
|
|
|
|
# SEC-SGHE710
|
352
|
|
|
|
|
|
|
#
|
353
|
|
|
|
|
|
|
# Notice how often one certain brand of these user-agents is handled by this function. I say no more.
|
354
|
|
|
|
|
|
|
#
|
355
|
|
|
|
|
|
|
# Parameter: user-agent string.
|
356
|
|
|
|
|
|
|
# Returns: hash ref or undef.
|
357
|
|
|
|
|
|
|
sub _parseUserAgentRubbish {
|
358
|
62
|
|
|
62
|
|
119
|
my $proto = shift;
|
359
|
62
|
|
|
|
|
108
|
my $useragent = shift;
|
360
|
|
|
|
|
|
|
# Old ReqwirelessWeb browsers for Nokia. ReqwirelessWeb/2.0.0 MIDP-1.0 CLDC-1.0 Nokia3650
|
361
|
62
|
50
|
|
|
|
1451
|
if ($useragent =~ /(Nokia)\s*(N-Gage|\d+)$/) {
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
362
|
0
|
|
|
|
|
0
|
return {'vendor' => $1, 'model' => $2};
|
363
|
|
|
|
|
|
|
}
|
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
# LG Electronics
|
366
|
|
|
|
|
|
|
elsif ($useragent =~ /^(LG)E?\/(\w+)(\/(\d+\.\d+))?/) { # LGE/U8150/1.0 Profile/MIDP-2.0 Configuration/CLDC-1.0
|
367
|
1
|
|
|
|
|
5
|
my %result = ('vendor' => $1, 'model' => $2);
|
368
|
1
|
50
|
33
|
|
|
11
|
if (defined($4) && length($4)) {
|
369
|
1
|
|
|
|
|
3
|
$result{'version'} = $4;
|
370
|
|
|
|
|
|
|
}
|
371
|
1
|
|
|
|
|
6
|
return \%result;
|
372
|
|
|
|
|
|
|
}
|
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
# And now for the worst of all user agents...
|
375
|
|
|
|
|
|
|
elsif ($useragent =~ /^(PHILIPS)(.+)/) {
|
376
|
60
|
|
|
|
|
131
|
my $vendor = $1;
|
377
|
60
|
|
|
|
|
72
|
my $model;
|
378
|
60
|
|
|
|
|
157
|
my $garbage = uc($2); # everything after the word PHILIPS in uppercase.
|
379
|
60
|
|
|
|
|
644
|
$garbage =~ s/(^\s+|\s+$)//g; # trim
|
380
|
60
|
100
|
|
|
|
430
|
if ($garbage =~ /^-?(\d+)/) { # match the model names that are just digits.
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
381
|
2
|
|
|
|
|
6
|
$model = $1;
|
382
|
|
|
|
|
|
|
# PHILIPS855 ObigoInternetBrowser/2.0
|
383
|
|
|
|
|
|
|
# PHILIPS 535 / Obigo Internet Browser 2.0
|
384
|
|
|
|
|
|
|
}
|
385
|
|
|
|
|
|
|
elsif ($garbage =~ /^-?(FISIO)\s*(\d+)/) { # match the FISIO model names.
|
386
|
29
|
|
|
|
|
72
|
$model = "$1$2";
|
387
|
|
|
|
|
|
|
# PHILIPS-FISIO 620/3
|
388
|
|
|
|
|
|
|
# PHILIPS-Fisio311/2.1
|
389
|
|
|
|
|
|
|
# PHILIPS-FISIO311/2.1
|
390
|
|
|
|
|
|
|
}
|
391
|
|
|
|
|
|
|
elsif ($garbage =~ /^-?(XENIUM)/) { # match the XENIUM model names.
|
392
|
8
|
|
|
|
|
17
|
$model = $1;
|
393
|
|
|
|
|
|
|
# PHILIPS-Xenium9@9 UP/4.1.16r
|
394
|
|
|
|
|
|
|
# PHILIPS-XENIUM 9@9/2.1
|
395
|
|
|
|
|
|
|
# PHILIPS-Xenium 9@9++/3.14
|
396
|
|
|
|
|
|
|
}
|
397
|
|
|
|
|
|
|
elsif ($garbage =~ /^-?([^\s\/]+)/) { # match all other model names that contain no spaces and no slashes.
|
398
|
21
|
|
|
|
|
43
|
$model = $1;
|
399
|
|
|
|
|
|
|
# PHILIPS-Ozeo UP/4
|
400
|
|
|
|
|
|
|
# PHILIPS-V21WAP UP/4
|
401
|
|
|
|
|
|
|
# PHILIPS-Az@lis288 UP/4.1.19m
|
402
|
|
|
|
|
|
|
# PHILIPS-SYSOL2/3.11 UP.Browser/5.0.1.11
|
403
|
|
|
|
|
|
|
}
|
404
|
60
|
50
|
|
|
|
130
|
if (defined($model)) {
|
405
|
60
|
|
|
|
|
354
|
return {'vendor' => $vendor, 'model' => $model};
|
406
|
|
|
|
|
|
|
}
|
407
|
|
|
|
|
|
|
}
|
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
# Vitelcom user-agents (used in Spain)
|
410
|
|
|
|
|
|
|
elsif ($useragent =~ /^(Vitelcom)-(Feature Phone)(\d+\.\d+)/) {
|
411
|
|
|
|
|
|
|
# Vitelcom-Feature Phone1.0 UP.Browser/5.0.2.2(GUI) -- this is a TSM 3 or a TSM 4.
|
412
|
1
|
|
|
|
|
11
|
return {'vendor' => $1,
|
413
|
|
|
|
|
|
|
'model' => $2,
|
414
|
|
|
|
|
|
|
'version' => $3};
|
415
|
|
|
|
|
|
|
}
|
416
|
0
|
|
|
|
|
0
|
return undef;
|
417
|
|
|
|
|
|
|
}
|
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
# Protected object method.
|
422
|
|
|
|
|
|
|
# Parses a user agent string.
|
423
|
|
|
|
|
|
|
# This method simply calls the other 4 _parseUserAgent*() methods to do the work.
|
424
|
|
|
|
|
|
|
# If no match can be made, undef is returned.
|
425
|
|
|
|
|
|
|
# If a match is made, a hash ref is returned containing the compulsory
|
426
|
|
|
|
|
|
|
# keys "vendor" and "model", and the optional keys "version", "imode_cache",
|
427
|
|
|
|
|
|
|
# and "screendims".
|
428
|
|
|
|
|
|
|
#
|
429
|
|
|
|
|
|
|
# Parameter: user-agent string.
|
430
|
|
|
|
|
|
|
# Returns: hash ref or undef.
|
431
|
|
|
|
|
|
|
sub _parseUserAgent {
|
432
|
1991
|
|
|
1991
|
|
4529
|
my $self = shift;
|
433
|
1991
|
|
|
|
|
2906
|
my $useragent = shift;
|
434
|
1991
|
|
|
|
|
2590
|
my $result;
|
435
|
1991
|
100
|
|
|
|
5164
|
if ($result = $self->_parseUserAgentStandard($useragent)) {
|
436
|
1798
|
|
|
|
|
3110
|
$self->{'is_standard'} = 1;
|
437
|
1798
|
|
|
|
|
8520
|
return $result;
|
438
|
|
|
|
|
|
|
}
|
439
|
193
|
100
|
|
|
|
456
|
if ($result = $self->_parseUserAgentMozilla($useragent)) {
|
440
|
33
|
|
|
|
|
53
|
$self->{'is_mozilla'} = 1;
|
441
|
33
|
|
|
|
|
64
|
return $result;
|
442
|
|
|
|
|
|
|
}
|
443
|
160
|
100
|
|
|
|
432
|
if ($result = $self->_parseUserAgentImode($useragent)) {
|
444
|
98
|
|
|
|
|
161
|
$self->{'is_imode'} = 1;
|
445
|
98
|
|
|
|
|
218
|
return $result;
|
446
|
|
|
|
|
|
|
}
|
447
|
62
|
50
|
|
|
|
170
|
if ($result = $self->_parseUserAgentRubbish($useragent)) {
|
448
|
62
|
|
|
|
|
109
|
$self->{'is_rubbish'} = 1;
|
449
|
62
|
|
|
|
|
132
|
return $result;
|
450
|
|
|
|
|
|
|
}
|
451
|
0
|
|
|
|
|
0
|
return $result;
|
452
|
|
|
|
|
|
|
}
|
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
# Public object method.
|
457
|
|
|
|
|
|
|
# Returns true if the user-agent string passed into the constructor could be parsed, else false.
|
458
|
|
|
|
|
|
|
# If this method returns false, then it's probably not a mobile user agent string that was
|
459
|
|
|
|
|
|
|
# passed into the constructor.
|
460
|
|
|
|
|
|
|
sub success {
|
461
|
1991
|
|
|
1991
|
1
|
12665
|
my $self = shift;
|
462
|
1991
|
|
|
|
|
6096
|
return defined($self->{'vendor'});
|
463
|
|
|
|
|
|
|
}
|
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
# Public object method.
|
467
|
|
|
|
|
|
|
# Returns the user agent string as passed into the constructor or read
|
468
|
|
|
|
|
|
|
# from the environment variable HTTP_USER_AGENT.
|
469
|
|
|
|
|
|
|
sub userAgent {
|
470
|
0
|
|
|
0
|
1
|
0
|
my $self = shift;
|
471
|
0
|
|
|
|
|
0
|
return $self->{'useragent'};
|
472
|
|
|
|
|
|
|
}
|
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
# Public object method.
|
477
|
|
|
|
|
|
|
# Returns the vendor of the handset if success() returns true, else undef.
|
478
|
|
|
|
|
|
|
sub vendor {
|
479
|
1991
|
|
|
1991
|
1
|
9517
|
my $self = shift;
|
480
|
1991
|
|
|
|
|
5173
|
return $self->{'vendor'};
|
481
|
|
|
|
|
|
|
}
|
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
# Public object method.
|
485
|
|
|
|
|
|
|
# Returns the model of the handset if success() returns true, else undef.
|
486
|
|
|
|
|
|
|
sub model {
|
487
|
1991
|
|
|
1991
|
1
|
21629
|
my $self = shift;
|
488
|
1991
|
|
|
|
|
23788
|
return $self->{'model'};
|
489
|
|
|
|
|
|
|
}
|
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
# Public object method.
|
493
|
|
|
|
|
|
|
# Returns the version (if any) of the user agent.
|
494
|
|
|
|
|
|
|
# The version information isn't always present, nor reliable.
|
495
|
|
|
|
|
|
|
#
|
496
|
|
|
|
|
|
|
# @return string|null
|
497
|
|
|
|
|
|
|
sub version {
|
498
|
0
|
|
|
0
|
1
|
|
my $self = shift;
|
499
|
0
|
|
|
|
|
|
return $self->{'version'};
|
500
|
|
|
|
|
|
|
}
|
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
# Public object method.
|
504
|
|
|
|
|
|
|
# Determines if the parsed user-agent string belongs to an i-mode handset.
|
505
|
|
|
|
|
|
|
# Returns boolean.
|
506
|
|
|
|
|
|
|
sub isImode() {
|
507
|
0
|
|
|
0
|
1
|
|
my $self = shift;
|
508
|
0
|
|
|
|
|
|
return $self->{'is_imode'};
|
509
|
|
|
|
|
|
|
}
|
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
# Public object method.
|
513
|
|
|
|
|
|
|
# Determines if the parsed user-agent string has a Mozilla 'compatible' format.
|
514
|
|
|
|
|
|
|
# Returns boolean.
|
515
|
|
|
|
|
|
|
sub isMozilla() {
|
516
|
0
|
|
|
0
|
1
|
|
my $self = shift;
|
517
|
0
|
|
|
|
|
|
return $self->{'is_mozilla'};
|
518
|
|
|
|
|
|
|
}
|
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
# Public object method.
|
522
|
|
|
|
|
|
|
# Determines if the parsed user-agent string has a standard vendor-model/version format.
|
523
|
|
|
|
|
|
|
# Returns true, if so, else false.
|
524
|
|
|
|
|
|
|
sub isStandard() {
|
525
|
0
|
|
|
0
|
1
|
|
my $self = shift;
|
526
|
0
|
|
|
|
|
|
return $self->{'is_standard'};
|
527
|
|
|
|
|
|
|
}
|
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
# Public object method.
|
531
|
|
|
|
|
|
|
# Determines if the parsed user-agent string has a non-standard or messed up format.
|
532
|
|
|
|
|
|
|
# Returns true, if so, else false.
|
533
|
|
|
|
|
|
|
sub isRubbish() {
|
534
|
0
|
|
|
0
|
1
|
|
my $self = shift;
|
535
|
0
|
|
|
|
|
|
return $self->{'is_rubbish'};
|
536
|
|
|
|
|
|
|
}
|
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
# Public object method.
|
540
|
|
|
|
|
|
|
# Returns the maximum i-mode cache data size in kb's of the user agent if it is
|
541
|
|
|
|
|
|
|
# an i-mode user-agent, else null.
|
542
|
|
|
|
|
|
|
sub imodeCache() {
|
543
|
0
|
|
|
0
|
1
|
|
my $self = shift;
|
544
|
0
|
|
|
|
|
|
return $self->{'imode_cache'};
|
545
|
|
|
|
|
|
|
}
|
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
# Public object method.
|
549
|
|
|
|
|
|
|
# Returns the screen dimensions in the format wxh if this information was parsed
|
550
|
|
|
|
|
|
|
# from the user agent string itself, else undef.
|
551
|
|
|
|
|
|
|
sub screenDims {
|
552
|
0
|
|
|
0
|
1
|
|
my $self = shift;
|
553
|
0
|
|
|
|
|
|
return $self->{'screendims'};
|
554
|
|
|
|
|
|
|
}
|
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
# Public object method.
|
558
|
|
|
|
|
|
|
# Determines if this is a Symbian OS Series 60 user-agent string.
|
559
|
|
|
|
|
|
|
sub isSeries60 {
|
560
|
0
|
|
|
0
|
1
|
|
my $self = shift;
|
561
|
0
|
0
|
|
|
|
|
unless(defined($self->{'is_series60'})) {
|
562
|
|
|
|
|
|
|
# NokiaN-Gage/1.0 SymbianOS/6.1 Series60/1.2 Profile/MIDP-1.0 Configuration/CLDC-1.0
|
563
|
|
|
|
|
|
|
# Mozilla/4.1 (compatible; MSIE 5.0; Symbian OS Series 60 42) Opera 6.0 [fr]
|
564
|
0
|
|
|
|
|
|
$self->{'is_series60'} = $self->{'useragent'} =~ /\b(Symbian OS Series 60|SymbianOS\/\S+ Series60)\b/;
|
565
|
|
|
|
|
|
|
}
|
566
|
0
|
|
|
|
|
|
return $self->{'is_series60'};
|
567
|
|
|
|
|
|
|
}
|
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
1;
|
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
__END__
|