diff --git a/device_detector/parser/client/base.py b/device_detector/parser/client/base.py index d87faf1..77da309 100644 --- a/device_detector/parser/client/base.py +++ b/device_detector/parser/client/base.py @@ -19,8 +19,8 @@ class BaseClientParser(Parser): def name_version_pairs(self) -> list: - cached = DDCache['user_agents'][self.ua_hash].get('name_version_pairs', []) - if cached: + cached = DDCache['user_agents'][self.ua_hash].get('name_version_pairs', None) + if cached is not None: return cached name_version_pairs = key_value_pairs(ua=self.user_agent) diff --git a/device_detector/parser/key_value_pairs.py b/device_detector/parser/key_value_pairs.py index edab401..e632dbc 100644 --- a/device_detector/parser/key_value_pairs.py +++ b/device_detector/parser/key_value_pairs.py @@ -11,7 +11,27 @@ re.IGNORECASE, ) -REGEXES = ( + +# Extra version / name from UAs +VERSION_NAME_REGEXES = ( + # 1.172.0.1 - LIVE - Mar 5 2020 + # 17build 113411 LIVE Sep 17 20180 + re.compile( + r'(?P[\d\.]+)[ \-]+(?PLIVE)', + re.IGNORECASE, + ), + + # 15.5.53 Boxcar + # 165 CandyCanes + re.compile( + r'^(?P[\d\.]+)[ \-/]+(?P\w+)$', + re.IGNORECASE, + ), +) + + +# Extra name / version from UAs +NAME_VERSION_REGEXES = ( # Get ALL / pairs from the regex re.compile( @@ -72,23 +92,21 @@ def name_matches_regex(name) -> bool: return False -def extract_pairs(regex, ua): +def scrub_name_version_pairs(matches: list) -> list: """ - Extract all key/value pairs of the specified regex, - and return pairs along with unmatched portion of ua string. + Takes list of (name,version) tuples. + Remove all pairs where name matches SKIP patterns """ - matches = regex.findall(ua) - substring = ua - - if matches: - substring = regex.sub(' ', ua) - pairs = [] for name, version in matches: name = name.strip(' -,') if not name: continue + # does this look like base64 encoded data? + if name.endswith('=='): + continue + name_lower = name.lower() if name_lower in SKIP_PREFIXES: continue @@ -99,6 +117,37 @@ def extract_pairs(regex, ua): code = name_lower.replace(' ', '') pairs.append((code, name, version.strip())) + return pairs + + +def extract_version_name_pairs(regex, ua): + """ + Extract all key/value pairs of the specified regex, + where key==version and value==name + and return pairs along with unmatched portion of ua string. + """ + match = regex.search(ua) + + if match: + return scrub_name_version_pairs([(match.group('name'), match.group('version'))]) + + return [] + + +def extract_name_version_pairs(regex, ua): + """ + Extract all key/value pairs of the specified regex, + where key==name and value==version + and return pairs along with unmatched portion of ua string. + """ + matches = regex.findall(ua) + substring = ua + + if matches: + substring = regex.sub(' ', ua) + + pairs = scrub_name_version_pairs(matches) + return pairs, substring @@ -111,14 +160,24 @@ def key_value_pairs(ua): all_pairs = [] - for rgx in REGEXES: - pairs, substring = extract_pairs(rgx, substring) + for rgx in VERSION_NAME_REGEXES: + pairs = extract_version_name_pairs(rgx, substring) + if pairs: + all_pairs.extend(pairs) + + # / regexes will be much less common + # so if we found such entries return then first + if all_pairs: + return all_pairs + + for rgx in NAME_VERSION_REGEXES: + pairs, substring = extract_name_version_pairs(rgx, substring) all_pairs.extend(pairs) return all_pairs __all__ = ( - 'extract_pairs', + 'extract_name_version_pairs', 'key_value_pairs', ) diff --git a/device_detector/tests/fixtures/local/app_names.yml b/device_detector/tests/fixtures/local/app_names.yml index 65ba014..b5356d1 100644 --- a/device_detector/tests/fixtures/local/app_names.yml +++ b/device_detector/tests/fixtures/local/app_names.yml @@ -145,6 +145,31 @@ client: name: Akamai NetSession C-API version: '' +- + user_agent: 1.172.0.1 - LIVE - Mar 5 2020 + client: + name: LIVE + version: 1.172.0.1 +- + user_agent: 17build 113411 LIVE Sep 17 20180 + client: + name: LIVE + version: 113411 +- + user_agent: 11.3.27 Boxcar + client: + name: Boxcar + version: 11.3.27 +- + user_agent: 1.3.7 CoalCar + client: + name: CoalCar + version: 1.3.7 +- + user_agent: 12.31.79/Caboose + client: + name: Caboose + version: 12.31.79 # -------------------------------------------------------------------------------- # UA Strings with no interesting pairs should fall back to browser details -