source: OpenRLabs-Git/deploy/rlabs-docker/web2py-rlabs/gluon/contrib/user_agent_parser.py

main
Last change on this file was 42bd667, checked in by David Fuertes <dfuertes@…>, 4 years ago

Historial Limpio

  • Property mode set to 100755
File size: 17.3 KB
Line 
1"""
2Extract client information from http user agent
3The module does not try to detect all capabilities of browser in current form (it can easily be extended though).
4Tries to
5    * be fast
6    * very easy to extend
7    * reliable enough for practical purposes
8    * assist python web apps to detect clients.
9"""
10
11__version__ = '1.7.8'
12
13
14class DetectorsHub(dict):
15    _known_types = ['os', 'dist', 'flavor', 'browser']
16
17    def __init__(self, *args, **kw):
18        dict.__init__(self, *args, **kw)
19        for typ in self._known_types:
20            self.setdefault(typ, [])
21        self.registerDetectors()
22
23    def register(self, detector):
24        if detector.info_type not in self._known_types:
25            self[detector.info_type] = [detector]
26            self._known_types.insert(detector.order, detector.info_type)
27        else:
28            self[detector.info_type].append(detector)
29
30    def __iter__(self):
31        return iter(self._known_types)
32
33    def registerDetectors(self):
34        detectors = [v() for v in globals().values() if DetectorBase in getattr(v, '__mro__', [])]
35        for d in detectors:
36            if d.can_register:
37                self.register(d)
38
39
40class DetectorBase(object):
41    name = ""  # "to perform match in DetectorsHub object"
42    info_type = "override me"
43    result_key = "override me"
44    order = 10  # 0 is highest
45    look_for = "string to look for"
46    skip_if_found = []  # strings if present stop processin
47    can_register = False
48    version_markers = [("/", " ")]
49    allow_space_in_version = False
50    _suggested_detectors = None
51    platform = None
52    bot = False
53
54    def __init__(self):
55        if not self.name:
56            self.name = self.__class__.__name__
57        self.can_register = (self.__class__.__dict__.get('can_register', True))
58
59    def detect(self, agent, result):
60        # -> True/None
61        word = self.checkWords(agent)
62        if word:
63            result[self.info_type] = dict(name=self.name)
64            result['bot'] = self.bot
65            version = self.getVersion(agent, word)
66            if version:
67                result[self.info_type]['version'] = version
68            if self.platform:
69                result['platform'] = {'name': self.platform, 'version': version}
70            return True
71
72    def checkWords(self, agent):
73        # -> True/None
74        for w in self.skip_if_found:
75            if w in agent:
76                return False
77        if isinstance(self.look_for, (tuple, list)):
78            for word in self.look_for:
79                if word in agent:
80                    return word
81        elif self.look_for in agent:
82            return self.look_for
83
84    def getVersion(self, agent, word):
85        """
86        => version string /None
87        """
88        version_markers = self.version_markers if \
89            isinstance(self.version_markers[0], (list, tuple)) else [self.version_markers]
90        version_part = agent.split(word, 1)[-1]
91        for start, end in version_markers:
92            if version_part.startswith(start) and end in version_part:
93                version = version_part[1:]
94                if end:  # end could be empty string
95                    version = version.split(end)[0]
96                if not self.allow_space_in_version:
97                    version = version.split()[0]
98                return version
99
100
101class OS(DetectorBase):
102    info_type = "os"
103    can_register = False
104    version_markers = [";", " "]
105    allow_space_in_version = True
106    platform = None
107
108
109class Dist(DetectorBase):
110    info_type = "dist"
111    can_register = False
112    platform = None
113
114
115class Flavor(DetectorBase):
116    info_type = "flavor"
117    can_register = False
118    platform = None
119
120
121class Browser(DetectorBase):
122    info_type = "browser"
123    can_register = False
124
125
126class Firefox(Browser):
127    look_for = "Firefox"
128    version_markers = [('/', '')]
129    skip_if_found = ["SeaMonkey", "web/snippet"]
130
131
132class SeaMonkey(Browser):
133    look_for = "SeaMonkey"
134    version_markers = [('/', '')]
135
136
137class Konqueror(Browser):
138    look_for = "Konqueror"
139    version_markers = ["/", ";"]
140
141
142class OperaMobile(Browser):
143    look_for = "Opera Mobi"
144    name = "Opera Mobile"
145
146    def getVersion(self, agent, word):
147        try:
148            look_for = "Version"
149            return agent.split(look_for)[1][1:].split(' ')[0]
150        except IndexError:
151            look_for = "Opera"
152            return agent.split(look_for)[1][1:].split(' ')[0]
153
154
155class Opera(Browser):
156    look_for = "Opera"
157
158    def getVersion(self, agent, word):
159        try:
160            look_for = "Version"
161            return agent.split(look_for)[1][1:].split(' ')[0]
162        except IndexError:
163            look_for = "Opera"
164            version = agent.split(look_for)[1][1:].split(' ')[0]
165            return version.split('(')[0]
166
167
168class OperaNew(Browser):
169    """
170    Opera after version 15
171    """
172    name = "Opera"
173    look_for = "OPR"
174    version_markers = [('/', '')]
175
176
177class Netscape(Browser):
178    look_for = "Netscape"
179    version_markers = [("/", '')]
180
181
182class Trident(Browser):
183    look_for = "Trident"
184    skip_if_found = ["MSIE", "Opera"]
185    name = "Microsoft Internet Explorer"
186    version_markers = ["/", ";"]
187    trident_to_ie_versions = {
188        '4.0': '8.0',
189        '5.0': '9.0',
190        '6.0': '10.0',
191        '7.0': '11.0',
192    }
193
194    def getVersion(self, agent, word):
195        return self.trident_to_ie_versions.get(super(Trident, self).getVersion(agent, word))
196
197
198class MSIE(Browser):
199    look_for = "MSIE"
200    skip_if_found = ["Opera"]
201    name = "Microsoft Internet Explorer"
202    version_markers = [" ", ";"]
203
204class MSEdge(Browser):
205    look_for = "Edge"
206    skip_if_found = ["MSIE"]
207    version_markers = ["/", ""]
208
209class Galeon(Browser):
210    look_for = "Galeon"
211
212
213class WOSBrowser(Browser):
214    look_for = "wOSBrowser"
215
216    def getVersion(self, agent, word):
217        pass
218
219
220class Safari(Browser):
221    look_for = "Safari"
222    skip_if_found = ["Edge"]
223
224    def checkWords(self, agent):
225        unless_list = ["Chrome", "OmniWeb", "wOSBrowser", "Android"]
226        if self.look_for in agent:
227            for word in unless_list:
228                if word in agent:
229                    return False
230            return self.look_for
231
232    def getVersion(self, agent, word):
233        if "Version/" in agent:
234            return agent.split('Version/')[-1].split(' ')[0].strip()
235        if "Safari/" in agent:
236            return agent.split('Safari/')[-1].split(' ')[0].strip()
237        else:
238            return agent.split('Safari ')[-1].split(' ')[0].strip()  # Mobile Safari
239
240class GoogleBot(Browser):
241    # https://support.google.com/webmasters/answer/1061943
242    look_for = ["Googlebot", "Googlebot-News", "Googlebot-Image",
243                "Googlebot-Video", "Googlebot-Mobile", "Mediapartners-Google",
244                "Mediapartners", "AdsBot-Google", "web/snippet"]
245    bot = True
246    version_markers = [('/', ';'), ('/', ' ')]
247
248class GoogleFeedFetcher(Browser):
249    look_for = "Feedfetcher-Google"
250    bot = True
251
252    def get_version(self, agent):
253        pass
254
255class RunscopeRadar(Browser):
256    look_for = "runscope-radar"
257    bot = True
258
259class GoogleAppEngine(Browser):
260    look_for = "AppEngine-Google"
261    bot = True
262
263    def get_version(self, agent):
264        pass
265
266class GoogleApps(Browser):
267    look_for = "GoogleApps script"
268    bot = True
269
270    def get_version(self, agent):
271        pass
272
273class TwitterBot(Browser):
274    look_for = "Twitterbot"
275    bot = True
276
277class MJ12Bot(Browser):
278    look_for = "MJ12bot"
279    bot = True
280
281class YandexBot(Browser):
282    # http://help.yandex.com/search/robots/agent.xml
283    look_for = "Yandex"
284    bot = True
285
286    def getVersion(self, agent, word):
287        return agent[agent.index('Yandex'):].split('/')[-1].split(')')[0].strip()
288
289class BingBot(Browser):
290    look_for = "bingbot"
291    version_markers = ["/", ";"]
292    bot = True
293
294
295class BaiduBot(Browser):
296    # http://help.baidu.com/question?prod_en=master&class=1&id=1000973
297    look_for = ["Baiduspider", "Baiduspider-image", "Baiduspider-video",
298                "Baiduspider-news", "Baiduspider-favo", "Baiduspider-cpro",
299                "Baiduspider-ads"]
300    bot = True
301    version_markers = ('/', ';')
302
303
304class LinkedInBot(Browser):
305    look_for = "LinkedInBot"
306    bot = True
307
308class ArchiveDotOrgBot(Browser):
309    look_for = "archive.org_bot"
310    bot = True
311
312class YoudaoBot(Browser):
313    look_for = "YoudaoBot"
314    bot = True
315
316class YoudaoBotImage(Browser):
317    look_for = "YodaoBot-Image"
318    bot = True
319
320class RogerBot(Browser):
321    look_for = "rogerbot"
322    bot = True
323
324class TweetmemeBot(Browser):
325    look_for = "TweetmemeBot"
326    bot = True
327
328class WebshotBot(Browser):
329    look_for = "WebshotBot"
330    bot = True
331
332class SensikaBot(Browser):
333    look_for = "SensikaBot"
334    bot = True
335
336class YesupBot(Browser):
337    look_for = "YesupBot"
338    bot = True
339
340class DotBot(Browser):
341    look_for = "DotBot"
342    bot = True
343
344class PhantomJS(Browser):
345    look_for = "Browser/Phantom"
346    bot = True
347
348class FacebookExternalHit(Browser):
349    look_for = 'facebookexternalhit'
350    bot = True
351
352
353class NokiaOvi(Browser):
354    look_for = "S40OviBrowser"
355
356class UCBrowser(Browser):
357    look_for = "UCBrowser"
358
359class BrowserNG(Browser):
360    look_for = "BrowserNG"
361
362class Dolfin(Browser):
363    look_for = 'Dolfin'
364
365class NetFront(Browser):
366    look_for = 'NetFront'
367
368class Jasmine(Browser):
369    look_for = 'Jasmine'
370
371class Openwave(Browser):
372    look_for = 'Openwave'
373
374class UPBrowser(Browser):
375    look_for = 'UP.Browser'
376
377class OneBrowser(Browser):
378    look_for = 'OneBrowser'
379
380class ObigoInternetBrowser(Browser):
381    look_for = 'ObigoInternetBrowser'
382
383class TelecaBrowser(Browser):
384    look_for = 'TelecaBrowser'
385
386class MAUI(Browser):
387    look_for = 'Browser/MAUI'
388
389    def getVersion(self, agent, word):
390        version = agent.split("Release/")[-1][:10]
391        return version
392
393
394class NintendoBrowser(Browser):
395    look_for = 'NintendoBrowser'
396
397
398class AndroidBrowser(Browser):
399    look_for = "Android"
400    skip_if_found = ['Chrome', 'Windows Phone']
401
402    # http://decadecity.net/blog/2013/11/21/android-browser-versions
403    def getVersion(self, agent, word):
404        pass
405
406
407class Linux(OS):
408    look_for = 'Linux'
409    platform = 'Linux'
410
411    def getVersion(self, agent, word):
412        pass
413
414
415class Blackberry(OS):
416    look_for = 'BlackBerry'
417    platform = 'BlackBerry'
418
419    def getVersion(self, agent, word):
420        pass
421
422
423class BlackberryPlaybook(Dist):
424    look_for = 'PlayBook'
425    platform = 'BlackBerry'
426
427    def getVersion(self, agent, word):
428        pass
429
430
431class WindowsPhone(OS):
432    name = "Windows Phone"
433    platform = 'Windows'
434    look_for = ["Windows Phone OS", "Windows Phone"]
435    version_markers = [(" ", ";"), (" ", ")")]
436
437
438class iOS(OS):
439    look_for = ('iPhone', 'iPad')
440    skip_if_found = ['like iPhone']
441
442
443class iPhone(Dist):
444    look_for = 'iPhone'
445    platform = 'iOS'
446    skip_if_found = ['like iPhone']
447
448    def getVersion(self, agent, word):
449        version_end_chars = [' ']
450        if not "iPhone OS" in agent:
451            return None
452        part = agent.split('iPhone OS')[-1].strip()
453        for c in version_end_chars:
454            if c in part:
455                version = part.split(c)[0]
456                return version.replace('_', '.')
457        return None
458
459
460class IPad(Dist):
461    look_for = 'iPad;'
462    platform = 'iOS'
463
464    def getVersion(self, agent, word):
465        version_end_chars = [' ']
466        if not "CPU OS " in agent:
467            return None
468        part = agent.split('CPU OS ')[-1].strip()
469        for c in version_end_chars:
470            if c in part:
471                version = part.split(c)[0]
472                return version.replace('_', '.')
473        return None
474
475
476class Macintosh(OS):
477    look_for = 'Macintosh'
478
479    def getVersion(self, agent, word):
480        pass
481
482
483class MacOS(Flavor):
484    look_for = 'Mac OS'
485    platform = 'Mac OS'
486    skip_if_found = ['iPhone', 'iPad']
487
488    def getVersion(self, agent, word):
489        version_end_chars = [';', ')']
490        part = agent.split('Mac OS')[-1].strip()
491        for c in version_end_chars:
492            if c in part:
493                version = part.split(c)[0]
494                return version.replace('_', '.')
495        return ''
496
497
498class Windows(Dist):
499    look_for = 'Windows'
500    platform = 'Windows'
501
502
503class Windows(OS):
504    look_for = 'Windows'
505    platform = 'Windows'
506    skip_if_found = ["Windows Phone"]
507    win_versions = {
508                    "NT 10.0": "10",
509                    "NT 6.3": "8.1",
510                    "NT 6.2": "8",
511                    "NT 6.1": "7",
512                    "NT 6.0": "Vista",
513                    "NT 5.2": "Server 2003 / XP x64",
514                    "NT 5.1": "XP",
515                    "NT 5.01": "2000 SP1",
516                    "NT 5.0": "2000",
517                    "98; Win 9x 4.90": "Me"
518    }
519
520    def getVersion(self, agent, word):
521        v = agent.split('Windows')[-1].split(';')[0].strip()
522        if ')' in v:
523            v = v.split(')')[0]
524        v = self.win_versions.get(v, v)
525        return v
526
527
528class Ubuntu(Dist):
529    look_for = 'Ubuntu'
530    version_markers = ["/", " "]
531
532
533class Debian(Dist):
534    look_for = 'Debian'
535    version_markers = ["/", " "]
536
537
538class Chrome(Browser):
539    look_for = "Chrome"
540    version_markers = ["/", " "]
541    skip_if_found = ["OPR", "Edge"]
542
543    def getVersion(self, agent, word):
544        part = agent.split(word + self.version_markers[0])[-1]
545        version = part.split(self.version_markers[1])[0]
546        if '+' in version:
547            version = part.split('+')[0]
548        return version.strip()
549
550
551class ChromeiOS(Browser):
552    look_for = "CriOS"
553    version_markers = ["/", " "]
554
555
556class ChromeOS(OS):
557    look_for = "CrOS"
558    platform = ' ChromeOS'
559    version_markers = [" ", " "]
560
561    def getVersion(self, agent, word):
562        version_markers = self.version_markers
563        if word + '+' in agent:
564            version_markers = ['+', '+']
565        return agent.split(word + version_markers[0])[-1].split(version_markers[1])[1].strip()[:-1]
566
567
568class Android(Dist):
569    look_for = 'Android'
570    platform = 'Android'
571    skip_if_found = ['Windows Phone']
572
573    def getVersion(self, agent, word):
574        return agent.split(word)[-1].split(';')[0].strip()
575
576
577class WebOS(Dist):
578    look_for = 'hpwOS'
579
580    def getVersion(self, agent, word):
581        return agent.split('hpwOS/')[-1].split(';')[0].strip()
582
583
584class NokiaS40(OS):
585    look_for = 'Series40'
586    platform = 'Nokia S40'
587
588    def getVersion(self, agent, word):
589        pass
590
591
592class Symbian(OS):
593    look_for = ['Symbian', 'SymbianOS']
594    platform = 'Symbian'
595
596
597class PlayStation(OS):
598    look_for = ['PlayStation', 'PLAYSTATION']
599    platform = 'PlayStation'
600    version_markers = [" ", ")"]
601
602
603class prefs:  # experimental
604    os = dict(
605        Linux=dict(dict(browser=[Firefox, Chrome], dist=[Ubuntu, Android])),
606        BlackBerry=dict(dist=[BlackberryPlaybook]),
607        Macintosh=dict(flavor=[MacOS]),
608        Windows=dict(browser=[MSIE, Firefox]),
609        ChromeOS=dict(browser=[Chrome]),
610        Debian=dict(browser=[Firefox])
611    )
612    dist = dict(
613        Ubuntu=dict(browser=[Firefox]),
614        Android=dict(browser=[Safari]),
615        IPhone=dict(browser=[Safari]),
616        IPad=dict(browser=[Safari]),
617    )
618    flavor = dict(
619        MacOS=dict(browser=[Opera, Chrome, Firefox, MSIE])
620    )
621
622
623detectorshub = DetectorsHub()
624
625
626def detect(agent, fill_none=False):
627    """
628    fill_none: if name/version is not detected respective key is still added to the result with value None
629    """
630    result = dict(platform=dict(name=None, version=None))
631    _suggested_detectors = []
632
633    for info_type in detectorshub:
634        detectors = _suggested_detectors or detectorshub[info_type]
635        for detector in detectors:
636            try:
637                detector.detect(agent, result)
638            except Exception as _err:
639                pass
640
641    if fill_none:
642        attrs_d = {'name': None, 'version': None}
643        for key in ('os', 'browser'):
644            if key not in result:
645                result[key] = attrs_d
646            else:
647                for k, v in attrs_d.items():
648                    result[k] = v
649
650    return result
651
652
653def simple_detect(agent):
654    """
655    -> (os, browser) # tuple of strings
656    """
657    result = detect(agent)
658    os_list = []
659    if 'flavor' in result:
660        os_list.append(result['flavor']['name'])
661    if 'dist' in result:
662        os_list.append(result['dist']['name'])
663    if 'os' in result:
664        os_list.append(result['os']['name'])
665
666    os = os_list and " ".join(os_list) or "Unknown OS"
667    os_version = os_list and (result.get('flavor') and result['flavor'].get('version')) or \
668        (result.get('dist') and result['dist'].get('version')) or (result.get('os') and result['os'].get('version')) or ""
669    browser = 'browser' in result and result['browser'].get('name') or 'Unknown Browser'
670    browser_version = 'browser' in result and result['browser'].get('version') or ""
671    if browser_version:
672        browser = " ".join((browser, browser_version))
673    if os_version:
674        os = " ".join((os, os_version))
675    return os, browser
676
677
678class mobilize(object): 
679    """
680    Decorator for controller functions so they use different views for mobile devices.
681
682    WARNING: If you update httpagentparser make sure to leave mobilize for
683    backwards compatibility.
684    """
685    def __init__(self, func):
686        self.func = func
687   
688    def __call__(self):
689        from gluon import current
690        user_agent = current.request.user_agent()
691        if user_agent.is_mobile:
692            items = current.response.view.split('.')
693            items.insert(-1, 'mobile')
694            current.response.view = '.'.join(items)
695        return self.func()
Note: See TracBrowser for help on using the repository browser.