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