pywurfl.algorithms.wurfl.handlers

Package pywurfl :: Package algorithms :: Package wurfl :: Module handlers |
[hide private]
[frames] | no frames]

# Source Code for Module pywurfl.algorithms.wurfl.handlers

      1  # pywurfl - Wireless Universal Resource File Tools in Python
      2  # Copyright (C) 2006-2010 Armand Lynch
      3  #
      4  # This library is free software; you can redistribute it and/or modify it
      5  # under the terms of the GNU Lesser General Public License as published by the
      6  # Free Software Foundation; either version 2.1 of the License, or (at your
      7  # option) any later version.
      8  #
      9  # This library is distributed in the hope that it will be useful, but WITHOUT
     10  # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
     11  # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
     12  # details.
     13  #
     14  # You should have received a copy of the GNU Lesser General Public License
     15  # along with this library; if not, write to the Free Software Foundation, Inc.,
     16  # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
     17  #
     18  # Armand Lynch <lyncha@users.sourceforge.net>
     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) 2008-2009 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.0.1"
     37
     38  import re
     39
     40  from pywurfl.algorithms.wurfl import utils
     41  from pywurfl.algorithms.wurfl import normalizers
     42  from pywurfl.algorithms.wurfl.strategies import ld_match, ris_match
     43
     44
     45  uiol = utils.indexof_or_length
     46  uoi = utils.ordinal_index
     47
     48
     49 -class AbstractMatcher(object):
     50      user_agent_map = {}
     51
     52 -    def __init__(self, normalizer=None):
     53          if normalizer is None:
     54              self.normalizer = lambda x: x
     55          else:
     56              self.normalizer = normalizer
     57          self.known_user_agents = set()
     58
     59 -    def add(self, user_agent, wurfl_id):
     60          self.known_user_agents.add(user_agent)
     61          self.user_agent_map[user_agent] = wurfl_id
     62
     63      @property
     64 -    def user_agents(self):
     65          return sorted(self.known_user_agents)
     66
     67 -    def can_handle(self, user_agent):
     68          raise NotImplementedError
     69
     70 -    def __call__(self, user_agent):
     71          devid = self.conclusive_match(user_agent)
     72          if not devid or devid == u"generic":
     73              devid = self.recovery_match(user_agent)
     74          if not devid or devid == u"generic":
     75              devid = self.catch_all_recovery_match(user_agent)
     76          return devid
     77
     78 -    def conclusive_match(self, user_agent):
     79          user_agent = self.normalizer(user_agent)
     80          match = self.look_for_matching_user_agent(user_agent)
     81          #print "%s -> conclusive_match -> %s" % (user_agent, match)
     82          devid = self.user_agent_map.get(match, u"generic")
     83          return devid
     84
     85 -    def look_for_matching_user_agent(self, user_agent):
     86          tolerance = utils.first_slash(user_agent)
     87          #print "AbstractMatcher tolerance %s" % tolerance
     88          match = ris_match(self.user_agents, user_agent, tolerance)
     89          #print "AbstractMatcher %s -> l_f_m_ua -> %s" % (user_agent, match)
     90          return match
     91
     92 -    def recovery_match(self, user_agent):
     93          return u"generic"
     94
     95      recovery_map = (
     96          # Openwave
     97          (u"UP.Browser/7.2", u"opwv_v72_generic"),
     98          (u"UP.Browser/7", u"opwv_v7_generic"),
     99          (u"UP.Browser/6.2", u"opwv_v62_generic"),
    100          (u"UP.Browser/6", u"opwv_v6_generic"),
    101          (u"UP.Browser/5", u"upgui_generic"),
    102          (u"UP.Browser/4", u"uptext_generic"),
    103          (u"UP.Browser/3", u"uptext_generic"),
    104
    105          # Series 60
    106          (u"Series60", u"nokia_generic_series60"),
    107
    108          # Access/Net Front
    109          (u"NetFront/3.0", u"netfront_ver3"),
    110          (u"ACS-NF/3.0", u"netfront_ver3"),
    111          (u"NetFront/3.1", u"netfront_ver3_1"),
    112          (u"ACS-NF/3.1", u"netfront_ver3_1"),
    113          (u"NetFront/3.2", u"netfront_ver3_2"),
    114          (u"ACS-NF/3.2", u"netfront_ver3_2"),
    115          (u"NetFront/3.3", u"netfront_ver3_3"),
    116          (u"ACS-NF/3.3", u"netfront_ver3_3"),
    117          (u"NetFront/3.4", u"netfront_ver3_4"),
    118          (u"NetFront/3.5", u"netfront_ver3_5"),
    119
    120          # Windows CE
    121          (u"Windows CE", u"ms_mobile_browser_ver1"),
    122
    123          # web browsers?
    124          (u"Mozilla/4.0", u"generic_web_browser"),
    125          (u"Mozilla/5.0", u"generic_web_browser"),
    126          (u"Mozilla/5.0", u"generic_web_browser"),
    127
    128          # Generic XHTML
    129          (u"Mozilla/", u"generic_xhtml"),
    130          (u"ObigoInternetBrowser/Q03C", u"generic_xhtml"),
    131          (u"AU-MIC/2", u"generic_xhtml"),
    132          (u"AU-MIC-", u"generic_xhtml"),
    133          (u"AU-OBIGO/", u"generic_xhtml"),
    134          (u"Obigo/Q03", u"generic_xhtml"),
    135          (u"Obigo/Q04", u"generic_xhtml"),
    136          (u"ObigoInternetBrowser/2", u"generic_xhtml"),
    137          (u"Teleca Q03B1", u"generic_xhtml"),
    138
    139          # Opera Mini
    140          (u"Opera Mini/1", u"opera_mini_ver1"),
    141          (u"Opera Mini/2", u"opera_mini_ver2"),
    142          (u"Opera Mini/3", u"opera_mini_ver3"),
    143          (u"Opera Mini/4", u"opera_mini_ver4"),
    144
    145          # DoCoMo
    146          (u"DoCoMo", u"docomo_generic_jap_ver1"),
    147          (u"KDDI", u"docomo_generic_jap_ver1"))
    148
    149 -    def catch_all_recovery_match(self, user_agent):
    150
    151          match = u"generic"
    152          for partial_agent, wdevice in self.recovery_map:
    153              if partial_agent in user_agent:
    154                  match = wdevice
    155                  break
    156          return match
    157
    158
    159 -class AlcatelMatcher(AbstractMatcher):
    160 -    def can_handle(self, user_agent):
    161          return (user_agent.startswith(u"Alcatel") or
    162                  user_agent.startswith(u"ALCATEL"))
    163
    164
    165 -class AndroidMatcher(AbstractMatcher):
    166 -    def can_handle(self, user_agent):
    167          return u"Android" in user_agent
    168
    169 -    def look_for_matching_user_agent(self, user_agent):
    170          tolerance = uiol(user_agent, u" ",
    171                           start_index=uiol(user_agent, u"Android"))
    172          match = ris_match(self.user_agents, user_agent, tolerance)
    173          #print "AndroidMatcher %s -> l_f_m_ua -> %s" % (user_agent, match)
    174          return match
    175
    176 -    def recovery_match(self, user_agent):
    177          return u"generic_android"
    178
    179
    180 -class AOLMatcher(AbstractMatcher):
    181 -    def can_handle(self, user_agent):
    182          return not utils.is_mobile_browser(user_agent) and u"AOL" in user_agent
    183
    184
    185 -class AppleMatcher(AbstractMatcher):
    186      APPLE_LD_TOLERANCE = 5
    187
    188 -    def can_handle(self, user_agent):
    189          return (u"iPhone" in user_agent or u"iPod" in user_agent or u"iPad" in
    190                  user_agent)
    191
    192 -    def look_for_matching_user_agent(self, user_agent):
    193          tolerance = utils.first_semi_colon(user_agent)
    194          match = ld_match(self.user_agents, user_agent, tolerance)
    195          #print "AppleMatcher %s -> l_f_m_ua -> %s" % (user_agent, match)
    196          return match
    197
    198 -    def recovery_match(self, user_agent):
    199          if u"iPad" in user_agent:
    200              return "apple_ipad_ver1"
    201          if u"iPod" in user_agent:
    202              return u"apple_ipod_ver1"
    203          return u"apple_iphone_ver1"
    204
    205
    206 -class BenQMatcher(AbstractMatcher):
    207 -    def can_handle(self, user_agent):
    208          return user_agent.startswith(u"BENQ") or user_agent.startswith(u"BenQ")
    209
    210
    211 -class BlackberryMatcher(AbstractMatcher):
    212      blackberries = {}
    213      blackberries["2."] = u"blackberry_generic_ver2"
    214      blackberries["3.2"] = u"blackberry_generic_ver3_sub2"
    215      blackberries["3.3"] = u"blackberry_generic_ver3_sub30"
    216      blackberries["3.5"] = u"blackberry_generic_ver3_sub50"
    217      blackberries["3.6"] = u"blackberry_generic_ver3_sub60"
    218      blackberries["3.7"] = u"blackberry_generic_ver3_sub70"
    219      blackberries["4."] = u"blackberry_generic_ver4"
    220
    221 -    def can_handle(self, user_agent):
    222          return u"BlackBerry" in user_agent
    223
    224 -    def recovery_match(self, user_agent):
    225          match = u"generic"
    226
    227          if user_agent.startswith(u"BlackBerry"):
    228              version = self.get_version(user_agent)
    229              match = self.blackberries.get(version, u"generic")
    230
    231          return match
    232
    233 -    def get_version(self, user_agent):
    234          version = u""
    235          position = user_agent.index('/')
    236          if position + 4 < len(user_agent):
    237              version = user_agent[position+1:position+4]
    238          return version
    239
    240
    241 -class BotMatcher(AbstractMatcher):
    242      bots = (u"bot", u"crawler", u"spider", u"novarra", u"transcoder",
    243              u"yahoo! searchmonkey", u"yahoo! slurp", u"feedfetcher-google",
    244              u"toolbar", u"mowser")
    245
    246      BOT_TOLERANCE = 4
    247
    248 -    def can_handle(self, user_agent):
    249          user_agent = user_agent.lower()
    250          for bot in self.bots:
    251              if bot in user_agent:
    252                  return True
    253          return False
    254
    255 -    def look_for_matching_user_agent(self, user_agent):
    256          match = ld_match(self.user_agents, user_agent, self.BOT_TOLERANCE)
    257          return match
    258
    259 -    def recovery_match(self, user_agent):
    260          return u"generic_web_crawler"
    261
    262
    263 -class CatchAllMatcher(AbstractMatcher):
    264      MOZILLA_LD_TOLERANCE = 4
    265
    266 -    def can_handle(self, user_agent):
    267          return True
    268
    269 -    def look_for_matching_user_agent(self, user_agent):
    270          if user_agent.startswith(u"Mozilla"):
    271              if user_agent.startswith(u"Mozilla/4"):
    272                  match = ld_match(self.extract_uas(u"Mozilla/4"), user_agent,
    273                                   self.MOZILLA_LD_TOLERANCE)
    274              elif user_agent.startswith(u"Mozilla/5"):
    275                  match = ld_match(self.extract_uas(u"Mozilla/5"), user_agent,
    276                                   self.MOZILLA_LD_TOLERANCE)
    277              else:
    278                  match = ld_match(self.extract_uas(u"Mozilla"), user_agent,
    279                                   self.MOZILLA_LD_TOLERANCE)
    280          else:
    281              match = AbstractMatcher.look_for_matching_user_agent(self,
    282                                                                   user_agent)
    283          #print "CatchAllMatcher %s -> l_f_m_ua -> %s" % (user_agent, match)
    284          return match
    285
    286 -    def extract_uas(self, start):
    287          return (x for x in self.user_agents if x.startswith(start))
    288
    289
    290 -class ChromeMatcher(AbstractMatcher):
    291 -    def can_handle(self, user_agent):
    292          return (not utils.is_mobile_browser(user_agent) and
    293                  u"Chrome" in user_agent)
    294
    295
    296 -class DoCoMoMatcher(AbstractMatcher):
    297 -    def can_handle(self, user_agent):
    298          return user_agent.startswith(u"DoCoMo")
    299
    300 -    def look_for_matching_user_agent(self, user_agent):
    301          return u""
    302
    303 -    def recovery_match(self, user_agent):
    304          if user_agent.startswith(u"DoCoMo/2"):
    305              return u"docomo_generic_jap_ver2"
    306          return u"docomo_generic_jap_ver1"
    307
    308
    309 -class FirefoxMatcher(AbstractMatcher):
    310 -    def can_handle(self, user_agent):
    311          return (not utils.is_mobile_browser(user_agent) and
    312                  u"Firefox" in user_agent)
    313
    314
    315 -class GrundigMatcher(AbstractMatcher):
    316 -    def can_handle(self, user_agent):
    317          return (user_agent.startswith(u"Grundig") or
    318                  user_agent.startswith(u"GRUNDIG"))
    319
    320
    321 -class HTCMatcher(AbstractMatcher):
    322 -    def can_handle(self, user_agent):
    323          return user_agent.startswith(u"HTC")
    324
    325
    326 -class KDDIMatcher(AbstractMatcher):
    327 -    def can_handle(self, user_agent):
    328          return u"KDDI" in user_agent
    329
    330 -    def look_for_matching_user_agent(self, user_agent):
    331          if user_agent.startswith(u"KDDI/"):
    332              tolerance = utils.second_slash(user_agent)
    333          elif user_agent.startswith(u"KDDI"):
    334              tolerance = utils.first_slash(user_agent)
    335          else:
    336              tolerance = uiol(user_agent, ")")
    337
    338          match = ris_match(self.user_agents, user_agent, tolerance)
    339          #print "KDDIMatcher %s -> l_f_m_ua -> %s" % (user_agent, match)
    340          return match
    341
    342 -    def recovery_match(self, user_agent):
    343          if u"Opera" in user_agent:
    344              return u"opera"
    345          return u"opwv_v62_generic"
    346
    347
    348 -class KonquerorMatcher(AbstractMatcher):
    349 -    def can_handle(self, user_agent):
    350          return (not utils.is_mobile_browser(user_agent) and
    351                  u"Konqueror" in user_agent)
    352
    353
    354 -class KyoceraMatcher(AbstractMatcher):
    355 -    def can_handle(self, user_agent):
    356          return (user_agent.startswith(u"kyocera") or
    357                  user_agent.startswith(u"QC-") or
    358                  user_agent.startswith(u"KWC-"))
    359
    360
    361 -class LGMatcher(AbstractMatcher):
    362 -    def can_handle(self, user_agent):
    363          return (user_agent.startswith(u"lg") or u"LG-" in user_agent or
    364                  u"LGE" in user_agent)
    365
    366 -    def look_for_matching_user_agent(self, user_agent):
    367          if u"Vodafone" in user_agent:
    368              tolerance = uiol(user_agent, u"LG")
    369          elif user_agent.startswith(u"LGE/") or user_agent.startswith("LG/"):
    370              tolerance = utils.second_slash(user_agent)
    371          else:
    372              tolerance = utils.first_slash(user_agent)
    373
    374          return ris_match(self.user_agents, user_agent, tolerance)
    375
    376
    377 -class MitsubishiMatcher(AbstractMatcher):
    378 -    def can_handle(self, user_agent):
    379          return user_agent.startswith(u"Mitsu")
    380
    381
    382 -class MotorolaMatcher(AbstractMatcher):
    383      MOTOROLA_TOLERANCE = 5
    384
    385 -    def can_handle(self, user_agent):
    386          return (user_agent.startswith(u"Mot-") or
    387                  u"MOT-" in user_agent or
    388                  u"Motorola" in user_agent)
    389
    390 -    def look_for_matching_user_agent(self, user_agent):
    391          if (user_agent.startswith(u"Mot-") or user_agent.startswith(u"MOT-") or
    392              user_agent.startswith(u"Motorola")):
    393              match = AbstractMatcher.look_for_matching_user_agent(self,
    394                                                                   user_agent)
    395          else:
    396              match = ld_match(self.user_agents, user_agent,
    397                               self.MOTOROLA_TOLERANCE)
    398          #print "MotorolaMatcher %s -> l_f_m_ua -> %s" % (user_agent, match)
    399          return match
    400
    401 -    def recovery_match(self, user_agent):
    402          if u"MIB/2.2" in user_agent or u"MIB/BER2.2" in user_agent:
    403              match = u"mot_mib22_generic"
    404          else:
    405              match = u"generic"
    406          return match
    407
    408
    409 -class MSIEMatcher(AbstractMatcher):
    410 -    def can_handle(self, user_agent):
    411          return (not utils.is_mobile_browser(user_agent) and
    412                  user_agent.startswith(u"Mozilla") and
    413                  u"MSIE" in user_agent)
    414
    415
    416 -class NecMatcher(AbstractMatcher):
    417
    418      NEC_LD_TOLERANCE = 2
    419
    420 -    def can_handle(self, user_agent):
    421          return user_agent.startswith(u"NEC") or user_agent.startswith(u"KGT")
    422
    423 -    def look_for_matching_user_agent(self, user_agent):
    424          if user_agent.startswith(u"NEC"):
    425              match = AbstractMatcher.look_for_matching_user_agent(self,
    426                                                                   user_agent)
    427          else:
    428              match = ld_match(self.user_agents, user_agent,
    429                               self.NEC_LD_TOLERANCE)
    430          #print "NecMatcher %s -> l_f_m_ua -> %s" % (user_agent, match)
    431          return match
    432
    433
    434 -class NokiaMatcher(AbstractMatcher):
    435 -    def can_handle(self, user_agent):
    436          return u"Nokia" in user_agent
    437
    438 -    def look_for_matching_user_agent(self, user_agent):
    439          tolerance = uiol(user_agent, u"/",
    440                           start_index=user_agent.index(u"Nokia"))
    441          #print "NokiaMatcher tolerance %s" % tolerance
    442          match = ris_match(self.user_agents, user_agent, tolerance)
    443          #print "NokiaMatcher %s -> l_f_m_ua -> %s" % (user_agent, match)
    444          return match
    445
    446 -    def recovery_match(self, user_agent):
    447          if u"Series60" in user_agent:
    448              match = u"nokia_generic_series60"
    449          elif u"Series80" in user_agent:
    450              match = u"nokia_generic_series80"
    451          else:
    452              match = u"generic"
    453          return match
    454
    455
    456 -class OperaMatcher(AbstractMatcher):
    457
    458      OPERA_TOLERANCE = 3
    459
    460      operas = {}
    461      operas["7"] = u"opera_7"
    462      operas["8"] = u"opera_8"
    463      operas["9"] = u"opera_9"
    464      operas["10"] = u"opera_10"
    465
    466      opera_re = re.compile(r".*Opera/(\d+).*")
    467
    468 -    def can_handle(self, user_agent):
    469          return (not utils.is_mobile_browser(user_agent) and
    470                  u"Opera" in user_agent)
    471
    472 -    def look_for_matching_user_agent(self, user_agent):
    473          match = ld_match(self.user_agents, user_agent, self.OPERA_TOLERANCE)
    474          #print "OperaMatcher %s -> l_f_m_ua -> %s" % (user_agent, match)
    475          return match
    476
    477 -    def recovery_match(self, user_agent):
    478          match = self.opera_re.match(user_agent)
    479          if match:
    480              return self.operas.get(match.groups()[0], u"opera")
    481          return u"opera"
    482
    483
    484 -class OperaMiniMatcher(AbstractMatcher):
    485 -    def can_handle(self, user_agent):
    486          return u"Opera Mini" in user_agent
    487
    488 -    def recovery_match(self, user_agent):
    489          match = u""
    490          if u"Opera Mini/1" in user_agent:
    491              match = u"opera_mini_ver1"
    492          elif u"Opera Mini/2" in user_agent:
    493              match = u"opera_mini_ver2"
    494          elif u"Opera Mini/3" in user_agent:
    495              match = u"opera_mini_ver3"
    496          elif u"Opera Mini/4" in user_agent:
    497              match = u"opera_mini_ver4"
    498          return match
    499
    500
    501 -class PanasonicMatcher(AbstractMatcher):
    502 -    def can_handle(self, user_agent):
    503          return user_agent.startswith(u"Panasonic")
    504
    505
    506 -class PantechMatcher(AbstractMatcher):
    507
    508      PANTECH_LD_TOLERANCE = 4
    509
    510 -    def can_handle(self, user_agent):
    511          return (user_agent.startswith(u"Pantech") or
    512                  user_agent.startswith(u"PT-") or
    513                  user_agent.startswith(u"PANTECH") or
    514                  user_agent.startswith(u"PG-"))
    515
    516 -    def look_for_matching_user_agent(self, user_agent):
    517          if user_agent.startswith(u"Pantech"):
    518              match = ld_match(self.user_agents, user_agent,
    519                               self.PANTECH_LD_TOLERANCE)
    520          else:
    521              match = AbstractMatcher.look_for_matching_user_agent(self,
    522                                                                   user_agent)
    523          #print "PantechMatcher %s -> l_f_m_ua -> %s" % (user_agent, match)
    524          return match
    525
    526
    527 -class PhilipsMatcher(AbstractMatcher):
    528 -    def can_handle(self, user_agent):
    529          return (user_agent.startswith(u"Philips") or
    530                  user_agent.startswith(u"PHILIPS"))
    531
    532
    533 -class PortalmmmMatcher(AbstractMatcher):
    534 -    def can_handle(self, user_agent):
    535          return user_agent.startswith(u"portalmmm")
    536
    537 -    def look_for_matching_user_agent(self, user_agent):
    538          return u""
    539
    540
    541 -class QtekMatcher(AbstractMatcher):
    542 -    def can_handle(self, user_agent):
    543          return user_agent.startswith(u"Qtek")
    544
    545
    546 -class SafariMatcher(AbstractMatcher):
    547 -    def can_handle(self, user_agent):
    548          return (not utils.is_mobile_browser(user_agent) and
    549                  user_agent.startswith(u"Mozilla") and
    550                  u"Safari" in user_agent)
    551
    552 -    def recovery_match(self, user_agent):
    553          if u"Macintosh" in user_agent or u"Windows" in user_agent:
    554              match =  u"generic_web_browser"
    555          else:
    556              match = u"generic"
    557          return match
    558
    559
    560 -class SagemMatcher(AbstractMatcher):
    561 -    def can_handle(self, user_agent):
    562          return (user_agent.startswith(u"Sagem") or
    563                  user_agent.startswith(u"SAGEM"))
    564
    565
    566 -class SamsungMatcher(AbstractMatcher):
    567 -    def can_handle(self, user_agent):
    568          return (u"Samsung/SGH" in user_agent or
    569                  user_agent.startswith(u"SEC-") or
    570                  user_agent.startswith(u"Samsung") or
    571                  user_agent.startswith(u"SAMSUNG") or
    572                  user_agent.startswith(u"SPH") or
    573                  user_agent.startswith(u"SGH") or
    574                  user_agent.startswith(u"SCH"))
    575
    576 -    def look_for_matching_user_agent(self, user_agent):
    577          if (user_agent.startswith(u"SEC-") or
    578              user_agent.startswith(u"SAMSUNG-") or
    579              user_agent.startswith(u"SCH")):
    580              tolerance = utils.first_slash(user_agent)
    581          elif (user_agent.startswith(u"Samsung") or
    582                user_agent.startswith(u"SPH") or
    583                user_agent.startswith(u"SGH")):
    584              tolerance = utils.first_space(user_agent)
    585          elif user_agent.startswith(u"SAMSUNG/"):
    586              tolerance = utils.second_slash(user_agent)
    587          elif u"Samsung/SGH-L870" in user_agent:
    588              tolerance = uiol(user_agent, u"/", 5)
    589          else:
    590              tolerance = len(user_agent)
    591          match = ris_match(self.user_agents, user_agent, tolerance)
    592          #print "SamsungMatcher %s -> l_f_m_ua -> %s" % (user_agent, match)
    593          return match
    594
    595
    596 -class SanyoMatcher(AbstractMatcher):
    597 -    def can_handle(self, user_agent):
    598          return (user_agent.startswith(u"Sanyo") or
    599                  user_agent.startswith(u"SANYO"))
    600
    601
    602 -class SharpMatcher(AbstractMatcher):
    603 -    def can_handle(self, user_agent):
    604          return (user_agent.startswith(u"Sharp") or
    605                  user_agent.startswith(u"SHARP"))
    606
    607
    608 -class SiemensMatcher(AbstractMatcher):
    609 -    def can_handle(self, user_agent):
    610          return user_agent.startswith(u"SIE-")
    611
    612
    613 -class SonyEricssonMatcher(AbstractMatcher):
    614 -    def can_handle(self, user_agent):
    615          return u"SonyEricsson" in user_agent
    616
    617 -    def look_for_matching_user_agent(self, user_agent):
    618          if user_agent.startswith(u"SonyEricsson"):
    619              match = AbstractMatcher.look_for_matching_user_agent(self,
    620                                                                   user_agent)
    621          else:
    622              tolerance = utils.second_slash(user_agent)
    623              match = ris_match(self.user_agents, user_agent, tolerance)
    624          #print "SonyEricssonMatcher %s -> l_f_m_ua -> %s" % (user_agent, match)
    625          return match
    626
    627
    628 -class SPVMatcher(AbstractMatcher):
    629 -    def can_handle(self, user_agent):
    630          return u"SPV" in user_agent
    631
    632 -    def look_for_matching_user_agent(self, user_agent):
    633          tolerance = uiol(user_agent, u";", start_index=uiol(user_agent, u"SPV"))
    634          match = ris_match(self.user_agents, user_agent, tolerance)
    635          return match
    636
    637
    638 -class ToshibaMatcher(AbstractMatcher):
    639 -    def can_handle(self, user_agent):
    640          return user_agent.startswith(u"Toshiba")
    641
    642
    643 -class VodafoneMatcher(AbstractMatcher):
    644
    645 -    def can_handle(self, user_agent):
    646          return user_agent.startswith(u"Vodafone")
    647
    648 -    def look_for_matching_user_agent(self, user_agent):
    649          tolerance = uiol(user_agent, u"/", 3)
    650          match = ris_match(self.user_agents, user_agent, tolerance)
    651          #print "VodafoneMatcher %s -> l_f_m_ua -> %s" % (user_agent, match)
    652          return match
    653
    654
    655 -class WindowsCEMatcher(AbstractMatcher):
    656      WINDOWS_CE_TOLERANCE = 3
    657
    658 -    def can_handle(self, user_agent):
    659          return u"Mozilla/" in user_agent and (u"Windows CE" in user_agent or
    660                                                u"WindowsCE" in user_agent)
    661
    662 -    def look_for_matching_user_agent(self, user_agent):
    663          match = ld_match(self.user_agents, user_agent,
    664                           self.WINDOWS_CE_TOLERANCE)
    665          return match
    666
    667 -    def recovery_match(self, user_agent):
    668          return u"ms_mobile_browser_ver1"
    669
    670
    671  handlers = [NokiaMatcher(),
    672              AndroidMatcher(normalizers.android),
    673              SonyEricssonMatcher(),
    674              MotorolaMatcher(),
    675              BlackberryMatcher(),
    676              SiemensMatcher(),
    677              SagemMatcher(),
    678              SamsungMatcher(),
    679              PanasonicMatcher(),
    680              NecMatcher(),
    681              QtekMatcher(),
    682              MitsubishiMatcher(),
    683              PhilipsMatcher(),
    684              LGMatcher(),
    685              AppleMatcher(),
    686              KyoceraMatcher(),
    687              AlcatelMatcher(),
    688              SharpMatcher(),
    689              SanyoMatcher(),
    690              BenQMatcher(),
    691              PantechMatcher(),
    692              ToshibaMatcher(),
    693              GrundigMatcher(),
    694              HTCMatcher(),
    695              BotMatcher(),
    696              SPVMatcher(),
    697              WindowsCEMatcher(),
    698              PortalmmmMatcher(),
    699              DoCoMoMatcher(),
    700              KDDIMatcher(),
    701              VodafoneMatcher(),
    702              OperaMiniMatcher(),
    703              ChromeMatcher(normalizers.chrome),
    704              AOLMatcher(),
    705              OperaMatcher(normalizers.opera),
    706              KonquerorMatcher(normalizers.konqueror),
    707              SafariMatcher(normalizers.safari),
    708              FirefoxMatcher(normalizers.firefox),
    709              MSIEMatcher(normalizers.msie),
    710              CatchAllMatcher()]
    711