woothee 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +3 -0
- data/Makefile +13 -0
- data/Rakefile +13 -0
- data/lib/woothee/appliance.rb +46 -0
- data/lib/woothee/browser.rb +89 -0
- data/lib/woothee/crawler.rb +153 -0
- data/lib/woothee/dataset.rb +278 -0
- data/lib/woothee/misc.rb +67 -0
- data/lib/woothee/mobilephone.rb +94 -0
- data/lib/woothee/os.rb +166 -0
- data/lib/woothee/util.rb +24 -0
- data/lib/woothee.rb +159 -0
- data/spec/00_valid_spec.rb +13 -0
- data/spec/01_dataset_spec.rb +16 -0
- data/spec/02_run_testsets_spec.rb +39 -0
- data/woothee.gemspec +20 -0
- metadata +79 -0
data/Gemfile
ADDED
data/Makefile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rake'
|
5
|
+
$:.unshift File.join(File.dirname(__FILE__), "lib")
|
6
|
+
|
7
|
+
require 'rspec/core'
|
8
|
+
require 'rspec/core/rake_task'
|
9
|
+
|
10
|
+
task :default => :spec
|
11
|
+
|
12
|
+
desc "Run all specs in spec directory"
|
13
|
+
RSpec::Core::RakeTask.new(:spec)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'woothee/dataset'
|
4
|
+
require 'woothee/util'
|
5
|
+
|
6
|
+
module Woothee::Appliance
|
7
|
+
extend Woothee::Util
|
8
|
+
|
9
|
+
def self.challenge_playstation(ua, result)
|
10
|
+
data = case
|
11
|
+
when ua.index('PSP (PlayStation Portable);') then Woothee::DataSet.get('PSP')
|
12
|
+
when ua.index('PlayStation Vita') then Woothee::DataSet.get('PSVita')
|
13
|
+
when ua.index('PLAYSTATION 3;') then Woothee::DataSet.get('PS3')
|
14
|
+
else nil
|
15
|
+
end
|
16
|
+
return false unless data
|
17
|
+
|
18
|
+
update_map(result, data)
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.challenge_nintendo(ua, result)
|
23
|
+
data = case
|
24
|
+
when ua.index('Nintendo 3DS;') then Woothee::DataSet.get('Nintendo3DS')
|
25
|
+
when ua.index('Nintendo DSi;') then Woothee::DataSet.get('NintendoDSi')
|
26
|
+
when ua.index('Nintendo Wii;') then Woothee::DataSet.get('NintendoWii')
|
27
|
+
else nil
|
28
|
+
end
|
29
|
+
return false unless data
|
30
|
+
|
31
|
+
update_map(result, data)
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.challenge_digitaltv(ua, result)
|
36
|
+
data = if ua.index('InettvBrowser/')
|
37
|
+
Woothee::DataSet.get('DigitalTV')
|
38
|
+
else
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
return false unless data
|
42
|
+
|
43
|
+
update_map(result, data)
|
44
|
+
true
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'woothee/dataset'
|
4
|
+
require 'woothee/util'
|
5
|
+
|
6
|
+
module Woothee::Browser
|
7
|
+
extend Woothee::Util
|
8
|
+
|
9
|
+
def self.challenge_msie(ua, result)
|
10
|
+
return false if ua.index("compatible; MSIE").nil?
|
11
|
+
|
12
|
+
version = if ua =~ /MSIE ([.0-9]+);/o
|
13
|
+
$1
|
14
|
+
else
|
15
|
+
Woothee::VALUE_UNKNOWN
|
16
|
+
end
|
17
|
+
update_map(result, Woothee::DataSet.get('MSIE'))
|
18
|
+
update_version(result, version)
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.challenge_safari_chrome(ua, result)
|
23
|
+
return false if ua.index('Safari/').nil?
|
24
|
+
|
25
|
+
version = Woothee::VALUE_UNKNOWN
|
26
|
+
|
27
|
+
if ua =~ /(?:Chrome|CrMo)\/([.0-9]+)/o
|
28
|
+
# Chrome
|
29
|
+
version = $1
|
30
|
+
update_map(result, Woothee::DataSet.get('Chrome'))
|
31
|
+
update_version(result, version)
|
32
|
+
return true
|
33
|
+
end
|
34
|
+
|
35
|
+
# Safari
|
36
|
+
if ua =~ /Version\/([.0-9]+)/o
|
37
|
+
version = $1
|
38
|
+
end
|
39
|
+
update_map(result, Woothee::DataSet.get('Safari'))
|
40
|
+
update_version(result, version)
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.challenge_firefox(ua, result)
|
45
|
+
return false if ua.index('Firefox/').nil?
|
46
|
+
|
47
|
+
version = if ua =~ /Firefox\/([.0-9]+)/o
|
48
|
+
$1
|
49
|
+
else
|
50
|
+
Woothee::VALUE_UNKNOWN
|
51
|
+
end
|
52
|
+
update_map(result, Woothee::DataSet.get('Firefox'))
|
53
|
+
update_version(result, version)
|
54
|
+
true
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.challenge_opera(ua, result)
|
58
|
+
return false if ua.index('Opera').nil?
|
59
|
+
|
60
|
+
version = if ua =~ /Opera[\/ ]([.0-9]+)/o
|
61
|
+
$1
|
62
|
+
else
|
63
|
+
Woothee::VALUE_UNKNOWN
|
64
|
+
end
|
65
|
+
update_map(result, Woothee::DataSet.get('Opera'))
|
66
|
+
update_version(result, version)
|
67
|
+
true
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.challenge_sleipnir(ua, result)
|
71
|
+
return false if ua.index('Sleipnir/').nil?
|
72
|
+
|
73
|
+
version = if ua =~ /Sleipnir\/([.0-9]+)/o
|
74
|
+
$1
|
75
|
+
else
|
76
|
+
Woothee::VALUE_UNKNOWN
|
77
|
+
end
|
78
|
+
update_map(result, Woothee::DataSet.get('Sleipnir'))
|
79
|
+
update_version(result, version)
|
80
|
+
|
81
|
+
# Sleipnir's user-agent doesn't contain Windows version, so put 'Windows UNKNOWN Ver'.
|
82
|
+
# Sleipnir is IE component browser, so for Windows only.
|
83
|
+
win = Woothee::DataSet.get('Win')
|
84
|
+
update_category(result, win[Woothee::KEY_CATEGORY])
|
85
|
+
update_os(result, win[Woothee::KEY_NAME])
|
86
|
+
|
87
|
+
true
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'woothee/dataset'
|
4
|
+
require 'woothee/util'
|
5
|
+
|
6
|
+
module Woothee::Crawler
|
7
|
+
extend Woothee::Util
|
8
|
+
|
9
|
+
def self.challenge_google(ua, result)
|
10
|
+
return false if ua.index('Google').nil?
|
11
|
+
|
12
|
+
if ua.index('compatible; Googlebot')
|
13
|
+
if ua.index('compatible; Googlebot-Mobile')
|
14
|
+
update_map(result, Woothee::DataSet.get('GoogleBotMobile'))
|
15
|
+
return true
|
16
|
+
else
|
17
|
+
update_map(result, Woothee::DataSet.get('GoogleBot'))
|
18
|
+
return true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
if ua.index('Googlebot-Image/')
|
22
|
+
update_map(result, Woothee::DataSet.get('GoogleBot'))
|
23
|
+
return true
|
24
|
+
end
|
25
|
+
if ua.index('Mediapartners-Google')
|
26
|
+
if ua.index('compatible; Mediapartners-Google') or ua == 'Mediapartners-Google'
|
27
|
+
update_map(result, Woothee::DataSet.get('GoogleMediaPartners'))
|
28
|
+
return true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
if ua.index('Feedfetcher-Google;')
|
32
|
+
update_map(result, Woothee::DataSet.get('GoogleFeedFetcher'))
|
33
|
+
return true
|
34
|
+
end
|
35
|
+
if ua.index('AppEngine-Google')
|
36
|
+
update_map(result, Woothee::DataSet.get('GoogleAppEngine'))
|
37
|
+
return true
|
38
|
+
end
|
39
|
+
if ua.index('Google Web Preview')
|
40
|
+
update_map(result, Woothee::DataSet.get('GoogleWebPreview'))
|
41
|
+
return true
|
42
|
+
end
|
43
|
+
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.challenge_crawlers(ua, result)
|
48
|
+
if ua.index('Yahoo') or ua.index('listing.yahoo.co.jp/support/faq/')
|
49
|
+
if ua.index('compatible; Yahoo! Slurp;')
|
50
|
+
update_map(result, Woothee::DataSet.get('YahooSlurp'))
|
51
|
+
return true
|
52
|
+
end
|
53
|
+
if ua.index('YahooFeedSeekerJp') or ua.index('YahooFeedSeekerBetaJp')
|
54
|
+
update_map(result, Woothee::DataSet.get('YahooJP'))
|
55
|
+
return true
|
56
|
+
end
|
57
|
+
if ua.index('crawler (http://listing.yahoo.co.jp/support/faq/')
|
58
|
+
update_map(result, Woothee::DataSet.get('YahooJP'))
|
59
|
+
return true
|
60
|
+
end
|
61
|
+
if ua.index('Yahoo Pipes')
|
62
|
+
update_map(result, Woothee::DataSet.get('YahooPipes'))
|
63
|
+
return true
|
64
|
+
end
|
65
|
+
end
|
66
|
+
if ua.index('msnbot')
|
67
|
+
update_map(result, Woothee::DataSet.get('msnbot'))
|
68
|
+
return true
|
69
|
+
end
|
70
|
+
if ua.index('bingbot')
|
71
|
+
if ua.index('compatible; bingbot')
|
72
|
+
update_map(result, Woothee::DataSet.get('bingbot'))
|
73
|
+
return true
|
74
|
+
end
|
75
|
+
end
|
76
|
+
if ua.index('Baidu')
|
77
|
+
if ua.index('compatible; Baiduspider') or ua.index('Baiduspider+') or ua.index('Baiduspider-image+')
|
78
|
+
update_map(result, Woothee::DataSet.get('Baiduspider'))
|
79
|
+
return true
|
80
|
+
end
|
81
|
+
end
|
82
|
+
if ua.index('Yeti')
|
83
|
+
if ua.index('http://help.naver.com/robots')
|
84
|
+
update_map(result, Woothee::DataSet.get('Yeti'))
|
85
|
+
return true
|
86
|
+
end
|
87
|
+
end
|
88
|
+
if ua.index('FeedBurner/')
|
89
|
+
update_map(result, Woothee::DataSet.get('FeedBurner'))
|
90
|
+
return true
|
91
|
+
end
|
92
|
+
if ua.index('facebookexternalhit')
|
93
|
+
update_map(result, Woothee::DataSet.get('facebook'))
|
94
|
+
return true
|
95
|
+
end
|
96
|
+
if ua.index('ichiro')
|
97
|
+
if ua.index('http://help.goo.ne.jp/door/crawler.html') or ua.index('compatible; ichiro/mobile goo;')
|
98
|
+
update_map(result, Woothee::DataSet.get('gooIchiro'))
|
99
|
+
return true
|
100
|
+
end
|
101
|
+
end
|
102
|
+
if ua.index('Apple-PubSub')
|
103
|
+
update_map(result, Woothee::DataSet.get('ApplePubSub'))
|
104
|
+
return true
|
105
|
+
end
|
106
|
+
if ua.index('livedoor FeedFetcher') or ua.index('Fastladder FeedFetcher')
|
107
|
+
update_map(result, Woothee::DataSet.get('livedoorFeedFetcher'))
|
108
|
+
return true
|
109
|
+
end
|
110
|
+
if ua.index('Hatena ')
|
111
|
+
if ua.index('Hatena Antenna') or ua.index('Hatena Pagetitle Agent') or ua.index('Hatena Diary RSS')
|
112
|
+
update_map(result, Woothee::DataSet.get('Hatena'))
|
113
|
+
return true
|
114
|
+
end
|
115
|
+
end
|
116
|
+
if ua.index('mixi-check') or ua.index('mixi-news-crawler')
|
117
|
+
update_map(result, Woothee::DataSet.get('mixi'))
|
118
|
+
return true
|
119
|
+
end
|
120
|
+
if ua.index('Indy Library')
|
121
|
+
if ua.index('compatible; Indy Library')
|
122
|
+
update_map(result, Woothee::DataSet.get('IndyLibrary'))
|
123
|
+
return true
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
false
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.challenge_maybe_crawler(ua, result)
|
131
|
+
if ua =~ /bot(?:[-_ .\/;@()]|$)/oi
|
132
|
+
update_map(result, Woothee::DataSet.get('VariousCrawler'))
|
133
|
+
return true
|
134
|
+
end
|
135
|
+
|
136
|
+
if ua =~ /(?:Rome Client |UnwindFetchor\/|ia_archiver |Summify |PostRank\/)/o or ua.index('ASP-Ranker Feed Crawler')
|
137
|
+
update_map(result, Woothee::DataSet.get('VariousCrawler'))
|
138
|
+
return true
|
139
|
+
end
|
140
|
+
|
141
|
+
if ua =~ /(feed|web) ?parser/oi
|
142
|
+
update_map(result, Woothee::DataSet.get('VariousCrawler'))
|
143
|
+
return true
|
144
|
+
end
|
145
|
+
|
146
|
+
if ua =~ /watch ?dog/oi
|
147
|
+
update_map(result, Woothee::DataSet.get('VariousCrawler'))
|
148
|
+
return true
|
149
|
+
end
|
150
|
+
|
151
|
+
false
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,278 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module Woothee
|
3
|
+
KEY_LABEL = :label
|
4
|
+
KEY_NAME = :name
|
5
|
+
KEY_TYPE = :type
|
6
|
+
KEY_CATEGORY = :category
|
7
|
+
KEY_OS = :os
|
8
|
+
KEY_VENDOR = :vendor
|
9
|
+
KEY_VERSION = :version
|
10
|
+
|
11
|
+
TYPE_BROWSER = :browser
|
12
|
+
TYPE_OS = :os
|
13
|
+
TYPE_FULL = :full
|
14
|
+
|
15
|
+
CATEGORY_PC = :pc
|
16
|
+
CATEGORY_SMARTPHONE = :smartphone
|
17
|
+
CATEGORY_MOBILEPHONE = :mobilephone
|
18
|
+
CATEGORY_CRAWLER = :crawler
|
19
|
+
CATEGORY_APPLIANCE = :appliance
|
20
|
+
CATEGORY_MISC = :misc
|
21
|
+
|
22
|
+
ATTRIBUTE_NAME = :name
|
23
|
+
ATTRIBUTE_CATEGORY = :category
|
24
|
+
ATTRIBUTE_OS = :os
|
25
|
+
ATTRIBUTE_VENDOR = :vendor
|
26
|
+
ATTRIBUTE_VERSION = :version
|
27
|
+
|
28
|
+
VALUE_UNKNOWN = "UNKNOWN"
|
29
|
+
end
|
30
|
+
|
31
|
+
module Woothee::DataSet
|
32
|
+
DATASET = {}
|
33
|
+
# GENERATED from dataset.yaml at Tue Jun 12 12:17:48 JST 2012 by tagomoris
|
34
|
+
obj = {:label => 'MSIE', :name => 'Internet Explorer', :type => :browser}
|
35
|
+
obj[:vendor] = 'Microsoft'
|
36
|
+
DATASET[obj[:label]] = obj
|
37
|
+
obj = {:label => 'Chrome', :name => 'Chrome', :type => :browser}
|
38
|
+
obj[:vendor] = 'Google'
|
39
|
+
DATASET[obj[:label]] = obj
|
40
|
+
obj = {:label => 'Safari', :name => 'Safari', :type => :browser}
|
41
|
+
obj[:vendor] = 'Apple'
|
42
|
+
DATASET[obj[:label]] = obj
|
43
|
+
obj = {:label => 'Firefox', :name => 'Firefox', :type => :browser}
|
44
|
+
obj[:vendor] = 'Mozilla'
|
45
|
+
DATASET[obj[:label]] = obj
|
46
|
+
obj = {:label => 'Opera', :name => 'Opera', :type => :browser}
|
47
|
+
obj[:vendor] = 'Opera'
|
48
|
+
DATASET[obj[:label]] = obj
|
49
|
+
obj = {:label => 'Sleipnir', :name => 'Sleipnir', :type => :browser}
|
50
|
+
obj[:vendor] = 'Fenrir Inc.'
|
51
|
+
DATASET[obj[:label]] = obj
|
52
|
+
obj = {:label => 'Win', :name => 'Windows UNKNOWN Ver', :type => :os}
|
53
|
+
obj[:category] = :pc
|
54
|
+
DATASET[obj[:label]] = obj
|
55
|
+
obj = {:label => 'Win8', :name => 'Windows 8', :type => :os}
|
56
|
+
obj[:category] = :pc
|
57
|
+
DATASET[obj[:label]] = obj
|
58
|
+
obj = {:label => 'Win7', :name => 'Windows 7', :type => :os}
|
59
|
+
obj[:category] = :pc
|
60
|
+
DATASET[obj[:label]] = obj
|
61
|
+
obj = {:label => 'WinVista', :name => 'Windows Vista', :type => :os}
|
62
|
+
obj[:category] = :pc
|
63
|
+
DATASET[obj[:label]] = obj
|
64
|
+
obj = {:label => 'WinXP', :name => 'Windows XP', :type => :os}
|
65
|
+
obj[:category] = :pc
|
66
|
+
DATASET[obj[:label]] = obj
|
67
|
+
obj = {:label => 'Win2000', :name => 'Windows 2000', :type => :os}
|
68
|
+
obj[:category] = :pc
|
69
|
+
DATASET[obj[:label]] = obj
|
70
|
+
obj = {:label => 'WinNT4', :name => 'Windows NT 4.0', :type => :os}
|
71
|
+
obj[:category] = :pc
|
72
|
+
DATASET[obj[:label]] = obj
|
73
|
+
obj = {:label => 'WinMe', :name => 'Windows Me', :type => :os}
|
74
|
+
obj[:category] = :pc
|
75
|
+
DATASET[obj[:label]] = obj
|
76
|
+
obj = {:label => 'Win98', :name => 'Windows 98', :type => :os}
|
77
|
+
obj[:category] = :pc
|
78
|
+
DATASET[obj[:label]] = obj
|
79
|
+
obj = {:label => 'Win95', :name => 'Windows 95', :type => :os}
|
80
|
+
obj[:category] = :pc
|
81
|
+
DATASET[obj[:label]] = obj
|
82
|
+
obj = {:label => 'WinPhone', :name => 'Windows Phone OS', :type => :os}
|
83
|
+
obj[:category] = :smartphone
|
84
|
+
DATASET[obj[:label]] = obj
|
85
|
+
obj = {:label => 'WinCE', :name => 'Windows CE', :type => :os}
|
86
|
+
obj[:category] = :smartphone
|
87
|
+
DATASET[obj[:label]] = obj
|
88
|
+
obj = {:label => 'OSX', :name => 'Mac OSX', :type => :os}
|
89
|
+
obj[:category] = :pc
|
90
|
+
DATASET[obj[:label]] = obj
|
91
|
+
obj = {:label => 'MacOS', :name => 'Mac OS Classic', :type => :os}
|
92
|
+
obj[:category] = :pc
|
93
|
+
DATASET[obj[:label]] = obj
|
94
|
+
obj = {:label => 'Linux', :name => 'Linux', :type => :os}
|
95
|
+
obj[:category] = :pc
|
96
|
+
DATASET[obj[:label]] = obj
|
97
|
+
obj = {:label => 'BSD', :name => 'BSD', :type => :os}
|
98
|
+
obj[:category] = :pc
|
99
|
+
DATASET[obj[:label]] = obj
|
100
|
+
obj = {:label => 'Android', :name => 'Android', :type => :os}
|
101
|
+
obj[:category] = :smartphone
|
102
|
+
DATASET[obj[:label]] = obj
|
103
|
+
obj = {:label => 'iPhone', :name => 'iPhone', :type => :os}
|
104
|
+
obj[:category] = :smartphone
|
105
|
+
DATASET[obj[:label]] = obj
|
106
|
+
obj = {:label => 'iPad', :name => 'iPad', :type => :os}
|
107
|
+
obj[:category] = :smartphone
|
108
|
+
DATASET[obj[:label]] = obj
|
109
|
+
obj = {:label => 'iPod', :name => 'iPod', :type => :os}
|
110
|
+
obj[:category] = :smartphone
|
111
|
+
DATASET[obj[:label]] = obj
|
112
|
+
obj = {:label => 'iOS', :name => 'iOS', :type => :os}
|
113
|
+
obj[:category] = :smartphone
|
114
|
+
DATASET[obj[:label]] = obj
|
115
|
+
obj = {:label => 'BlackBerry', :name => 'BlackBerry', :type => :os}
|
116
|
+
obj[:category] = :smartphone
|
117
|
+
DATASET[obj[:label]] = obj
|
118
|
+
obj = {:label => 'docomo', :name => 'docomo', :type => :full}
|
119
|
+
obj[:vendor] = 'docomo'
|
120
|
+
obj[:category] = :mobilephone
|
121
|
+
obj[:os] = 'docomo'
|
122
|
+
DATASET[obj[:label]] = obj
|
123
|
+
obj = {:label => 'au', :name => 'au by KDDI', :type => :full}
|
124
|
+
obj[:vendor] = 'au'
|
125
|
+
obj[:category] = :mobilephone
|
126
|
+
obj[:os] = 'au'
|
127
|
+
DATASET[obj[:label]] = obj
|
128
|
+
obj = {:label => 'SoftBank', :name => 'SoftBank Mobile', :type => :full}
|
129
|
+
obj[:vendor] = 'SoftBank'
|
130
|
+
obj[:category] = :mobilephone
|
131
|
+
obj[:os] = 'SoftBank'
|
132
|
+
DATASET[obj[:label]] = obj
|
133
|
+
obj = {:label => 'willcom', :name => 'WILLCOM', :type => :full}
|
134
|
+
obj[:vendor] = 'WILLCOM'
|
135
|
+
obj[:category] = :mobilephone
|
136
|
+
obj[:os] = 'WILLCOM'
|
137
|
+
DATASET[obj[:label]] = obj
|
138
|
+
obj = {:label => 'jig', :name => 'jig browser', :type => :full}
|
139
|
+
obj[:category] = :mobilephone
|
140
|
+
obj[:os] = 'jig'
|
141
|
+
DATASET[obj[:label]] = obj
|
142
|
+
obj = {:label => 'emobile', :name => 'emobile', :type => :full}
|
143
|
+
obj[:category] = :mobilephone
|
144
|
+
obj[:os] = 'emobile'
|
145
|
+
DATASET[obj[:label]] = obj
|
146
|
+
obj = {:label => 'SymbianOS', :name => 'SymbianOS', :type => :full}
|
147
|
+
obj[:category] = :mobilephone
|
148
|
+
obj[:os] = 'SymbianOS'
|
149
|
+
DATASET[obj[:label]] = obj
|
150
|
+
obj = {:label => 'MobileTranscoder', :name => 'Mobile Transcoder', :type => :full}
|
151
|
+
obj[:category] = :mobilephone
|
152
|
+
obj[:os] = 'Mobile Transcoder'
|
153
|
+
DATASET[obj[:label]] = obj
|
154
|
+
obj = {:label => 'Nintendo3DS', :name => 'Nintendo 3DS', :type => :full}
|
155
|
+
obj[:vendor] = 'Nintendo'
|
156
|
+
obj[:category] = :appliance
|
157
|
+
obj[:os] = 'Nintendo 3DS'
|
158
|
+
DATASET[obj[:label]] = obj
|
159
|
+
obj = {:label => 'NintendoDSi', :name => 'Nintendo DSi', :type => :full}
|
160
|
+
obj[:vendor] = 'Nintendo'
|
161
|
+
obj[:category] = :appliance
|
162
|
+
obj[:os] = 'Nintendo DSi'
|
163
|
+
DATASET[obj[:label]] = obj
|
164
|
+
obj = {:label => 'NintendoWii', :name => 'Nintendo Wii', :type => :full}
|
165
|
+
obj[:vendor] = 'Nintendo'
|
166
|
+
obj[:category] = :appliance
|
167
|
+
obj[:os] = 'Nintendo Wii'
|
168
|
+
DATASET[obj[:label]] = obj
|
169
|
+
obj = {:label => 'PSP', :name => 'PlayStation Portable', :type => :full}
|
170
|
+
obj[:vendor] = 'Sony'
|
171
|
+
obj[:category] = :appliance
|
172
|
+
obj[:os] = 'PlayStation Portable'
|
173
|
+
DATASET[obj[:label]] = obj
|
174
|
+
obj = {:label => 'PSVita', :name => 'PlayStation Vita', :type => :full}
|
175
|
+
obj[:vendor] = 'Sony'
|
176
|
+
obj[:category] = :appliance
|
177
|
+
obj[:os] = 'PlayStation Vita'
|
178
|
+
DATASET[obj[:label]] = obj
|
179
|
+
obj = {:label => 'PS3', :name => 'PlayStation 3', :type => :full}
|
180
|
+
obj[:vendor] = 'Sony'
|
181
|
+
obj[:category] = :appliance
|
182
|
+
obj[:os] = 'PlayStation 3'
|
183
|
+
DATASET[obj[:label]] = obj
|
184
|
+
obj = {:label => 'DigitalTV', :name => 'InternetTVBrowser', :type => :full}
|
185
|
+
obj[:category] = :appliance
|
186
|
+
obj[:os] = 'DigitalTV'
|
187
|
+
DATASET[obj[:label]] = obj
|
188
|
+
obj = {:label => 'SafariRSSReader', :name => 'Safari RSSReader', :type => :full}
|
189
|
+
obj[:vendor] = 'Apple'
|
190
|
+
obj[:category] = :misc
|
191
|
+
DATASET[obj[:label]] = obj
|
192
|
+
obj = {:label => 'GoogleDesktop', :name => 'Google Desktop', :type => :full}
|
193
|
+
obj[:vendor] = 'Google'
|
194
|
+
obj[:category] = :misc
|
195
|
+
DATASET[obj[:label]] = obj
|
196
|
+
obj = {:label => 'WindowsRSSReader', :name => 'Windows RSSReader', :type => :full}
|
197
|
+
obj[:vendor] = 'Microsoft'
|
198
|
+
obj[:category] = :misc
|
199
|
+
DATASET[obj[:label]] = obj
|
200
|
+
obj = {:label => 'VariousRSSReader', :name => 'RSSReader', :type => :full}
|
201
|
+
obj[:category] = :misc
|
202
|
+
DATASET[obj[:label]] = obj
|
203
|
+
obj = {:label => 'HTTPLibrary', :name => 'HTTP Library', :type => :full}
|
204
|
+
obj[:category] = :misc
|
205
|
+
DATASET[obj[:label]] = obj
|
206
|
+
obj = {:label => 'GoogleBot', :name => 'Googlebot', :type => :full}
|
207
|
+
obj[:category] = :crawler
|
208
|
+
DATASET[obj[:label]] = obj
|
209
|
+
obj = {:label => 'GoogleBotMobile', :name => 'Googlebot Mobile', :type => :full}
|
210
|
+
obj[:category] = :crawler
|
211
|
+
DATASET[obj[:label]] = obj
|
212
|
+
obj = {:label => 'GoogleMediaPartners', :name => 'Google Mediapartners', :type => :full}
|
213
|
+
obj[:category] = :crawler
|
214
|
+
DATASET[obj[:label]] = obj
|
215
|
+
obj = {:label => 'GoogleFeedFetcher', :name => 'Google Feedfetcher', :type => :full}
|
216
|
+
obj[:category] = :crawler
|
217
|
+
DATASET[obj[:label]] = obj
|
218
|
+
obj = {:label => 'GoogleAppEngine', :name => 'Google AppEngine', :type => :full}
|
219
|
+
obj[:category] = :crawler
|
220
|
+
DATASET[obj[:label]] = obj
|
221
|
+
obj = {:label => 'GoogleWebPreview', :name => 'Google Web Preview', :type => :full}
|
222
|
+
obj[:category] = :crawler
|
223
|
+
DATASET[obj[:label]] = obj
|
224
|
+
obj = {:label => 'YahooSlurp', :name => 'Yahoo! Slurp', :type => :full}
|
225
|
+
obj[:category] = :crawler
|
226
|
+
DATASET[obj[:label]] = obj
|
227
|
+
obj = {:label => 'YahooJP', :name => 'Yahoo! Japan', :type => :full}
|
228
|
+
obj[:category] = :crawler
|
229
|
+
DATASET[obj[:label]] = obj
|
230
|
+
obj = {:label => 'YahooPipes', :name => 'Yahoo! Pipes', :type => :full}
|
231
|
+
obj[:category] = :crawler
|
232
|
+
DATASET[obj[:label]] = obj
|
233
|
+
obj = {:label => 'Baiduspider', :name => 'Baiduspider', :type => :full}
|
234
|
+
obj[:category] = :crawler
|
235
|
+
DATASET[obj[:label]] = obj
|
236
|
+
obj = {:label => 'msnbot', :name => 'msnbot', :type => :full}
|
237
|
+
obj[:category] = :crawler
|
238
|
+
DATASET[obj[:label]] = obj
|
239
|
+
obj = {:label => 'bingbot', :name => 'bingbot', :type => :full}
|
240
|
+
obj[:category] = :crawler
|
241
|
+
DATASET[obj[:label]] = obj
|
242
|
+
obj = {:label => 'Yeti', :name => 'Naver Yeti', :type => :full}
|
243
|
+
obj[:category] = :crawler
|
244
|
+
DATASET[obj[:label]] = obj
|
245
|
+
obj = {:label => 'FeedBurner', :name => 'Google FeedBurner', :type => :full}
|
246
|
+
obj[:category] = :crawler
|
247
|
+
DATASET[obj[:label]] = obj
|
248
|
+
obj = {:label => 'facebook', :name => 'facebook', :type => :full}
|
249
|
+
obj[:category] = :crawler
|
250
|
+
DATASET[obj[:label]] = obj
|
251
|
+
obj = {:label => 'mixi', :name => 'mixi', :type => :full}
|
252
|
+
obj[:category] = :crawler
|
253
|
+
DATASET[obj[:label]] = obj
|
254
|
+
obj = {:label => 'IndyLibrary', :name => 'Indy Library', :type => :full}
|
255
|
+
obj[:category] = :crawler
|
256
|
+
DATASET[obj[:label]] = obj
|
257
|
+
obj = {:label => 'ApplePubSub', :name => 'Apple iCloud', :type => :full}
|
258
|
+
obj[:category] = :crawler
|
259
|
+
DATASET[obj[:label]] = obj
|
260
|
+
obj = {:label => 'Hatena', :name => 'Hatena', :type => :full}
|
261
|
+
obj[:category] = :crawler
|
262
|
+
DATASET[obj[:label]] = obj
|
263
|
+
obj = {:label => 'gooIchiro', :name => 'goo', :type => :full}
|
264
|
+
obj[:category] = :crawler
|
265
|
+
DATASET[obj[:label]] = obj
|
266
|
+
obj = {:label => 'livedoorFeedFetcher', :name => 'livedoor FeedFetcher', :type => :full}
|
267
|
+
obj[:category] = :crawler
|
268
|
+
DATASET[obj[:label]] = obj
|
269
|
+
obj = {:label => 'VariousCrawler', :name => 'misc crawler', :type => :full}
|
270
|
+
obj[:category] = :crawler
|
271
|
+
DATASET[obj[:label]] = obj
|
272
|
+
|
273
|
+
DATASET.freeze
|
274
|
+
|
275
|
+
def self.get(label)
|
276
|
+
DATASET[label]
|
277
|
+
end
|
278
|
+
end
|
data/lib/woothee/misc.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'woothee/dataset'
|
4
|
+
require 'woothee/util'
|
5
|
+
|
6
|
+
module Woothee::Misc
|
7
|
+
extend Woothee::Util
|
8
|
+
|
9
|
+
def self.challenge_desktoptools(ua, result)
|
10
|
+
data = case
|
11
|
+
when ua.index('AppleSyndication/') then Woothee::DataSet.get('SafariRSSReader')
|
12
|
+
when ua.index('compatible; Google Desktop/') then Woothee::DataSet.get('GoogleDesktop')
|
13
|
+
when ua.index('Windows-RSS-Platform') then Woothee::DataSet.get('WindowsRSSReader')
|
14
|
+
else nil
|
15
|
+
end
|
16
|
+
return false unless data
|
17
|
+
|
18
|
+
update_map(result, data)
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.challenge_smartphone_patterns(ua, result)
|
23
|
+
if ua.index('CFNetwork/')
|
24
|
+
data = Woothee::DataSet.get('iOS')
|
25
|
+
update_category(result, data[Woothee::KEY_CATEGORY])
|
26
|
+
update_os(result, data[Woothee::KEY_NAME])
|
27
|
+
return true
|
28
|
+
end
|
29
|
+
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.challenge_http_library(ua, result)
|
34
|
+
data,version = case
|
35
|
+
when ua =~ /^(?:Apache-HttpClient\/|Jakarta Commons-HttpClient\/|Java\/)/o
|
36
|
+
[Woothee::DataSet.get('HTTPLibrary'), 'Java']
|
37
|
+
when ua =~ /^Wget/o
|
38
|
+
[Woothee::DataSet.get('HTTPLibrary'), 'wget']
|
39
|
+
when ua =~ /^(?:libwww-perl|WWW-Mechanize|LWP::Simple|LWP |lwp-trivial)/o
|
40
|
+
[Woothee::DataSet.get('HTTPLibrary'), 'perl']
|
41
|
+
when ua =~ /^Python-urllib\//o
|
42
|
+
[Woothee::DataSet.get('HTTPLibrary'), 'python']
|
43
|
+
when ua =~ /^(:?PHP\/|WordPress\/|CakePHP|PukiWiki\/)/o
|
44
|
+
[Woothee::DataSet.get('HTTPLibrary'), 'php']
|
45
|
+
when ua.index('PEAR HTTP_Request class;')
|
46
|
+
[Woothee::DataSet.get('HTTPLibrary'), 'php']
|
47
|
+
else [nil,nil]
|
48
|
+
end
|
49
|
+
return false unless data
|
50
|
+
|
51
|
+
update_map(result, data)
|
52
|
+
update_version(result, version)
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.challenge_maybe_rss_reader(ua, result)
|
57
|
+
data = if ua =~ /rss(?:reader|bar|[-_ \/;()])/oi
|
58
|
+
Woothee::DataSet.get('VariousRSSReader')
|
59
|
+
else
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
return false unless data
|
63
|
+
|
64
|
+
update_map(result, data)
|
65
|
+
true
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'woothee/dataset'
|
4
|
+
require 'woothee/util'
|
5
|
+
|
6
|
+
module Woothee::MobilePhone
|
7
|
+
extend Woothee::Util
|
8
|
+
|
9
|
+
def self.challenge_docomo(ua, result)
|
10
|
+
return false if ua.index('DoCoMo').nil? and ua.index(';FOMA;').nil?
|
11
|
+
|
12
|
+
version = if ua =~ /DoCoMo\/[.0-9]+[ \/]([^- \/;()"']+)/o
|
13
|
+
$1
|
14
|
+
elsif ua =~ /\(([^;)]+);FOMA;/o
|
15
|
+
$1
|
16
|
+
else
|
17
|
+
Woothee::VALUE_UNKNOWN
|
18
|
+
end
|
19
|
+
update_map(result, Woothee::DataSet.get('docomo'))
|
20
|
+
update_version(result, version)
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.challenge_au(ua, result)
|
25
|
+
return false if ua.index('KDDI-').nil?
|
26
|
+
|
27
|
+
# fix Woothee::OS.challenge_mobile if you want to fix here
|
28
|
+
version = if ua =~ /KDDI-([^- \/;()"']+)/o
|
29
|
+
$1
|
30
|
+
else
|
31
|
+
Woothee::VALUE_UNKNOWN
|
32
|
+
end
|
33
|
+
update_map(result, Woothee::DataSet.get('au'))
|
34
|
+
update_version(result, version)
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.challenge_softbank(ua, result)
|
39
|
+
return false if ua.index('SoftBank').nil? and ua.index('Vodafone').nil? and ua.index('J-PHONE').nil?
|
40
|
+
|
41
|
+
version = if ua =~ /(?:SoftBank|Vodafone|J-PHONE)\/[.0-9]+\/([^ \/;()]+)/o
|
42
|
+
$1
|
43
|
+
else
|
44
|
+
Woothee::VALUE_UNKNOWN
|
45
|
+
end
|
46
|
+
update_map(result, Woothee::DataSet.get('SoftBank'))
|
47
|
+
update_version(result, version)
|
48
|
+
true
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.challenge_willcom(ua, result)
|
52
|
+
return false if ua.index('WILLCOM').nil? and ua.index('DDIPOCKET').nil?
|
53
|
+
|
54
|
+
# fix Woothee::OS.challenge_mobile if you want to fix here
|
55
|
+
version = if ua =~ /(?:WILLCOM|DDIPOCKET);[^\/]+\/([^ \/;()]+)/o
|
56
|
+
$1
|
57
|
+
else
|
58
|
+
Woothee::VALUE_UNKNOWN
|
59
|
+
end
|
60
|
+
update_map(result, Woothee::DataSet.get('willcom'))
|
61
|
+
update_version(result, version)
|
62
|
+
true
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.challenge_misc(ua, result)
|
66
|
+
if ua.index('jig browser')
|
67
|
+
update_map(result, Woothee::DataSet.get('jig'))
|
68
|
+
if ua =~ /jig browser[^;]+; ([^);]+)/o
|
69
|
+
update_version(result, $1)
|
70
|
+
end
|
71
|
+
return true
|
72
|
+
end
|
73
|
+
if ua.index('emobile/') or ua.index('OpenBrowser') or ua.index('Browser/Obigo-Browser')
|
74
|
+
update_map(result, Woothee::DataSet.get('emobile'))
|
75
|
+
return true
|
76
|
+
end
|
77
|
+
if ua.index('SymbianOS')
|
78
|
+
update_map(result, Woothee::DataSet.get('SymbianOS'))
|
79
|
+
return true
|
80
|
+
end
|
81
|
+
if ua.index('Hatena-Mobile-Gateway/')
|
82
|
+
update_map(result, Woothee::DataSet.get('MobileTranscoder'))
|
83
|
+
update_version(result, 'Hatena')
|
84
|
+
return true
|
85
|
+
end
|
86
|
+
if ua.index('livedoor-Mobile-Gateway/')
|
87
|
+
update_map(result, Woothee::DataSet.get('MobileTranscoder'))
|
88
|
+
update_version(result, 'livedoor')
|
89
|
+
return true
|
90
|
+
end
|
91
|
+
|
92
|
+
false
|
93
|
+
end
|
94
|
+
end
|
data/lib/woothee/os.rb
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'woothee/dataset'
|
4
|
+
require 'woothee/util'
|
5
|
+
|
6
|
+
module Woothee::OS
|
7
|
+
extend Woothee::Util
|
8
|
+
|
9
|
+
def self.challenge_windows(ua, result)
|
10
|
+
return false if ua.index("Windows").nil?
|
11
|
+
|
12
|
+
data = Woothee::DataSet.get('Win')
|
13
|
+
unless ua =~ /Windows ([ .a-zA-Z0-9]+)[;\\)]/o
|
14
|
+
# Windows, but version unknown
|
15
|
+
update_category(result, data[Woothee::KEY_CATEGORY])
|
16
|
+
update_os(result, data[Woothee::KEY_NAME])
|
17
|
+
return true
|
18
|
+
end
|
19
|
+
|
20
|
+
version = $1
|
21
|
+
data = case
|
22
|
+
when version == 'NT 6.2' then Woothee::DataSet.get('Win8')
|
23
|
+
when version == 'NT 6.1' then Woothee::DataSet.get('Win7')
|
24
|
+
when version == 'NT 6.0' then Woothee::DataSet.get('WinVista')
|
25
|
+
when version == 'NT 5.1' then Woothee::DataSet.get('WinXP')
|
26
|
+
when version =~ /^Phone OS/o then Woothee::DataSet.get('WinPhone')
|
27
|
+
when version == 'NT 5.0' then Woothee::DataSet.get('Win2000')
|
28
|
+
when version == 'NT 4.0' then Woothee::DataSet.get('WinNT4')
|
29
|
+
when version == '98' then Woothee::DataSet.get('Win98') # wow, WinMe is shown as 'Windows 98; Win9x 4.90', fxxxk
|
30
|
+
when version == '95' then Woothee::DataSet.get('Win95')
|
31
|
+
when version == 'CE' then Woothee::DataSet.get('WinCE')
|
32
|
+
else
|
33
|
+
data # windows unknown version
|
34
|
+
end
|
35
|
+
update_category(result, data[Woothee::KEY_CATEGORY])
|
36
|
+
update_os(result, data[Woothee::KEY_NAME])
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.challenge_osx(ua, result)
|
41
|
+
return false if ua.index('Mac OS X').nil?
|
42
|
+
|
43
|
+
data = Woothee::DataSet.get('OSX')
|
44
|
+
if ua.index('like Mac OS X')
|
45
|
+
data = case
|
46
|
+
when ua.index('iPhone;') then Woothee::DataSet.get('iPhone')
|
47
|
+
when ua.index('iPad;') then Woothee::DataSet.get('iPad')
|
48
|
+
when ua.index('iPod;') then Woothee::DataSet.get('iPod')
|
49
|
+
else data
|
50
|
+
end
|
51
|
+
end
|
52
|
+
update_category(result, data[Woothee::KEY_CATEGORY])
|
53
|
+
update_os(result, data[Woothee::KEY_NAME])
|
54
|
+
true
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.challenge_linux(ua, result)
|
58
|
+
return false if ua.index('Linux').nil?
|
59
|
+
|
60
|
+
data = if ua.index('Android')
|
61
|
+
Woothee::DataSet.get('Android')
|
62
|
+
else
|
63
|
+
Woothee::DataSet.get('Linux')
|
64
|
+
end
|
65
|
+
update_category(result, data[Woothee::KEY_CATEGORY])
|
66
|
+
update_os(result, data[Woothee::KEY_NAME])
|
67
|
+
true
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.challenge_smartphone(ua, result)
|
71
|
+
data = case
|
72
|
+
when ua.index('iPhone') then Woothee::DataSet.get('iPhone')
|
73
|
+
when ua.index('iPad') then Woothee::DataSet.get('iPad')
|
74
|
+
when ua.index('iPod') then Woothee::DataSet.get('iPod')
|
75
|
+
when ua.index('Android') then Woothee::DataSet.get('Android')
|
76
|
+
when ua.index('CFNetwork') then Woothee::DataSet.get('iOS')
|
77
|
+
when ua.index('BlackBerry') then Woothee::DataSet.get('BlackBerry')
|
78
|
+
else nil
|
79
|
+
end
|
80
|
+
return false unless data
|
81
|
+
|
82
|
+
update_category(result, data[Woothee::KEY_CATEGORY])
|
83
|
+
update_os(result, data[Woothee::KEY_NAME])
|
84
|
+
true
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.challenge_mobilephone(ua, result)
|
88
|
+
if ua.index('KDDI-')
|
89
|
+
if ua =~ /KDDI-([^- \/;()"']+)/o
|
90
|
+
term = $1
|
91
|
+
data = Woothee::DataSet.get('au')
|
92
|
+
update_category(result, data[Woothee::KEY_CATEGORY])
|
93
|
+
update_os(result, data[Woothee::KEY_OS])
|
94
|
+
update_version(result, term)
|
95
|
+
return true
|
96
|
+
end
|
97
|
+
end
|
98
|
+
if ua.index('WILLCOM') or ua.index('DDIPOCKET')
|
99
|
+
if ua =~ /(?:WILLCOM|DDIPOCKET);[^\/]+\/([^ \/;()]+)/o
|
100
|
+
term = $1
|
101
|
+
data = Woothee::DataSet.get('willcom')
|
102
|
+
update_category(result, data[Woothee::KEY_CATEGORY])
|
103
|
+
update_os(result, data[Woothee::KEY_OS])
|
104
|
+
update_version(result, term)
|
105
|
+
return true
|
106
|
+
end
|
107
|
+
end
|
108
|
+
if ua.index('SymbianOS')
|
109
|
+
data = Woothee::DataSet.get('SymbianOS')
|
110
|
+
update_category(result, data[Woothee::KEY_CATEGORY])
|
111
|
+
update_os(result, data[Woothee::KEY_OS])
|
112
|
+
return true
|
113
|
+
end
|
114
|
+
if ua.index('Google Wireless Transcoder')
|
115
|
+
update_map(result, Woothee::DataSet.get('MobileTranscoder'))
|
116
|
+
update_version(result, 'Google')
|
117
|
+
return true
|
118
|
+
end
|
119
|
+
if ua.index('Naver Transcoder')
|
120
|
+
update_map(result, Woothee::DataSet.get('MobileTranscoder'))
|
121
|
+
update_version(result, 'Naver')
|
122
|
+
return true
|
123
|
+
end
|
124
|
+
|
125
|
+
false
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.challenge_appliance(ua, result)
|
129
|
+
if ua.index('Nintendo DSi;')
|
130
|
+
data = Woothee::DataSet.get('NintendoDSi')
|
131
|
+
update_category(result, data[Woothee::KEY_CATEGORY])
|
132
|
+
update_os(result, data[Woothee::KEY_OS])
|
133
|
+
return true
|
134
|
+
end
|
135
|
+
if ua.index('Nintendo Wii;')
|
136
|
+
data = Woothee::DataSet.get('NintendoWii')
|
137
|
+
update_category(result, data[Woothee::KEY_CATEGORY])
|
138
|
+
update_os(result, data[Woothee::KEY_OS])
|
139
|
+
return true
|
140
|
+
end
|
141
|
+
|
142
|
+
false
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.challenge_misc(ua, result)
|
146
|
+
data = case
|
147
|
+
when ua.index('(Win98;')
|
148
|
+
Woothee::DataSet.get('Win98')
|
149
|
+
when ua.index('Macintosh; U; PPC;')
|
150
|
+
Woothee::DataSet.get('MacOS')
|
151
|
+
when ua.index('Mac_PowerPC')
|
152
|
+
Woothee::DataSet.get('MacOS')
|
153
|
+
when ua.index('X11; FreeBSD ')
|
154
|
+
Woothee::DataSet.get('BSD')
|
155
|
+
else
|
156
|
+
nil
|
157
|
+
end
|
158
|
+
if data
|
159
|
+
update_category(result, data[Woothee::KEY_CATEGORY])
|
160
|
+
update_os(result, data[Woothee::KEY_NAME])
|
161
|
+
return true
|
162
|
+
end
|
163
|
+
|
164
|
+
false
|
165
|
+
end
|
166
|
+
end
|
data/lib/woothee/util.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module Woothee::Util
|
4
|
+
def update_map(target, source)
|
5
|
+
source.keys.each do |key|
|
6
|
+
next if key == Woothee::KEY_LABEL or key == Woothee::KEY_TYPE
|
7
|
+
if source[key].length > 0
|
8
|
+
target[key] = source[key]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def update_category(target, category)
|
14
|
+
target[Woothee::ATTRIBUTE_CATEGORY] = category
|
15
|
+
end
|
16
|
+
|
17
|
+
def update_version(target, version)
|
18
|
+
target[Woothee::ATTRIBUTE_VERSION] = version
|
19
|
+
end
|
20
|
+
|
21
|
+
def update_os(target, os)
|
22
|
+
target[Woothee::ATTRIBUTE_OS] = os
|
23
|
+
end
|
24
|
+
end
|
data/lib/woothee.rb
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'woothee/dataset'
|
4
|
+
require 'woothee/browser'
|
5
|
+
require 'woothee/os'
|
6
|
+
require 'woothee/mobilephone'
|
7
|
+
require 'woothee/crawler'
|
8
|
+
require 'woothee/appliance'
|
9
|
+
require 'woothee/misc'
|
10
|
+
|
11
|
+
module Woothee
|
12
|
+
def self.parse(useragent)
|
13
|
+
fill_result(exec_parse(useragent))
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.is_crawler(useragent)
|
17
|
+
useragent.length > 0 and useragent != '-' and try_crawler(useragent, {})
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.exec_parse(useragent)
|
21
|
+
result = {}
|
22
|
+
|
23
|
+
return result if useragent.length < 1 or useragent == '-'
|
24
|
+
|
25
|
+
if try_crawler(useragent, result)
|
26
|
+
return result
|
27
|
+
end
|
28
|
+
|
29
|
+
if try_browser(useragent, result)
|
30
|
+
if try_os(useragent, result)
|
31
|
+
return result
|
32
|
+
else
|
33
|
+
return result
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
if try_mobilephone(useragent, result)
|
38
|
+
return result
|
39
|
+
end
|
40
|
+
|
41
|
+
if try_appliance(useragent, result)
|
42
|
+
return result
|
43
|
+
end
|
44
|
+
|
45
|
+
if try_misc(useragent, result)
|
46
|
+
return result
|
47
|
+
end
|
48
|
+
|
49
|
+
# browser unknown. check os only
|
50
|
+
if try_os(useragent, result)
|
51
|
+
return result
|
52
|
+
end
|
53
|
+
|
54
|
+
if try_rare_cases(useragent, result)
|
55
|
+
return result
|
56
|
+
end
|
57
|
+
|
58
|
+
result
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.try_crawler(useragent, result)
|
62
|
+
return true if Woothee::Crawler.challenge_google(useragent, result)
|
63
|
+
|
64
|
+
return true if Woothee::Crawler.challenge_crawlers(useragent, result)
|
65
|
+
|
66
|
+
false
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.try_browser(useragent, result)
|
70
|
+
return true if Woothee::Browser.challenge_msie(useragent, result)
|
71
|
+
|
72
|
+
return true if Woothee::Browser.challenge_safari_chrome(useragent, result)
|
73
|
+
|
74
|
+
return true if Woothee::Browser.challenge_firefox(useragent, result)
|
75
|
+
|
76
|
+
return true if Woothee::Browser.challenge_opera(useragent, result)
|
77
|
+
|
78
|
+
false
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.try_os(useragent, result)
|
82
|
+
return true if Woothee::OS.challenge_windows(useragent, result)
|
83
|
+
|
84
|
+
# OSX PC and iOS devices (strict check)
|
85
|
+
return true if Woothee::OS.challenge_osx(useragent, result)
|
86
|
+
|
87
|
+
# Linux PC and Android
|
88
|
+
return true if Woothee::OS.challenge_linux(useragent, result)
|
89
|
+
|
90
|
+
# all useragents matches /(iPhone|iPad|iPod|Android|BlackBerry)/
|
91
|
+
return true if Woothee::OS.challenge_smartphone(useragent, result)
|
92
|
+
|
93
|
+
# mobile phones like KDDI-.*
|
94
|
+
return true if Woothee::OS.challenge_mobilephone(useragent, result)
|
95
|
+
|
96
|
+
# Nintendo DSi/Wii with Opera
|
97
|
+
return true if Woothee::OS.challenge_appliance(useragent, result)
|
98
|
+
|
99
|
+
# Win98, BSD, classic MacOS, ...
|
100
|
+
return true if Woothee::OS.challenge_misc(useragent, result)
|
101
|
+
|
102
|
+
false
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.try_mobilephone(useragent, result)
|
106
|
+
return true if Woothee::MobilePhone.challenge_docomo(useragent, result)
|
107
|
+
|
108
|
+
return true if Woothee::MobilePhone.challenge_au(useragent, result)
|
109
|
+
|
110
|
+
return true if Woothee::MobilePhone.challenge_softbank(useragent, result)
|
111
|
+
|
112
|
+
return true if Woothee::MobilePhone.challenge_willcom(useragent, result)
|
113
|
+
|
114
|
+
return true if Woothee::MobilePhone.challenge_misc(useragent, result)
|
115
|
+
|
116
|
+
false
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.try_appliance(useragent, result)
|
120
|
+
return true if Woothee::Appliance.challenge_playstation(useragent, result)
|
121
|
+
|
122
|
+
return true if Woothee::Appliance.challenge_nintendo(useragent, result)
|
123
|
+
|
124
|
+
return true if Woothee::Appliance.challenge_digitaltv(useragent, result)
|
125
|
+
|
126
|
+
false
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.try_misc(useragent, result)
|
130
|
+
return true if Woothee::Misc.challenge_desktoptools(useragent, result)
|
131
|
+
|
132
|
+
false
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.try_rare_cases(useragent, result)
|
136
|
+
return true if Woothee::Misc.challenge_smartphone_patterns(useragent, result)
|
137
|
+
|
138
|
+
return true if Woothee::Browser.challenge_sleipnir(useragent, result)
|
139
|
+
|
140
|
+
return true if Woothee::Misc.challenge_http_library(useragent, result)
|
141
|
+
|
142
|
+
return true if Woothee::Misc.challenge_maybe_rss_reader(useragent, result)
|
143
|
+
|
144
|
+
return true if Woothee::Crawler.challenge_maybe_crawler(useragent, result)
|
145
|
+
|
146
|
+
false
|
147
|
+
end
|
148
|
+
|
149
|
+
FILLED = {
|
150
|
+
Woothee::ATTRIBUTE_NAME => Woothee::VALUE_UNKNOWN,
|
151
|
+
Woothee::ATTRIBUTE_CATEGORY => Woothee::VALUE_UNKNOWN,
|
152
|
+
Woothee::ATTRIBUTE_OS => Woothee::VALUE_UNKNOWN,
|
153
|
+
Woothee::ATTRIBUTE_VERSION => Woothee::VALUE_UNKNOWN,
|
154
|
+
Woothee::ATTRIBUTE_VENDOR => Woothee::VALUE_UNKNOWN,
|
155
|
+
}.freeze
|
156
|
+
def self.fill_result(result)
|
157
|
+
FILLED.merge(result)
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
$LOAD_PATH.push '../lib' unless $LOAD_PATH.include?('../lib')
|
4
|
+
|
5
|
+
module Woothee
|
6
|
+
end
|
7
|
+
|
8
|
+
describe Woothee do
|
9
|
+
it "should be read from each modules correctly" do
|
10
|
+
lambda { require 'woothee/dataset' }.should_not raise_error(LoadError)
|
11
|
+
lambda { require 'woothee' }.should_not raise_error(LoadError)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
$LOAD_PATH.push '../lib' unless $LOAD_PATH.include?('../lib')
|
4
|
+
|
5
|
+
require 'woothee/dataset'
|
6
|
+
|
7
|
+
describe Woothee::DataSet do
|
8
|
+
it "contains constants" do
|
9
|
+
lambda { Woothee::ATTRIBUTE_NAME }.should_not raise_error(NameError)
|
10
|
+
Woothee::ATTRIBUTE_NAME.should eql(:name)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "contains dataset" do
|
14
|
+
Woothee::DataSet.get('GoogleBot')[:name].should eql('Googlebot')
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
$LOAD_PATH.push '../lib' unless $LOAD_PATH.include?('../lib')
|
4
|
+
|
5
|
+
require 'yaml'
|
6
|
+
require 'woothee'
|
7
|
+
|
8
|
+
TESTSET_DIR = File.dirname(__FILE__) + "/../../testsets/";
|
9
|
+
|
10
|
+
TARGETS = [
|
11
|
+
['crawler.yaml','Crawler'],['crawler_google.yaml','Crawler/Google'],
|
12
|
+
['pc_windows.yaml','PC/Windows'],['pc_misc.yaml','PC/Misc'],
|
13
|
+
['mobilephone_docomo.yaml','MobilePhone/docomo'],['mobilephone_au.yaml','MobilePhone/au'],
|
14
|
+
['mobilephone_softbank.yaml','MobilePhone/softbank'],['mobilephone_willcom.yaml','MobilePhone/willcom'],
|
15
|
+
['mobilephone_misc.yaml','MobilePhone/misc'],
|
16
|
+
['smartphone_ios.yaml','SmartPhone/ios'],['smartphone_android.yaml','SmartPhone/android'],
|
17
|
+
['smartphone_misc.yaml','SmartPhone/misc'],
|
18
|
+
['appliance.yaml','Appliance'],
|
19
|
+
['pc_lowpriority.yaml','PC/LowPriority'],
|
20
|
+
['misc.yaml','Misc'],
|
21
|
+
['crawler_nonmajor.yaml','Crawler/NonMajor'],
|
22
|
+
]
|
23
|
+
|
24
|
+
describe Woothee do
|
25
|
+
|
26
|
+
TARGETS.each do |filename,groupname|
|
27
|
+
YAML.load_file(TESTSET_DIR + filename).each do |e|
|
28
|
+
r = Woothee.parse(e['target'])
|
29
|
+
[:name, :category, :os, :version, :vendor].each do |attribute|
|
30
|
+
it groupname + ("test(%s): %s" % [attribute, e['target']]) do
|
31
|
+
if [:name, :category].include?(attribute) or ([:os, :version, :vendor].include?(attribute) and e.has_key?(attribute.to_s))
|
32
|
+
r[attribute].to_s.should eql(e[attribute.to_s])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
data/woothee.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# $:.push File.expand_path('../lib', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = "woothee"
|
6
|
+
gem.description = "Cross-language UserAgent classifier library, ruby implementation"
|
7
|
+
gem.homepage = "https://github.com/tagomoris/woothee"
|
8
|
+
gem.summary = gem.description
|
9
|
+
gem.version = "0.2.0"
|
10
|
+
gem.authors = ["TAGOMORI Satoshi"]
|
11
|
+
gem.email = "tagomoris@gmail.com"
|
12
|
+
gem.has_rdoc = false
|
13
|
+
#gem.platform = Gem::Platform::RUBY
|
14
|
+
gem.files = `git ls-files`.split("\n")
|
15
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
gem.require_paths = ['lib']
|
18
|
+
|
19
|
+
gem.add_development_dependency "rspec", ">= 2.8.0"
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: woothee
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- TAGOMORI Satoshi
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-06-13 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.8.0
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 2.8.0
|
30
|
+
description: Cross-language UserAgent classifier library, ruby implementation
|
31
|
+
email: tagomoris@gmail.com
|
32
|
+
executables: []
|
33
|
+
extensions: []
|
34
|
+
extra_rdoc_files: []
|
35
|
+
files:
|
36
|
+
- Gemfile
|
37
|
+
- Makefile
|
38
|
+
- Rakefile
|
39
|
+
- lib/woothee.rb
|
40
|
+
- lib/woothee/appliance.rb
|
41
|
+
- lib/woothee/browser.rb
|
42
|
+
- lib/woothee/crawler.rb
|
43
|
+
- lib/woothee/dataset.rb
|
44
|
+
- lib/woothee/misc.rb
|
45
|
+
- lib/woothee/mobilephone.rb
|
46
|
+
- lib/woothee/os.rb
|
47
|
+
- lib/woothee/util.rb
|
48
|
+
- spec/00_valid_spec.rb
|
49
|
+
- spec/01_dataset_spec.rb
|
50
|
+
- spec/02_run_testsets_spec.rb
|
51
|
+
- woothee.gemspec
|
52
|
+
homepage: https://github.com/tagomoris/woothee
|
53
|
+
licenses: []
|
54
|
+
post_install_message:
|
55
|
+
rdoc_options: []
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ! '>='
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
requirements: []
|
71
|
+
rubyforge_project:
|
72
|
+
rubygems_version: 1.8.21
|
73
|
+
signing_key:
|
74
|
+
specification_version: 3
|
75
|
+
summary: Cross-language UserAgent classifier library, ruby implementation
|
76
|
+
test_files:
|
77
|
+
- spec/00_valid_spec.rb
|
78
|
+
- spec/01_dataset_spec.rb
|
79
|
+
- spec/02_run_testsets_spec.rb
|