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 2010, Armand Lynch"
34 __license__ = "LGPL"
35 __url__ = "http://celljam.net/"
36 __version__ = "1.2"
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
626 return (u"Samsung/SGH" in user_agent or
627 u"Samsung" in user_agent or
628 user_agent.startswith(u"SEC-") or
629 user_agent.startswith(u"SAMSUNG") or
630 user_agent.startswith(u"SPH") or
631 user_agent.startswith(u"SGH") or
632 user_agent.startswith(u"SCH"))
633
635 if (user_agent.startswith(u"SEC-") or
636 user_agent.startswith(u"SAMSUNG-") or
637 user_agent.startswith(u"SCH")):
638 tolerance = first_slash(user_agent)
639 elif (user_agent.startswith(u"Samsung") or
640 user_agent.startswith(u"SPH") or
641 user_agent.startswith(u"SGH")):
642 tolerance = first_space(user_agent)
643 elif user_agent.startswith(u"SAMSUNG/"):
644 tolerance = second_slash(user_agent)
645 elif u"Samsung/SGH-L870" in user_agent:
646 tolerance = iol(user_agent, u"/", 5)
647 else:
648 tolerance = len(user_agent)
649 match = self.ris_matcher(user_agent, tolerance)
650
651 return match
652
656 return (user_agent.startswith(u"Sanyo") or
657 user_agent.startswith(u"SANYO"))
658
662 return (user_agent.startswith(u"Sharp") or
663 user_agent.startswith(u"SHARP"))
664
668 return user_agent.startswith(u"SIE-")
669
673 return u"SonyEricsson" in user_agent
674
683
687 return u"SPV" in user_agent
688
690 tolerance = iol(user_agent, u";", start_index=iol(user_agent, u"SPV"))
691 match = self.ris_matcher(user_agent, tolerance)
692 return match
693
697 return user_agent.startswith(u"Toshiba")
698
702 return user_agent.startswith(u"Vodafone")
703
705 tolerance = iol(user_agent, u"/", 3)
706 match = self.ris_matcher(user_agent, tolerance)
707
708 return match
709
712 WINDOWS_CE_TOLERANCE = 3
713
715 return (u"Mozilla/" in user_agent and (u"Windows CE" in user_agent or
716 u"WindowsCE" in user_agent))
717
721
723 return u"generic_ms_mobile_browser_ver1"
724
725
726 handlers = [NokiaMatcher(),
727 LGUPLUSMatcher(),
728 AndroidMatcher(),
729 SonyEricssonMatcher(),
730 MotorolaMatcher(),
731 BlackberryMatcher(),
732 SiemensMatcher(),
733 SagemMatcher(),
734 SamsungMatcher(),
735 PanasonicMatcher(),
736 NecMatcher(),
737 QtekMatcher(),
738 MitsubishiMatcher(),
739 PhilipsMatcher(),
740 LGMatcher(normalizers.lg),
741 AppleMatcher(),
742 KyoceraMatcher(),
743 AlcatelMatcher(),
744 SharpMatcher(),
745 SanyoMatcher(),
746 BenQMatcher(),
747 PantechMatcher(),
748 ToshibaMatcher(),
749 GrundigMatcher(),
750 HTCMatcher(),
751 BotMatcher(),
752 SPVMatcher(),
753 WindowsCEMatcher(),
754 PortalmmmMatcher(),
755 DoCoMoMatcher(),
756 KDDIMatcher(),
757 VodafoneMatcher(),
758 OperaMiniMatcher(),
759 MaemoMatcher(normalizers.maemo),
760 ChromeMatcher(normalizers.chrome),
761 AOLMatcher(),
762 OperaMatcher(),
763 KonquerorMatcher(normalizers.konqueror),
764 SafariMatcher(normalizers.safari),
765 FirefoxMatcher(normalizers.firefox),
766 MSIEMatcher(normalizers.msie),
767 CatchAllMatcher()]
768