1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 __doc__ = """
21 This module contains the supporting classes for the Two Step Analysis user agent
22 algorithm that is used as the primary way to match user agents with the Java API
23 for the WURFL.
24
25 A description of the way the following source is intended to work can be found
26 within the source for the original Java API implementation here:
27 http://sourceforge.net/projects/wurfl/files/WURFL Java API/
28
29 The original Java code is GPLd and Copyright (c) WURFL-Pro srl
30 """
31
32 __author__ = "Armand Lynch <lyncha@users.sourceforge.net>"
33 __copyright__ = "Copyright 2011, Armand Lynch"
34 __license__ = "LGPL"
35 __url__ = "http://celljam.net/"
36 __version__ = "1.2.1"
37
38 import re
39
40 from pywurfl.algorithms.wurfl.utils import (first_semi_colon, first_slash,
41 first_space, is_mobile_browser,
42 second_slash, third_space)
43 from pywurfl.algorithms.wurfl.utils import indexof_or_length as iol
44 from pywurfl.algorithms.wurfl import normalizers
45 from pywurfl.algorithms.wurfl.strategies import ld_match, ris_match
49 user_agent_map = {}
50
52 self.normalizer = normalizer
53 self.known_user_agents = set()
54
55 - def add(self, user_agent, wurfl_id):
56 self.known_user_agents.add(user_agent)
57 self.user_agent_map[user_agent] = wurfl_id
58
59 @property
61 return sorted(self.known_user_agents)
62
64 raise NotImplementedError
65
67 normalized_user_agent = self.normalizer(user_agent)
68 devid = self.conclusive_match(normalized_user_agent)
69 if not devid or devid == u"generic":
70 devid = self.recovery_match(normalized_user_agent)
71 if not devid or devid == u"generic":
72 devid = self.catch_all_recovery_match(user_agent)
73 return devid
74
80
86
89
90 recovery_map = (
91
92 (u"UP.Browser/7.2", u"opwv_v72_generic"),
93 (u"UP.Browser/7", u"opwv_v7_generic"),
94 (u"UP.Browser/6.2", u"opwv_v62_generic"),
95 (u"UP.Browser/6", u"opwv_v6_generic"),
96 (u"UP.Browser/5", u"upgui_generic"),
97 (u"UP.Browser/4", u"uptext_generic"),
98 (u"UP.Browser/3", u"uptext_generic"),
99
100
101 (u"Series60", u"nokia_generic_series60"),
102
103
104 (u"NetFront/3.0", u"generic_netfront_ver3"),
105 (u"ACS-NF/3.0", u"generic_netfront_ver3"),
106 (u"NetFront/3.1", u"generic_netfront_ver3_1"),
107 (u"ACS-NF/3.1", u"generic_netfront_ver3_1"),
108 (u"NetFront/3.2", u"generic_netfront_ver3_2"),
109 (u"ACS-NF/3.2", u"generic_netfront_ver3_2"),
110 (u"NetFront/3.3", u"generic_netfront_ver3_3"),
111 (u"ACS-NF/3.3", u"generic_netfront_ver3_3"),
112 (u"NetFront/3.4", u"generic_netfront_ver3_4"),
113 (u"NetFront/3.5", u"generic_netfront_ver3_5"),
114 (u"NetFront/4.0", u"generic_netfront_ver4"),
115 (u"NetFront/4.1", u"generic_netfront_ver4_1"),
116
117
118 (u"Windows CE", u"generic_ms_mobile_browser_ver1"),
119
120
121 (u"Mozilla/4.0", u"generic_web_browser"),
122 (u"Mozilla/5.0", u"generic_web_browser"),
123 (u"Mozilla/6.0", u"generic_web_browser"),
124
125
126 (u"Mozilla/", u"generic_xhtml"),
127 (u"ObigoInternetBrowser/Q03C", u"generic_xhtml"),
128 (u"AU-MIC/2", u"generic_xhtml"),
129 (u"AU-MIC-", u"generic_xhtml"),
130 (u"AU-OBIGO/", u"generic_xhtml"),
131 (u"Obigo/Q03", u"generic_xhtml"),
132 (u"Obigo/Q04", u"generic_xhtml"),
133 (u"ObigoInternetBrowser/2", u"generic_xhtml"),
134 (u"Teleca Q03B1", u"generic_xhtml"),
135
136
137 (u"Opera Mini/1", u"browser_opera_mini_release1"),
138 (u"Opera Mini/2", u"browser_opera_mini_release2"),
139 (u"Opera Mini/3", u"browser_opera_mini_release3"),
140 (u"Opera Mini/4", u"browser_opera_mini_release4"),
141 (u"Opera Mini/5", u"browser_opera_mini_release5"),
142
143
144 (u"DoCoMo", u"docomo_generic_jap_ver1"),
145 (u"KDDI", u"docomo_generic_jap_ver1"))
146
148
149 match = u"generic"
150 for partial_agent, wdevice in self.recovery_map:
151 if partial_agent in user_agent:
152 match = wdevice
153 break
154 return match
155
158
161
165 return (user_agent.startswith(u"Alcatel") or
166 user_agent.startswith(u"ALCATEL"))
167
170
171 androids = {}
172 androids[u""] = u"generic_android"
173 androids[u"1_5"] = u"generic_android_ver1_5"
174 androids[u"1_6"] = u"generic_android_ver1_6"
175 androids[u"2_0"] = u"generic_android_ver2"
176 androids[u"2_1"] = u"generic_android_ver2_1"
177 androids[u"2_2"] = u"generic_android_ver2_2"
178
179 android_os_re = re.compile(r".*Android[\s/](\d)\.(\d)")
180
182 return user_agent.startswith(u"Mozilla") and u"Android" in user_agent
183
185 tolerance = iol(user_agent, u" ",
186 start_index=iol(user_agent, u"Android"))
187 match = self.ris_matcher(user_agent, tolerance)
188
189 return match
190
192 if u"Froyo" in user_agent:
193 return u"generic_android_ver2_2"
194 return self.androids.get(self.android_os_version(user_agent),
195 u"generic_android")
196
201
206
209 APPLE_LD_TOLERANCE = 5
210
212 return (u"iPhone" in user_agent or u"iPod" in user_agent or u"iPad" in
213 user_agent)
214
223
225 if u"iPad" in user_agent:
226 return "apple_ipad_ver1"
227 if u"iPod" in user_agent:
228 return u"apple_ipod_touch_ver1"
229 return u"apple_iphone_ver1"
230
234 return user_agent.startswith(u"BENQ") or user_agent.startswith(u"BenQ")
235
238 blackberries = {}
239 blackberries[u"2."] = u"blackberry_generic_ver2"
240 blackberries[u"3.2"] = u"blackberry_generic_ver3_sub2"
241 blackberries[u"3.3"] = u"blackberry_generic_ver3_sub30"
242 blackberries[u"3.5"] = u"blackberry_generic_ver3_sub50"
243 blackberries[u"3.6"] = u"blackberry_generic_ver3_sub60"
244 blackberries[u"3.7"] = u"blackberry_generic_ver3_sub70"
245 blackberries[u"4.1"] = u"blackberry_generic_ver4_sub10"
246 blackberries[u"4.2"] = u"blackberry_generic_ver4_sub20"
247 blackberries[u"4.3"] = u"blackberry_generic_ver4_sub30"
248 blackberries[u"4.5"] = u"blackberry_generic_ver4_sub50"
249 blackberries[u"4.6"] = u"blackberry_generic_ver4_sub60"
250 blackberries[u"4.7"] = u"blackberry_generic_ver4_sub70"
251 blackberries[u"4."] = u"blackberry_generic_ver4"
252 blackberries[u"5."] = u"blackberry_generic_ver5"
253 blackberries[u"6."] = u"blackberry_generic_ver6"
254
255 blackberry_os_re = re.compile(r".*Black[Bb]erry[^/\s]+/(\d\.\d)")
256
258 return u"BlackBerry" in user_agent or u"Blackberry" in user_agent
259
268
273
276 bots = (u"bot", u"crawler", u"spider", u"novarra", u"transcoder",
277 u"yahoo! searchmonkey", u"yahoo! slurp", u"feedfetcher-google",
278 u"toolbar", u"mowser", u"mediapartners-google", u"azureus",
279 u"inquisitor", u"baiduspider", u"baidumobaider", u"indy library",
280 u"slurp", u"crawl", u"wget", u"ucweblient", u"snoopy",
281 u"mozfdsilla", u"ask jeeves", u"jeeves/teoma", u"mechanize",
282 u"http client", u"servicemonitor", u"httpunit", u"hatena",
283 u"ichiro")
284
285 BOT_TOLERANCE = 4
286
288 user_agent = user_agent.lower()
289 for bot in self.bots:
290 if bot in user_agent:
291 return True
292 return False
293
297
299 return u"generic_web_crawler"
300
326
331
335 return user_agent.startswith(u"DoCoMo")
336
339
341 if user_agent.startswith(u"DoCoMo/2"):
342 return u"docomo_generic_jap_ver2"
343 return u"docomo_generic_jap_ver1"
344
349
353 return (user_agent.startswith(u"Grundig") or
354 user_agent.startswith(u"GRUNDIG"))
355
359 return user_agent.startswith(u"HTC") or u"XV6875.1" in user_agent
360
364 return u"KDDI" in user_agent
365
367 if user_agent.startswith(u"KDDI/"):
368 tolerance = second_slash(user_agent)
369 elif user_agent.startswith(u"KDDI"):
370 tolerance = first_slash(user_agent)
371 else:
372 tolerance = iol(user_agent, ")")
373 match = self.ris_matcher(user_agent, tolerance)
374
375 return match
376
378 if u"Opera" in user_agent:
379 return u"opera"
380 return u"opwv_v62_generic"
381
386
390 return (user_agent.startswith(u"kyocera") or
391 user_agent.startswith(u"QC-") or
392 user_agent.startswith(u"KWC-"))
393
397 return (user_agent.startswith(u"lg") or u"LG-" in user_agent or
398 u"LGE" in user_agent)
399
401 tolerance = iol(user_agent, u"/",
402 start_index=user_agent.upper().index(u"LG"))
403 match = self.ris_matcher(user_agent, tolerance)
404 return match
405
408 lgpluses = (
409 (u"generic_lguplus_rexos_facebook_browser",
410 (u"Windows NT 5", u"POLARIS")),
411 (u"generic_lguplus_rexos_webviewer_browser",
412 (u"Windows NT 5",)),
413 (u"generic_lguplus_winmo_facebook_browser",
414 (u"Windows CE", u"POLARIS")),
415 (u"generic_lguplus_android_webkit_browser",
416 (u"Android", u"AppleWebKit")))
417
419 return u"lgtelecom" in user_agent or u"LGUPLUS" in user_agent
420
423
425 for wid, searches in self.lgpluses:
426 for search in searches:
427 if search not in user_agent:
428 break
429 else:
430 return wid
431 return u"generic_lguplus"
432
436 return u"Maemo " in user_agent
437
439 tolerance = first_space(user_agent)
440 match = self.ris_matcher(user_agent, tolerance)
441 return match
442
446 return user_agent.startswith(u"Mitsu")
447
450 MOTOROLA_TOLERANCE = 5
451
453 return (user_agent.startswith(u"Mot-") or
454 u"MOT-" in user_agent or
455 u"Motorola" in user_agent)
456
465
467 match = u"generic"
468 if u"MIB/2.2" in user_agent or u"MIB/BER2.2" in user_agent:
469 match = u"mot_mib22_generic"
470 return match
471
475 return (not is_mobile_browser(user_agent) and
476 user_agent.startswith(u"Mozilla") and
477 u"MSIE" in user_agent)
478
481 NEC_LD_TOLERANCE = 2
482
484 return user_agent.startswith(u"NEC") or user_agent.startswith(u"KGT")
485
493
497 return u"Nokia" in user_agent
498
500 tol1 = iol(user_agent, u"/", start_index=user_agent.index(u"Nokia"))
501 tol2 = iol(user_agent, u" ", start_index=user_agent.index(u"Nokia"))
502 tolerance = tol1 if tol1 < tol2 else tol2
503
504 match = self.ris_matcher(user_agent, tolerance)
505
506 return match
507
509 match = u"generic"
510 if u"Series60" in user_agent:
511 match = u"nokia_generic_series60"
512 elif u"Series80" in user_agent:
513 match = u"nokia_generic_series80"
514 return match
515
541
545 return u"Opera Mini" in user_agent
546
548 match = u""
549 if u"Opera Mini/1" in user_agent:
550 match = u"browser_opera_mini_release1"
551 elif u"Opera Mini/2" in user_agent:
552 match = u"browser_opera_mini_release2"
553 elif u"Opera Mini/3" in user_agent:
554 match = u"browser_opera_mini_release3"
555 elif u"Opera Mini/4" in user_agent:
556 match = u"browser_opera_mini_release4"
557 elif u"Opera Mini/5" in user_agent:
558 match = u"browser_opera_mini_release5"
559 return match
560
564 return user_agent.startswith(u"Panasonic")
565
568 PANTECH_LD_TOLERANCE = 4
569
571 return (user_agent.startswith(u"Pantech") or
572 user_agent.startswith(u"PT-") or
573 user_agent.startswith(u"PANTECH") or
574 user_agent.startswith(u"PG-"))
575
583
587 return (user_agent.startswith(u"Philips") or
588 user_agent.startswith(u"PHILIPS"))
589
593 return user_agent.startswith(u"portalmmm")
594
597
601 return user_agent.startswith(u"Qtek")
602
606 return (not is_mobile_browser(user_agent) and
607 user_agent.startswith(u"Mozilla") and
608 u"Safari" in user_agent)
609
611 if u"Macintosh" in user_agent or u"Windows" in user_agent:
612 match = u"generic_web_browser"
613 else:
614 match = u"generic"
615 return match
616
620 return (user_agent.startswith(u"Sagem") or
621 user_agent.startswith(u"SAGEM"))
622
625 SAMSUNGS = [u"SEC-", u"SAMSUNG-", u"SCH", u"Samsung", u"SPH", u"SGH",
626 u"SAMSUNG/"]
627
629 return (u"Samsung/SGH" in user_agent or
630 u"Samsung" in user_agent or
631 user_agent.startswith(u"SEC-") or
632 user_agent.startswith(u"SAMSUNG") or
633 user_agent.startswith(u"SPH") or
634 user_agent.startswith(u"SGH") or
635 user_agent.startswith(u"SCH"))
636
638 for sams in self.SAMSUNGS:
639 if sams in user_agent:
640 tol1 = iol(user_agent, u"/", start_index=user_agent.index(sams))
641 tol2 = iol(user_agent, u" ", start_index=user_agent.index(sams))
642 tolerance = tol1 if tol1 < tol2 else tol2
643 break
644 else:
645 tolerance = len(user_agent)
646
647 match = self.ris_matcher(user_agent, tolerance)
648
649 return match
650
654 return (user_agent.startswith(u"Sanyo") or
655 user_agent.startswith(u"SANYO"))
656
660 return (user_agent.startswith(u"Sharp") or
661 user_agent.startswith(u"SHARP"))
662
666 return user_agent.startswith(u"SIE-")
667
671 return u"SonyEricsson" in user_agent
672
681
685 return u"SPV" in user_agent
686
688 tolerance = iol(user_agent, u";", start_index=iol(user_agent, u"SPV"))
689 match = self.ris_matcher(user_agent, tolerance)
690 return match
691
695 return user_agent.startswith(u"Toshiba")
696
700 return user_agent.startswith(u"Vodafone")
701
703 tolerance = iol(user_agent, u"/", 3)
704 match = self.ris_matcher(user_agent, tolerance)
705
706 return match
707
710 WINDOWS_CE_TOLERANCE = 3
711
713 return (u"Mozilla/" in user_agent and (u"Windows CE" in user_agent or
714 u"WindowsCE" in user_agent or
715 u"ZuneWP7" in user_agent))
716
720
722 return u"generic_ms_mobile_browser_ver1"
723
724
725 handlers = [NokiaMatcher(),
726 LGUPLUSMatcher(),
727 AndroidMatcher(normalizers.android),
728 SonyEricssonMatcher(),
729 MotorolaMatcher(),
730 BlackberryMatcher(),
731 SiemensMatcher(),
732 SagemMatcher(),
733 SamsungMatcher(),
734 PanasonicMatcher(),
735 NecMatcher(),
736 QtekMatcher(),
737 MitsubishiMatcher(),
738 PhilipsMatcher(),
739 LGMatcher(normalizers.lg),
740 AppleMatcher(),
741 KyoceraMatcher(),
742 AlcatelMatcher(),
743 SharpMatcher(),
744 SanyoMatcher(),
745 BenQMatcher(),
746 PantechMatcher(),
747 ToshibaMatcher(),
748 GrundigMatcher(),
749 HTCMatcher(),
750 BotMatcher(),
751 SPVMatcher(),
752 WindowsCEMatcher(),
753 PortalmmmMatcher(),
754 DoCoMoMatcher(),
755 KDDIMatcher(),
756 VodafoneMatcher(),
757 OperaMiniMatcher(),
758 MaemoMatcher(normalizers.maemo),
759 ChromeMatcher(normalizers.chrome),
760 AOLMatcher(),
761 OperaMatcher(),
762 KonquerorMatcher(normalizers.konqueror),
763 SafariMatcher(normalizers.safari),
764 FirefoxMatcher(normalizers.firefox),
765 MSIEMatcher(normalizers.msie),
766 CatchAllMatcher()]
767