├── .deepsource.toml ├── .gitignore ├── .style.yapf ├── .travis.yml ├── MANIFEST.in ├── README.md ├── device_detector ├── LICENSE ├── __init__.py ├── appdetails │ ├── __init__.py │ ├── app_ids.yml │ ├── desktop_app.yml │ ├── game.yml │ ├── library.yml │ ├── mediaplayer.yml │ ├── messaging.yml │ ├── mobile_app.yml │ ├── p2p.yml │ ├── pim.yml │ └── vpnproxy.yml ├── appids │ ├── __init__.py │ ├── ignored.yml │ ├── normalized.yml │ └── secondary.yml ├── device_detector.py ├── lazy_regex.py ├── parser │ ├── __init__.py │ ├── client │ │ ├── __init__.py │ │ ├── base.py │ │ ├── browser.py │ │ ├── desktopapp.py │ │ ├── dictua.py │ │ ├── extractor_name_version.py │ │ ├── extractor_whole_name.py │ │ ├── feed_reader.py │ │ ├── game.py │ │ ├── library.py │ │ ├── mediaplayer.py │ │ ├── messaging.py │ │ ├── mobileapp.py │ │ ├── p2p.py │ │ ├── pim.py │ │ └── vpnproxy.py │ ├── device │ │ ├── __init__.py │ │ ├── base.py │ │ ├── bot.py │ │ ├── device.py │ │ └── vendor_fragment.py │ ├── extractors.py │ ├── key_value_pairs.py │ ├── operating_system.py │ ├── os_fragment.py │ ├── parser.py │ └── settings.py ├── regexes │ ├── __init__.py │ ├── local │ │ ├── __init__.py │ │ ├── client │ │ │ ├── __init__.py │ │ │ ├── antivirus.yml │ │ │ ├── browsers.yml │ │ │ ├── desktop_apps.yml │ │ │ ├── games.yml │ │ │ ├── libraries.yml │ │ │ ├── mediaplayers.yml │ │ │ ├── mobile_apps.yml │ │ │ ├── osutility.yml │ │ │ ├── p2p.yml │ │ │ ├── pim.yml │ │ │ └── vpnproxy.yml │ │ ├── device │ │ │ ├── __init__.py │ │ │ └── normalize.yml │ │ ├── osfragments.yml │ │ └── oss.yml │ └── upstream │ │ ├── __init__.py │ │ ├── bots.yml │ │ ├── client │ │ ├── __init__.py │ │ ├── browser_engine.yml │ │ ├── browsers.yml │ │ ├── feed_readers.yml │ │ ├── libraries.yml │ │ ├── mediaplayers.yml │ │ ├── mobile_apps.yml │ │ └── pim.yml │ │ ├── device │ │ ├── __init__.py │ │ ├── cameras.yml │ │ ├── car_browsers.yml │ │ ├── consoles.yml │ │ ├── mobiles.yml │ │ ├── notebooks.yml │ │ ├── portable_media_player.yml │ │ ├── shell_tv.yml │ │ └── televisions.yml │ │ ├── oss.yml │ │ └── vendorfragments.yml ├── settings.py ├── tests │ ├── __init__.py │ ├── base.py │ ├── device │ │ ├── __init__.py │ │ ├── test_device_detector.py │ │ └── test_upstream_collisions.py │ ├── fixtures │ │ ├── __init__.py │ │ ├── local │ │ │ ├── __init__.py │ │ │ ├── app_names.yml │ │ │ └── normalize.yml │ │ └── upstream │ │ │ ├── __init__.py │ │ │ ├── bots.yml │ │ │ ├── camera.yml │ │ │ ├── car_browser.yml │ │ │ ├── console.yml │ │ │ ├── desktop.yml │ │ │ ├── feature_phone.yml │ │ │ ├── feed_reader.yml │ │ │ ├── mediaplayer.yml │ │ │ ├── mobile_apps.yml │ │ │ ├── peripheral.yml │ │ │ ├── phablet.yml │ │ │ ├── portable_media_player.yml │ │ │ ├── smart_display.yml │ │ │ ├── smart_speaker.yml │ │ │ ├── smartphone-1.yml │ │ │ ├── smartphone-10.yml │ │ │ ├── smartphone-11.yml │ │ │ ├── smartphone-12.yml │ │ │ ├── smartphone-13.yml │ │ │ ├── smartphone-14.yml │ │ │ ├── smartphone-15.yml │ │ │ ├── smartphone-16.yml │ │ │ ├── smartphone-17.yml │ │ │ ├── smartphone-18.yml │ │ │ ├── smartphone-19.yml │ │ │ ├── smartphone-2.yml │ │ │ ├── smartphone-20.yml │ │ │ ├── smartphone-21.yml │ │ │ ├── smartphone-22.yml │ │ │ ├── smartphone-23.yml │ │ │ ├── smartphone-24.yml │ │ │ ├── smartphone-25.yml │ │ │ ├── smartphone-26.yml │ │ │ ├── smartphone-27.yml │ │ │ ├── smartphone-28.yml │ │ │ ├── smartphone-3.yml │ │ │ ├── smartphone-4.yml │ │ │ ├── smartphone-5.yml │ │ │ ├── smartphone-6.yml │ │ │ ├── smartphone-7.yml │ │ │ ├── smartphone-8.yml │ │ │ ├── smartphone-9.yml │ │ │ ├── smartphone.yml │ │ │ ├── tablet-1.yml │ │ │ ├── tablet-2.yml │ │ │ ├── tablet-3.yml │ │ │ ├── tablet-4.yml │ │ │ ├── tablet-5.yml │ │ │ ├── tablet-6.yml │ │ │ ├── tablet.yml │ │ │ ├── tv-1.yml │ │ │ ├── tv.yml │ │ │ ├── unknown.yml │ │ │ └── wearable.yml │ └── parser │ │ ├── __init__.py │ │ ├── fixtures │ │ ├── local │ │ │ ├── __init__.py │ │ │ ├── client │ │ │ │ ├── __init__.py │ │ │ │ ├── antivirus.yml │ │ │ │ ├── browser.yml │ │ │ │ ├── desktop_apps.yml │ │ │ │ ├── dictua.yml │ │ │ │ ├── extractor_name_version.yml │ │ │ │ ├── extractor_no_name.yml │ │ │ │ ├── extractor_whole_name.yml │ │ │ │ ├── games.yml │ │ │ │ ├── library.yml │ │ │ │ ├── mediaplayer.yml │ │ │ │ ├── messaging.yml │ │ │ │ ├── mobile_app.yml │ │ │ │ ├── osutility.yml │ │ │ │ ├── p2p.yml │ │ │ │ ├── pim.yml │ │ │ │ └── vpnproxy.yml │ │ │ ├── collisions.yml │ │ │ ├── extractor │ │ │ │ ├── __init__.py │ │ │ │ ├── app_id_override_name.yml │ │ │ │ └── applicationid.yml │ │ │ ├── osfragments.yml │ │ │ └── oss.yml │ │ └── upstream │ │ │ ├── __init__.py │ │ │ ├── client │ │ │ ├── browser.yml │ │ │ ├── feed_reader.yml │ │ │ ├── library.yml │ │ │ ├── mediaplayer.yml │ │ │ ├── mobile_app.yml │ │ │ └── pim.yml │ │ │ ├── device │ │ │ ├── camera.yml │ │ │ ├── car_browser.yml │ │ │ ├── console.yml │ │ │ └── notebook.yml │ │ │ ├── oss.yml │ │ │ └── vendorfragments.yml │ │ ├── test_bot.py │ │ ├── test_cache.py │ │ ├── test_clients.py │ │ ├── test_device.py │ │ ├── test_extractor.py │ │ ├── test_key_value_pairs.py │ │ ├── test_os.py │ │ ├── test_utils.py │ │ └── test_vendorfragment.py ├── utils.py └── yaml_loader.py ├── requirements.txt └── setup.py /.deepsource.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | [[analyzers]] 4 | name = "python" 5 | enabled = true 6 | 7 | [analyzers.meta] 8 | runtime_version = "3.x.x" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.egg-info/ 3 | .eggs/ 4 | .cache/ 5 | .tox/ 6 | build/ 7 | dist/ 8 | tmp/ 9 | .idea/ 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: xenial 2 | language: python 3 | python: 4 | - "3.6" 5 | - "3.7" 6 | install: 7 | - pip install -r requirements.txt 8 | script: 9 | - python -m unittest 10 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include requirements.txt 2 | include README.md 3 | include device_detector/appids/*.yml device_detector/appdetails/*.yml 4 | recursive-include device_detector/regexes * 5 | global-exclude *~ 6 | -------------------------------------------------------------------------------- /device_detector/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Dave Burkholder, Thinkwell Designs 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /device_detector/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '5.0.1' 2 | from .settings import * 3 | from .parser import * 4 | from .device_detector import * 5 | -------------------------------------------------------------------------------- /device_detector/appdetails/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinkwelltwd/device_detector/7df1d95d4c6342499e7d04bcb4e08a2948fde0b1/device_detector/appdetails/__init__.py -------------------------------------------------------------------------------- /device_detector/appdetails/app_ids.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # App Name / Type details for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2019 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | - app_id: com.google.gmail 9 | name: Gmail 10 | type: mobile app 11 | 12 | - app_id: com.airmailapp.iphone 13 | name: Airmail 14 | type: mobile app 15 | 16 | - app_id: com.airmailapp.iphone.notificationservice 17 | name: Airmail 18 | type: mobile app 19 | 20 | - app_id: com.yahoo.mobile.client.android.mail 21 | name: Yahoo Mobile 22 | type: mobile app 23 | -------------------------------------------------------------------------------- /device_detector/appdetails/game.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # App Name / Type details for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2019 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | - uaname: Asphalt8 9 | name: Asphalt 8 10 | 11 | - uaname: At Bat 12 | name: At Bat 13 | -------------------------------------------------------------------------------- /device_detector/appdetails/library.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # App Name / Type details for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2019 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | - uaname: xboxservicesapi 9 | name: Xbox Services API 10 | 11 | - uaname: zendesk-sdk 12 | name: Zendesk SDK 13 | -------------------------------------------------------------------------------- /device_detector/appdetails/mediaplayer.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # Supplemental App Name / Type details for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2019 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | - uaname: neroinfo 9 | name: Nero 10 | 11 | - uaname: neroupdate 12 | name: Nero 13 | 14 | - uaname: pandora 15 | name: Pandora 16 | 17 | - uaname: Spotify 18 | name: Spotify 19 | 20 | - uaname: Windows Media Player 21 | name: Windows Media Player 22 | 23 | - uaname: NSPlayer 24 | name: Windows Media Player 25 | 26 | - uaname: com.amazon.mp3 27 | name: Amazon Music 28 | -------------------------------------------------------------------------------- /device_detector/appdetails/messaging.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # App Name / Type details for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2019 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | - uaname: mattermost 9 | name: Mattermost 10 | 11 | - uaname: x-lite 12 | name: X-Lite Softphone 13 | 14 | - uaname: zoiperpremium 15 | name: Zoiper Premium Softphone 16 | 17 | - uaname: textme 18 | name: TextMe 19 | -------------------------------------------------------------------------------- /device_detector/appdetails/p2p.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # App Name / Type details for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2019 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | - uaname: Azureus 9 | name: Vuze BitTorrent Client 10 | 11 | - uaname: BTWebClient 12 | name: BitTorrent Client 13 | 14 | - uaname: bittorrent 15 | name: Torrent 16 | 17 | - uaname: torrent 18 | name: Torrent 19 | 20 | - uaname: µtorrent 21 | name: uTorrent 22 | 23 | - uaname: utorrent 24 | name: uTorrent 25 | -------------------------------------------------------------------------------- /device_detector/appdetails/pim.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # App Name / Type details for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2019 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | - uaname: applemail 9 | name: Apple Mail 10 | 11 | - uaname: bluemail 12 | name: Blue Mail 13 | 14 | - uaname: canary mail 15 | name: Canary Mail 16 | 17 | - uaname: email 18 | name: Email 19 | 20 | - uaname: android-gmail 21 | name: Gmail 22 | 23 | - uaname: androidgmail 24 | name: Gmail 25 | 26 | - uaname: gmail 27 | name: Gmail 28 | 29 | - uaname: mailbird 30 | name: Mailbird 31 | 32 | - uaname: mail 33 | name: Mail 34 | 35 | - uaname: outlook 36 | name: Microsoft Outlook 37 | 38 | - uaname: outlook-express 39 | name: Outlook Express 40 | 41 | - uaname: youmail 42 | suffixes: android 43 | name: YouMail 44 | 45 | - uaname: yahoo mail 46 | name: Yahoo Mail 47 | 48 | - uaname: ymobile 49 | name: Yahoo Mobile 50 | -------------------------------------------------------------------------------- /device_detector/appdetails/vpnproxy.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # App Name / Type details for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2019 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | - uaname: PxBroker 9 | name: ProxyBroker 10 | -------------------------------------------------------------------------------- /device_detector/appids/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinkwelltwd/device_detector/7df1d95d4c6342499e7d04bcb4e08a2948fde0b1/device_detector/appids/__init__.py -------------------------------------------------------------------------------- /device_detector/appids/ignored.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # Ignored Application IDs for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2019 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | - com.yourcompany.testwithcustomtabs 9 | - com.yourcompany.speedboxlite 10 | -------------------------------------------------------------------------------- /device_detector/appids/normalized.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # Normalized Application ID Map for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2019 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | com.apple.configurator.xpc.deviceservice: 9 | app_id: com.apple.configurator 10 | name: '' 11 | com.apple.configurator.xpc.internetservice: 12 | app_id: com.apple.configurator 13 | name: '' 14 | com.wunderground.storm: 15 | name: Storm by Weather Underground 16 | com.google.gmail: 17 | app_id: com.google.gmail 18 | name: Gmail 19 | com.google.googlemobile: 20 | app_id: com.google.googlemobile 21 | name: GoogleMobile 22 | com.google.googlemobile.trendstodayextension: 23 | app_id: com.google.googlemobile 24 | name: GoogleMobile 25 | com.google.googlemobile.keyboardextension: 26 | app_id: com.google.googlemobile 27 | name: GoogleMobile 28 | com.google.googlemobile.messagesextension: 29 | app_id: com.google.googlemobile 30 | name: GoogleMobile 31 | com.google.calendar.todayextension: 32 | app_id: com.google.calendar 33 | name: Google Calendar 34 | com.google.drive.fileproviderextension: 35 | app_id: com.google.drive 36 | name: Google Drive 37 | com.google.drive.fileprovideruiextension: 38 | app_id: com.google.drive 39 | name: Google Drive 40 | com.google.drive.shareextension: 41 | app_id: com.google.drive 42 | name: Google Drive 43 | com.google.maps.messagesextension: 44 | app_id: com.google.maps 45 | name: Google Maps 46 | com.google.maps.nearbytraffictodayextension: 47 | app_id: com.google.maps 48 | name: Google Maps 49 | com.google.ios.youtube: 50 | app_id: com.google.ios.youtube 51 | name: YouTube 52 | com.google.ios.youtube.messagesextension: 53 | app_id: com.google.ios.youtube 54 | name: YouTube 55 | com.google.ios.youtubemusic: 56 | app_id: com.googleios.youtube 57 | name: YouTube Music 58 | com.google.android.youtube.player: 59 | app_id: com.google.android.youtube 60 | name: YouTube 61 | com.google.android.youtube: 62 | app_id: com.google.android.youtube 63 | name: YouTube 64 | com.google.android.apps.youtube.music: 65 | app_id: com.google.android.apps.youtube.music 66 | name: YouTube Music -------------------------------------------------------------------------------- /device_detector/appids/secondary.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # Secondary Application IDs for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2019 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | # If UA string contains multiple App IDs, prefer any not listed here 9 | 10 | - com.usebutton.sdk 11 | - com.batch.ios 12 | - com.gimbal 13 | -------------------------------------------------------------------------------- /device_detector/lazy_regex.py: -------------------------------------------------------------------------------- 1 | from urllib.parse import unquote 2 | import regex 3 | from regex import IGNORECASE 4 | 5 | # When one of these attributes is called, compile the regex 6 | REGEX_ATTRS = { 7 | 'match', 8 | 'fullmatch', 9 | 'search', 10 | 'sub', 11 | 'subf', 12 | 'subfn', 13 | 'split', 14 | 'splititer', 15 | 'findall', 16 | 'finditer', 17 | 'purge', 18 | 'escape', 19 | 'compiled', 20 | } 21 | 22 | 23 | class RegexLazy: 24 | """ 25 | Defer compilation of regex until it's actually called. 26 | Some regexes, especially on device models will almost 27 | never be called, so save the compilation time. 28 | """ 29 | 30 | def __init__(self, pattern, flags=0): 31 | 32 | # Decode UA regexes because UA strings are also decoded 33 | # Pic%20Collage/(\d+[\.\d]+) CFNetwork 34 | self.pattern = unquote(pattern) 35 | self.flags = flags 36 | self.compiled = None 37 | 38 | def __getattribute__(self, attribute): 39 | compiled_regex = super().__getattribute__('compiled') 40 | if compiled_regex is None and attribute in REGEX_ATTRS: 41 | pattern = super().__getattribute__('pattern') 42 | flags = super().__getattribute__('flags') 43 | compiled_regex = regex.compile(pattern, flags) 44 | 45 | self.compiled = compiled_regex 46 | 47 | if attribute == 'compiled': 48 | return compiled_regex 49 | 50 | return getattr(compiled_regex, attribute) 51 | 52 | def __repr__(self): 53 | return repr(self.compiled) 54 | 55 | def __hash__(self): 56 | return hash(self.compiled) 57 | 58 | def __eq__(self, other): 59 | return self.compiled == other.compiled 60 | 61 | 62 | class RegexLazyIgnore(RegexLazy): 63 | 64 | def __init__(self, pattern): 65 | super().__init__(pattern, IGNORECASE) 66 | 67 | 68 | __all__ = ( 69 | 'RegexLazy', 70 | 'RegexLazyIgnore', 71 | ) 72 | -------------------------------------------------------------------------------- /device_detector/parser/__init__.py: -------------------------------------------------------------------------------- 1 | from .client import * 2 | from .extractors import * 3 | from .device import * 4 | from .key_value_pairs import * 5 | from .parser import * 6 | from .operating_system import * 7 | from .os_fragment import * 8 | from .settings import * 9 | -------------------------------------------------------------------------------- /device_detector/parser/client/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import * 2 | from .browser import * 3 | from .desktopapp import * 4 | from .dictua import * 5 | from .feed_reader import * 6 | from .game import * 7 | from .extractor_name_version import * 8 | from .extractor_whole_name import * 9 | from .library import * 10 | from .mediaplayer import * 11 | from .messaging import * 12 | from .mobileapp import * 13 | from .p2p import * 14 | from .pim import * 15 | from .vpnproxy import * 16 | -------------------------------------------------------------------------------- /device_detector/parser/client/browser.py: -------------------------------------------------------------------------------- 1 | try: 2 | import regex as re 3 | except (ImportError, ModuleNotFoundError): 4 | import re 5 | from . import BaseClientParser 6 | from ...settings import BOUNDED_REGEX 7 | from ..settings import ( 8 | AVAILABLE_BROWSERS, 9 | AVAILABLE_ENGINES, 10 | BROWSER_FAMILIES, 11 | BROWSER_TO_ABBREV, 12 | FAMILY_FROM_ABBREV, 13 | CHECK_PAIRS, 14 | MOBILE_ONLY_BROWSERS, 15 | ) 16 | 17 | from .extractor_name_version import NameVersionExtractor 18 | from .extractor_whole_name import WholeNameExtractor 19 | 20 | 21 | class EngineVersion: 22 | 23 | def __init__(self, user_agent): 24 | self.user_agent = user_agent 25 | 26 | def parse(self, engine) -> str: 27 | if not engine: 28 | return '' 29 | 30 | regex = r"{engine}\s*\/?\s*((?=\d+\.\d)\d+[.\d]*|\d{{1,7}}(?=(?:\D|$)))".format( 31 | engine=engine 32 | ) 33 | regex = BOUNDED_REGEX.format(regex) 34 | match = re.search(regex, self.user_agent, re.IGNORECASE) 35 | if match: 36 | engine_version = self.user_agent[match.start():match.end()] 37 | try: 38 | return engine_version.split('/')[1] 39 | except IndexError: 40 | pass 41 | 42 | return '' 43 | 44 | 45 | class Engine(BaseClientParser): 46 | 47 | AVAILABLE_ENGINES = AVAILABLE_ENGINES 48 | 49 | fixture_files = [ 50 | 'upstream/client/browser_engine.yml', 51 | ] 52 | 53 | def _parse(self): 54 | super()._parse() 55 | if 'name' in self.ua_data: 56 | self.ua_data['engine_version'] = EngineVersion( 57 | self.user_agent, 58 | ).parse( 59 | engine=self.ua_data['name'], 60 | ) 61 | 62 | 63 | class Browser(BaseClientParser): 64 | 65 | fixture_files = [ 66 | 'local/client/browsers.yml', 67 | 'upstream/client/browsers.yml', 68 | ] 69 | 70 | AVAILABLE_ENGINES = AVAILABLE_ENGINES 71 | AVAILABLE_BROWSERS = AVAILABLE_BROWSERS 72 | BROWSER_TO_ABBREV = BROWSER_TO_ABBREV 73 | BROWSER_FAMILIES = BROWSER_FAMILIES 74 | FAMILY_FROM_ABBREV = FAMILY_FROM_ABBREV 75 | MOBILE_ONLY_BROWSERS = MOBILE_ONLY_BROWSERS 76 | 77 | def has_interesting_pair(self): 78 | """ 79 | If the UA string has interesting name/version pair(s), 80 | we don't want to process Browser regexes, but rather 81 | move on to other parser classes. 82 | """ 83 | # if the name <= 2 characters, don't consider it interesting 84 | # if that name is actually interesting, add to relevant 85 | # appdetails/.yml, so it'll be parsed before now. 86 | for code, name, version in self.name_version_pairs(): 87 | if len(name) > 2 and not name.lower().endswith(('build', 'version')): 88 | return True 89 | return False 90 | 91 | def set_details(self): 92 | super().set_details() 93 | if self.ua_data: 94 | browser = self.ua_data.get('name', '') 95 | abbrevation = self.BROWSER_TO_ABBREV.get(browser.lower(), browser) 96 | self.ua_data.update({ 97 | 'short_name': abbrevation, 98 | 'family': self.FAMILY_FROM_ABBREV.get(abbrevation, browser), 99 | }) 100 | 101 | if 'engine' not in self.ua_data: 102 | self.ua_data['engine'] = Engine( 103 | self.user_agent, 104 | self.ua_hash, 105 | self.ua_spaceless, 106 | self.VERSION_TRUNCATION, 107 | ).parse().ua_data 108 | 109 | def short_name(self) -> str: 110 | return self.ua_data.get('short_name', None) 111 | 112 | def engine(self): 113 | if not self.ua_data.get('engine', ''): 114 | return '' 115 | if 'default' in self.ua_data['engine']: 116 | return self.ua_data['engine']['default'] 117 | return self.ua_data['engine']['name'] 118 | 119 | def is_mobile_only(self): 120 | return self.short_name() in self.MOBILE_ONLY_BROWSERS 121 | 122 | def _parse(self) -> None: 123 | super()._parse() 124 | self.check_secondary_client_data() 125 | 126 | def check_secondary_client_data(self): 127 | """ 128 | If the UA string matched is a browser that often 129 | contains more specific app information, check to 130 | see if name_version_pairs has data of interest. 131 | """ 132 | # Call these extractors here, since this regex matching as 133 | # browser means no further Client Parsers would be run. 134 | if self.ua_data.get('name', '') in CHECK_PAIRS: 135 | if self.has_interesting_pair(): 136 | self.get_secondary_client_data(extractor=NameVersionExtractor) 137 | else: 138 | self.get_secondary_client_data(extractor=WholeNameExtractor) 139 | 140 | def get_secondary_client_data(self, extractor): 141 | """ 142 | Update secondary_client dict with any data from specified extractor 143 | """ 144 | parsed = extractor( 145 | ua=self.user_agent, 146 | ua_hash=self.ua_hash, 147 | ua_spaceless=self.ua_spaceless, 148 | version_truncation=self.VERSION_TRUNCATION, 149 | ).parse() 150 | 151 | if parsed.ua_data: 152 | self.secondary_client = parsed.ua_data 153 | self.ua_data['secondary_client'] = parsed.ua_data 154 | 155 | 156 | __all__ = ( 157 | 'Browser', 158 | 'Engine', 159 | 'EngineVersion', 160 | ) 161 | -------------------------------------------------------------------------------- /device_detector/parser/client/desktopapp.py: -------------------------------------------------------------------------------- 1 | from . import BaseClientParser 2 | 3 | 4 | class DesktopApp(BaseClientParser): 5 | 6 | fixture_files = [ 7 | 'local/client/osutility.yml', 8 | 'local/client/antivirus.yml', 9 | 'local/client/desktop_apps.yml', 10 | ] 11 | 12 | def dtype(self): 13 | return self.calculated_dtype or self.ua_data.get('type', '') or 'desktop app' 14 | 15 | 16 | __all__ = [ 17 | 'DesktopApp', 18 | ] 19 | -------------------------------------------------------------------------------- /device_detector/parser/client/dictua.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | try: 4 | import rapidjson as json 5 | except ImportError: 6 | import json 7 | 8 | from . import BaseClientParser 9 | from ...utils import calculate_dtype 10 | 11 | 12 | class DictUA(BaseClientParser): 13 | """ 14 | Some UA strings can be loaded as dicts directly, or with a little parsing. 15 | 16 | {"ac":"CCDesktop_app","av":"4.8.1.435"} 17 | {"locale":"en_US","appVersion":"5.2.5","os":"iOS","deviceName":"iPhone","deviceType":"phone","iOSApiLevel":"12.1.4","gzip":true,"appId":"KHAiOS","buildNumber":"31667"} 18 | target=LetGo; appVersion=1.58.0; bundle=com.letgo.ios; build=524; os=iOS 9.2.1; device=Apple iPad4,2; httpLibrary=Alamofire/4.7.3 19 | """ 20 | 21 | def load_via_json(self): 22 | try: 23 | # sanity check - really shouldn't need to cast to dict. 24 | # at least 1 UA doesn't crash json.loads but remains a string. 25 | return dict(json.loads(self.user_agent)) 26 | except Exception: 27 | return {} 28 | 29 | def parse_key_value_pairs(self): 30 | # AppName=iOSProApp;AppId=3;Platform=iOS;Model=iPad Pro 9.7-inch (Wi-Fi Cellular);OSVersion=12.0;Carrier=iPad;AppVersion=3.27.0.4 31 | try: 32 | return dict(item.strip().split("=") for item in self.user_agent.split(";")) 33 | except Exception: 34 | return {} 35 | 36 | def ua_as_dict(self) -> Optional[dict]: 37 | 38 | valid_json = self.load_via_json() 39 | if valid_json: 40 | return valid_json 41 | 42 | return self.parse_key_value_pairs() 43 | 44 | def _parse(self) -> None: 45 | """ 46 | Each subclass may have `appdetails/.yml` file(s) defined 47 | containing manually specified details for the regex. 48 | 49 | These files before checking regexes, for best performance. 50 | """ 51 | ua_as_dict = self.ua_as_dict() 52 | if not ua_as_dict: 53 | return 54 | 55 | # descending order of interest! 56 | for name_key in ( 57 | 'app', 58 | 'AppName', 59 | 'target', 60 | 'bundle', 61 | 'bundleId', 62 | 'bundleid', 63 | 'BundleID', 64 | 'ac', 65 | 'appId', 66 | ): 67 | name = ua_as_dict.get(name_key, '') 68 | if name: 69 | self.app_name = name 70 | self.ua_data['name'] = name 71 | break 72 | 73 | # don't try to extract a version if we couldn't find a name 74 | if not self.ua_data: 75 | return 76 | 77 | # descending order of interest! 78 | for version_key in ( 79 | 'AppVersion', 80 | 'appVersion', 81 | 'version', 82 | 'Version', 83 | 'ver', 84 | 'Ver', 85 | 'av', 86 | ): 87 | version = ua_as_dict.get(version_key, '') 88 | if version: 89 | self.app_version = version_key 90 | self.ua_data['version'] = version 91 | break 92 | 93 | def dtype(self) -> str: 94 | return self.calculated_dtype or calculate_dtype(app_name=self.app_name) 95 | 96 | 97 | __all__ = [ 98 | 'DictUA', 99 | ] 100 | -------------------------------------------------------------------------------- /device_detector/parser/client/extractor_name_version.py: -------------------------------------------------------------------------------- 1 | from . import GenericClientParser 2 | from ..settings import METADATA_NAMES 3 | 4 | 5 | class NameVersionExtractor(GenericClientParser): 6 | """ 7 | Generic extractor for user agents that do not have a matching regex and 8 | have a , e.g. HotelSearch/187 9 | 10 | Also support user agents that have a 11 | format, HotelSearch/ios_5 12 | 13 | Checks all name/version pairs, preferring any name that the UA string 14 | starts with. If that beginning of the UA string isn't interesting, 15 | prefer the longest name in the name/value pair list. 16 | """ 17 | 18 | # ------------------------------------------------------------------- 19 | app_name = '' 20 | app_version = '' 21 | 22 | def parse_name_version_pairs(self): 23 | """ 24 | Check all name/version pairs for most interesting values 25 | """ 26 | name_version_pairs = self.name_version_pairs() 27 | 28 | for code, name, version in name_version_pairs: 29 | 30 | # Only extract interesting pairs! 31 | if name.isdigit() \ 32 | or len(name) == 1 \ 33 | or code in METADATA_NAMES \ 34 | or code.endswith(('version', 'build')): 35 | continue 36 | 37 | # prefer the name that the UA starts with 38 | if self.user_agent.startswith(name): 39 | self.app_name = name 40 | self.app_version = version 41 | return 42 | 43 | # consider longest name the most interesting 44 | if len(name) > len(self.app_name): 45 | self.app_name = name 46 | self.app_version = version 47 | 48 | def version_contains_numbers(self): 49 | """ 50 | Version contains no numeric characters 51 | """ 52 | if not self.app_version: 53 | return False 54 | 55 | for char in self.app_version: 56 | if char.isnumeric(): 57 | return True 58 | 59 | return False 60 | 61 | def _parse(self) -> None: 62 | 63 | self.parse_name_version_pairs() 64 | 65 | if not self.app_name: 66 | return 67 | 68 | if self.discard_name(): 69 | return 70 | 71 | self.ua_data = { 72 | 'name': self.app_name, 73 | 'version': self.app_version if self.version_contains_numbers() else '', 74 | } 75 | 76 | self.known = True 77 | 78 | 79 | __all__ = [ 80 | 'NameVersionExtractor', 81 | ] 82 | -------------------------------------------------------------------------------- /device_detector/parser/client/extractor_whole_name.py: -------------------------------------------------------------------------------- 1 | from . import GenericClientParser 2 | 3 | from ...lazy_regex import RegexLazyIgnore 4 | from ..settings import SKIP_PREFIXES 5 | from ...settings import DDCache 6 | 7 | # ------------------------------------------------------------------- 8 | # Regexes that we use to parse UAs with a similar structure 9 | parse_generic_regex = [ 10 | # Weather_WeatherFoundation[1]_15E302 11 | # SpringBoard_WeatherFoundation[1]_16A 12 | (RegexLazyIgnore(r'(^(?:\w+)_WeatherFoundation).*'), 1), 13 | (RegexLazyIgnore(r'(^AVGSETUP).*'), 1), 14 | 15 | # ACC__14EDB170A0EAA78B40F 16 | (RegexLazyIgnore(r'(^ACC)__[A-Z0-9].*'), 1), 17 | 18 | # YWeather (iPhone/11.4.1; iOS; en_US;) 19 | # Mapbox iOS SDK (iPhone/11.1.1) 20 | # SCSDK/(iPhone;iOS;12.2) 21 | # Akamai NetSession C-API (win;AdDLMgr;capi_1.9.2;Vista) 22 | (RegexLazyIgnore(r'^([a-z0-9\- ]+)[ /]?\((?:iphone|ipad|win)'), 1), 23 | 24 | # CPIS&iPhone10.1& 25 | (RegexLazyIgnore(r'(^CPIS).*'), 1), 26 | 27 | # samsung SAMSUNG-SM-T337A SyncML_DM Client 28 | # samsung SMT377P SPDClient to samsung SM-T377P SPD-Client 29 | (RegexLazyIgnore(r'^samsung.*((?:SyncML_DM|SPD)[ \-]Client)$'), 1), 30 | (RegexLazyIgnore(r'(WXCommonUtils).*'), 1), 31 | 32 | # UBT_1EDA413E5BA1DEA76A 33 | # UBT_1ED 34 | (RegexLazyIgnore(r'^(UBT)_[\d\w]+$'), 1), 35 | 36 | # ACC10FDFFD20BCFEDA0D7D 37 | # ACC_4.00.3019_1BF78BFDF84E7EEFDA 38 | # ALU_1.02.3005_1EDDDB14FD0EBB93AE 39 | # ALU1023502106209EEF239E246AA 40 | # ALU__1023502106209EEF239E246AA 41 | (RegexLazyIgnore(r'^(ACC|ALU)_*[\d\w_\.]+$'), 1), 42 | 43 | # mShop:::Telly_iPhone_13.7.0:::iPad:::iPhone_OS_ == Telly_iPhone_13.7.0 44 | # mShop:::Amazon_Android_18.11.0.100:::SAMSUNG-SM-G935A:::Android_6.0.1 45 | # mShop:::WindowShop_Android_16.13.0.850:::SM-T817V:::Android_6.0.1 46 | (RegexLazyIgnore(r'^mshop:+([a-z0-9_\.]+)'), 1), 47 | ] 48 | 49 | # ------------------------------------------------------------------- 50 | # Regexes that we use to extract app versions 51 | extract_version_regex = [ 52 | # ANVSDKv.5.0.21 53 | # ANVSDKv5.0.21 54 | (RegexLazyIgnore(r'(v?[\.\d]+$)')), 55 | 56 | # Catch versions with trailing, separated characters 57 | # AppNamev.5.0.21_PRC = v.5.0.21 58 | (RegexLazyIgnore(r'((?:v.?)?\d[\d\.]+)')), 59 | ] 60 | 61 | 62 | class WholeNameExtractor(GenericClientParser): 63 | """ 64 | Catch all for user agents that do not use the slash format 65 | """ 66 | 67 | parse_generic_regex = parse_generic_regex 68 | extract_version_regex = extract_version_regex 69 | 70 | # ------------------------------------------------------------------- 71 | def _parse(self): 72 | 73 | self.clean_name() 74 | 75 | if self.discard_name() or not self.is_name_length_valid(): 76 | return 77 | 78 | self.parse_name_version() 79 | self.check_manual_appdetails() 80 | 81 | # WholeNameExtractor is called to supply secondary app data 82 | # if the Browser class matches. So if only Browser data was 83 | # found then there is no "secondary" data! 84 | if self.app_name.lower() in SKIP_PREFIXES: 85 | return 86 | 87 | app_details = self.appdetails_data 88 | code = self.app_name.lower().replace(' ', '') 89 | 90 | try: 91 | self.app_name = app_details[code]['name'] 92 | self.calculated_dtype = app_details[code].get('type', '') 93 | except KeyError: 94 | pass 95 | 96 | self.ua_data = { 97 | 'name': self.app_name, 98 | 'version': self.app_version, 99 | 'type': self.calculated_dtype, 100 | } 101 | 102 | self.known = True 103 | 104 | def parse_name_version(self) -> str: 105 | """ 106 | Check if app name has a suffix with the version number and 107 | extract if it does. Return the UA string without the suffix. 108 | """ 109 | for regex in self.extract_version_regex: 110 | 111 | match = regex.search(self.app_name) 112 | if match: 113 | self.app_version = match.group().strip() 114 | self.app_name = self.user_agent[:match.start()].strip(' /-') 115 | return self.app_version 116 | 117 | def is_name_length_valid(self) -> bool: 118 | """ 119 | Check if app name portion of UA is between 3 and 60 chars 120 | """ 121 | return 2 < len(self.app_name) <= 60 122 | -------------------------------------------------------------------------------- /device_detector/parser/client/feed_reader.py: -------------------------------------------------------------------------------- 1 | from . import BaseClientParser 2 | 3 | 4 | class FeedReader(BaseClientParser): 5 | 6 | def dtype(self): 7 | return self.calculated_dtype or 'feed reader' 8 | 9 | fixture_files = [ 10 | 'upstream/client/feed_readers.yml', 11 | ] 12 | 13 | 14 | __all__ = [ 15 | 'FeedReader', 16 | ] 17 | -------------------------------------------------------------------------------- /device_detector/parser/client/game.py: -------------------------------------------------------------------------------- 1 | from . import BaseClientParser 2 | 3 | 4 | class Game(BaseClientParser): 5 | 6 | fixture_files = [ 7 | 'local/client/games.yml', 8 | ] 9 | 10 | def dtype(self): 11 | return self.calculated_dtype or 'game' 12 | 13 | 14 | __all__ = [ 15 | 'Game', 16 | ] 17 | -------------------------------------------------------------------------------- /device_detector/parser/client/library.py: -------------------------------------------------------------------------------- 1 | from . import BaseClientParser 2 | 3 | 4 | class Library(BaseClientParser): 5 | 6 | fixture_files = [ 7 | 'local/client/libraries.yml', 8 | 'upstream/client/libraries.yml', 9 | ] 10 | 11 | 12 | __all__ = [ 13 | 'Library', 14 | ] 15 | -------------------------------------------------------------------------------- /device_detector/parser/client/mediaplayer.py: -------------------------------------------------------------------------------- 1 | from . import BaseClientParser 2 | 3 | 4 | class MediaPlayer(BaseClientParser): 5 | 6 | fixture_files = [ 7 | 'local/client/mediaplayers.yml', 8 | 'upstream/client/mediaplayers.yml', 9 | ] 10 | 11 | 12 | __all__ = [ 13 | 'MediaPlayer', 14 | ] 15 | -------------------------------------------------------------------------------- /device_detector/parser/client/messaging.py: -------------------------------------------------------------------------------- 1 | from . import BaseClientParser 2 | 3 | 4 | class Messaging(BaseClientParser): 5 | 6 | def dtype(self): 7 | return self.calculated_dtype or 'messaging' 8 | 9 | 10 | __all__ = [ 11 | 'Messaging', 12 | ] 13 | -------------------------------------------------------------------------------- /device_detector/parser/client/mobileapp.py: -------------------------------------------------------------------------------- 1 | from . import BaseClientParser 2 | 3 | 4 | class MobileApp(BaseClientParser): 5 | 6 | fixture_files = [ 7 | 'local/client/mobile_apps.yml', 8 | 'upstream/client/mobile_apps.yml', 9 | ] 10 | 11 | def dtype(self): 12 | return self.calculated_dtype or 'mobile app' 13 | 14 | 15 | __all__ = [ 16 | 'MobileApp', 17 | ] 18 | -------------------------------------------------------------------------------- /device_detector/parser/client/p2p.py: -------------------------------------------------------------------------------- 1 | from . import BaseClientParser 2 | 3 | 4 | class P2P(BaseClientParser): 5 | 6 | fixture_files = [ 7 | 'local/client/p2p.yml', 8 | ] 9 | 10 | def dtype(self): 11 | return self.calculated_dtype or 'p2p' 12 | 13 | 14 | __all__ = [ 15 | 'P2P', 16 | ] 17 | -------------------------------------------------------------------------------- /device_detector/parser/client/pim.py: -------------------------------------------------------------------------------- 1 | from . import BaseClientParser 2 | 3 | 4 | class PIM(BaseClientParser): 5 | 6 | fixture_files = [ 7 | 'local/client/pim.yml', 8 | 'upstream/client/pim.yml', 9 | ] 10 | 11 | 12 | __all__ = [ 13 | 'PIM', 14 | ] 15 | -------------------------------------------------------------------------------- /device_detector/parser/client/vpnproxy.py: -------------------------------------------------------------------------------- 1 | from . import BaseClientParser 2 | 3 | 4 | class VPNProxy(BaseClientParser): 5 | 6 | fixture_files = [ 7 | 'local/client/vpnproxy.yml', 8 | ] 9 | 10 | def dtype(self): 11 | return self.calculated_dtype or 'vpnproxy' 12 | 13 | 14 | __all__ = [ 15 | 'VPNProxy', 16 | ] 17 | -------------------------------------------------------------------------------- /device_detector/parser/device/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import * 2 | from .bot import * 3 | from .device import * 4 | from .vendor_fragment import * 5 | -------------------------------------------------------------------------------- /device_detector/parser/device/bot.py: -------------------------------------------------------------------------------- 1 | from .base import BaseDeviceParser 2 | 3 | 4 | class Bot(BaseDeviceParser): 5 | 6 | fixture_files = [ 7 | 'upstream/bots.yml', 8 | ] 9 | 10 | def is_bot(self) -> bool: 11 | return self.matched_regex is not None 12 | 13 | 14 | __all__ = [ 15 | 'Bot', 16 | ] 17 | -------------------------------------------------------------------------------- /device_detector/parser/device/vendor_fragment.py: -------------------------------------------------------------------------------- 1 | from . import BaseDeviceParser 2 | from ...lazy_regex import RegexLazyIgnore 3 | 4 | 5 | class VendorFragment(BaseDeviceParser): 6 | fixture_files = [ 7 | 'upstream/vendorfragments.yml', 8 | ] 9 | 10 | def yaml_to_list(self, yfile) -> list: 11 | """ 12 | List of dicts like so: 13 | 14 | {'name': 'Dell', 'regexes': ['MDDR(JS)?', 'MDDC(JS)?', 'MDDS(JS)?']} 15 | """ 16 | new_regexes = self.load_from_yaml(yfile) 17 | reg_list = [] 18 | 19 | for brand, regexes in new_regexes.items(): 20 | reg_list.append({ 21 | 'name': brand, 22 | 'regexes': [RegexLazyIgnore(r) for r in regexes], 23 | }) 24 | 25 | return reg_list 26 | 27 | def _parse(self) -> None: 28 | for ua_data in self.regex_list: 29 | for regex in ua_data['regexes']: 30 | match = self._check_regex(regex) 31 | if match: 32 | self.matched_regex = match 33 | self.ua_data = ua_data.copy() 34 | self.known = True 35 | 36 | name = ua_data['name'] 37 | self.ua_data['brand'] = self.BRAND_TO_ABBREV.get(name.lower(), name) 38 | self.ua_data.pop('regexes', False) 39 | 40 | return 41 | 42 | 43 | __all__ = [ 44 | 'VendorFragment', 45 | ] 46 | -------------------------------------------------------------------------------- /device_detector/parser/key_value_pairs.py: -------------------------------------------------------------------------------- 1 | from ..lazy_regex import RegexLazyIgnore 2 | from .settings import SKIP_PREFIXES 3 | 4 | CONTAINS_URL = RegexLazyIgnore( 5 | r'https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)', 6 | ) 7 | 8 | # Extra version / name from UAs 9 | VERSION_NAME_REGEXES = ( 10 | # 1.172.0.1 - LIVE - Mar 5 2020 11 | # 17build 113411 LIVE Sep 17 20180 12 | RegexLazyIgnore(r'(?P[\d\.]+)[ \-]+(?PLIVE)'), 13 | 14 | # 15.5.53 Boxcar 15 | # 165 CandyCanes 16 | RegexLazyIgnore(r'^(?P[\d\.]+)[ \-/]+(?P\w+)$'), 17 | ) 18 | 19 | # Extra name / version from UAs 20 | NAME_VERSION_REGEXES = ( 21 | 22 | # Get ALL / pairs from the regex 23 | RegexLazyIgnore(r'(?P[\w\d\.\-_\&\'!®\?, \+\&]+)/(?P[\d\.\-\w\&\?]+)\b'), 24 | 25 | # - anchored at the beginning 26 | # CarboniteDownloader 6.3.2 build 7466 (Sep-07-2017) 27 | # libreoffice 5.4.3.2 (92a7159f7e4af62137622921e809f8546db437e5; windows; x86;) 28 | # openoffice.org 3.2 (320m18(build:9502); windows; x86; bundledlanguages=en-us) 29 | RegexLazyIgnore(r'^(?P[\w\._\&\'!®\?,\+\&]+) [rv]?(?P[\d\.\-\&\?]+)\b'), 30 | 31 | # - anywhere in string 32 | # Microsoft Office Access 2013 (15.0.4693) Windows NT 6.2, where version == 15.0.4693 33 | # Microsoft.VisualStudio.Help (2.3) 34 | # Microsoft URL Control - 6.01.9782 35 | # openshot-qt-2.4.2 36 | # DigiCal (v1.8.2b; http://digibites.nl/digical) 37 | # iPad; 10.3.3; autotrader.com; 3.0.10 38 | RegexLazyIgnore( 39 | r'(?P[\w\d\.\-_\&\'!®\?, \+]+) ?[(\-;] ?[vr]?(?P[\d\.]+)(?:\b|\w|$)' 40 | ), 41 | 42 | # - anywhere in remainder of string 43 | # Mozilla/5.0 AppleWebKit/537.36 Mobile Safari/537.36 Android SermonAudio.com 1.9.8, wanting "SermonAudio.com 1.9.8" 44 | RegexLazyIgnore(r'(?P[\w\._\&\'!®\?,\+\& ]+) [rv]?(?P[\d\.\-\&\?]+)\b'), 45 | ) 46 | 47 | # / pairs with names matching these 48 | # regexes are not considered interesting 49 | SKIP_NAME_REGEXES = [ 50 | # Android; samsung-SAMSUNG-SM-T377A; 6.0.1; AutoTrader.com; 2.6.4.3.236 51 | RegexLazyIgnore(r'samsung[- ]sm'), 52 | 53 | # Mozilla/5.0 (iPhone; iPhone103; 12.1.4) MLN/4.30.450041483 (0f3d913a35528d98b8793f4d7aa0539e)" 54 | RegexLazyIgnore(r'^(iphone|ipad)\d'), 55 | ] 56 | 57 | 58 | def name_matches_regex(name) -> bool: 59 | """ 60 | If name matches regex, then don't 61 | want its name/version pair. 62 | """ 63 | for rgx in SKIP_NAME_REGEXES: 64 | if rgx.search(name) is not None: 65 | return True 66 | 67 | return False 68 | 69 | 70 | def scrub_name_version_pairs(matches: list) -> list: 71 | """ 72 | Takes list of (name,version) tuples. 73 | Remove all pairs where name matches SKIP patterns 74 | """ 75 | pairs = [] 76 | for name, version in matches: 77 | name = name.strip(' -,') 78 | if not name: 79 | continue 80 | 81 | # does this look like base64 encoded data? 82 | if name.endswith('=='): 83 | continue 84 | 85 | name_lower = name.lower() 86 | if name_lower in SKIP_PREFIXES: 87 | continue 88 | 89 | if name_matches_regex(name): 90 | continue 91 | 92 | code = name_lower.replace(' ', '') 93 | pairs.append((code, name, version.strip())) 94 | 95 | return pairs 96 | 97 | 98 | def extract_version_name_pairs(regex, ua): 99 | """ 100 | Extract all key/value pairs of the specified regex, 101 | where key==version and value==name 102 | and return pairs along with unmatched portion of ua string. 103 | """ 104 | match = regex.search(ua) 105 | 106 | if match: 107 | return scrub_name_version_pairs([(match.group('name'), match.group('version'))]) 108 | 109 | return [] 110 | 111 | 112 | def extract_name_version_pairs(regex, ua): 113 | """ 114 | Extract all key/value pairs of the specified regex, 115 | where key==name and value==version 116 | and return pairs along with unmatched portion of ua string. 117 | """ 118 | matches = regex.findall(ua) 119 | substring = ua 120 | 121 | if matches: 122 | substring = regex.sub(' ', ua) 123 | 124 | pairs = scrub_name_version_pairs(matches) 125 | 126 | return pairs, substring 127 | 128 | 129 | def key_value_pairs(ua): 130 | """ 131 | Extract key/value pairs from User Agent String 132 | """ 133 | 134 | substring = CONTAINS_URL.sub(' ', ua) 135 | 136 | all_pairs = [] 137 | 138 | for rgx in VERSION_NAME_REGEXES: 139 | pairs = extract_version_name_pairs(rgx, substring) 140 | if pairs: 141 | all_pairs.extend(pairs) 142 | 143 | # / regexes will be much less common 144 | # so if we found such entries return then first 145 | if all_pairs: 146 | return all_pairs 147 | 148 | for rgx in NAME_VERSION_REGEXES: 149 | pairs, substring = extract_name_version_pairs(rgx, substring) 150 | all_pairs.extend(pairs) 151 | 152 | return all_pairs 153 | 154 | 155 | __all__ = ( 156 | 'extract_name_version_pairs', 157 | 'key_value_pairs', 158 | ) 159 | -------------------------------------------------------------------------------- /device_detector/parser/os_fragment.py: -------------------------------------------------------------------------------- 1 | from . import BaseDeviceParser 2 | from ..lazy_regex import RegexLazyIgnore 3 | 4 | 5 | class OSFragment(BaseDeviceParser): 6 | fixture_files = [ 7 | 'local/osfragments.yml', 8 | ] 9 | 10 | def yaml_to_list(self, yfile) -> list: 11 | """ 12 | List of dicts like so: 13 | 14 | {'name': 'iOS', 'regexes': ['iPhone', 'iPad']} 15 | """ 16 | new_regexes = self.load_from_yaml(yfile) 17 | reg_list = [] 18 | 19 | # load and compile regexes. Not using boundaries. 20 | for os, regexes in new_regexes.items(): 21 | reg_list.append({ 22 | 'name': os, 23 | 'regexes': [RegexLazyIgnore(reg) for reg in regexes], 24 | }) 25 | 26 | return reg_list 27 | 28 | def _parse(self) -> None: 29 | 30 | for ua_data in self.regex_list: 31 | for regex in ua_data['regexes']: 32 | match = regex.search(self.user_agent) 33 | 34 | if match: 35 | self.matched_regex = match 36 | self.ua_data = ua_data.copy() 37 | self.known = True 38 | 39 | self.ua_data.pop('regexes', False) 40 | 41 | return 42 | 43 | 44 | __all__ = [ 45 | 'OSFragment', 46 | ] 47 | -------------------------------------------------------------------------------- /device_detector/parser/parser.py: -------------------------------------------------------------------------------- 1 | try: 2 | import regex as re 3 | except (ImportError, ModuleNotFoundError): 4 | import re 5 | 6 | from ..settings import ( 7 | DDCache, 8 | ) 9 | from .extractors import ( 10 | NameExtractor, 11 | ModelExtractor, 12 | VersionExtractor, 13 | ) 14 | from ..yaml_loader import RegexLoader 15 | 16 | 17 | def build_version(version_str: str, truncation=1): 18 | """ 19 | Extract basic version from strings like 10.0.16299.371 20 | 21 | >>> build_version('10.0.16299.371') 22 | '10' 23 | 24 | >>> build_version('10') 25 | '10' 26 | """ 27 | if truncation == -1: 28 | return version_str 29 | 30 | retain_segments = truncation + 1 31 | 32 | try: 33 | segments = version_str.replace('_', '.').split('.') 34 | except AttributeError: 35 | return version_str 36 | 37 | if len(segments) == retain_segments: 38 | return version_str 39 | 40 | return '.'.join(segments[:retain_segments]) 41 | 42 | 43 | class Parser(RegexLoader): 44 | 45 | # Constant used as value for unknown browser / os 46 | UNKNOWN = 'UNK' 47 | UNKNOWN_NAME = 'Unknown' 48 | 49 | def __init__(self, ua, ua_hash, ua_spaceless, version_truncation): 50 | super().__init__(version_truncation) 51 | 52 | self.user_agent = ua 53 | self.ua_hash = ua_hash 54 | self.ua_spaceless = ua_spaceless 55 | self.ua_data = {} 56 | self.app_name = '' 57 | self.app_name_no_punctuation = '' 58 | self.matched_regex = None 59 | self.app_version = None 60 | self.known = False 61 | self.calculated_dtype = '' 62 | self.secondary_client = {} 63 | 64 | @property 65 | def cache_name(self) -> str: 66 | """Class name, used for cache key""" 67 | return self.__class__.__name__ 68 | 69 | def dtype(self) -> str: 70 | """ 71 | For adding 'type' key to ua_data 72 | """ 73 | return self.calculated_dtype or self.cache_name.lower() 74 | 75 | def get_from_cache(self) -> dict: 76 | try: 77 | return DDCache['user_agents'][self.ua_hash].get(self.cache_name, None) 78 | except KeyError: 79 | DDCache['user_agents'][self.ua_hash] = {} 80 | return {} 81 | 82 | def add_to_cache(self) -> dict: 83 | DDCache['user_agents'][self.ua_hash][self.cache_name] = self.ua_data 84 | return self.ua_data 85 | 86 | def _check_regex(self, regex): 87 | try: 88 | return regex.search(self.user_agent) 89 | except Exception as e: 90 | print('{} fired an error {}'.format(regex, e)) 91 | if re.__name__ == 're': 92 | print( 93 | 'You are using the builtin "re" library. ' 94 | 'Consider installing the "regex" library instead.' 95 | ) 96 | return None 97 | 98 | def _parse(self) -> None: 99 | """Override on subclasses if custom parsing is required""" 100 | for ua_data in self.regex_list: 101 | match = self._check_regex(ua_data['regex']) 102 | if match: 103 | self.matched_regex = match 104 | self.ua_data = ua_data.copy() 105 | self.known = True 106 | return 107 | 108 | def parse(self): 109 | """ 110 | Return parsed details of UA String 111 | """ 112 | details = self.get_from_cache() 113 | if details: 114 | return self 115 | 116 | self._parse() 117 | self.extract_details() 118 | 119 | return self 120 | 121 | def extract_details(self) -> dict: 122 | """ 123 | Wrap set_details and call add_to_cache 124 | """ 125 | self.extract_version() 126 | self.set_details() 127 | self.add_to_cache() 128 | return self.ua_data 129 | 130 | def extract_version(self): 131 | """ 132 | Extract the version if UA Yaml files specify version regexes. 133 | See oss.yml for example file structure. 134 | """ 135 | for version in self.ua_data.pop('versions', []): 136 | match = self._check_regex(version['regex']) 137 | if match: 138 | self.ua_data['version'] = version['version'] 139 | return 140 | 141 | def set_details(self) -> None: 142 | """ 143 | Override this method on subclasses. 144 | 145 | Update fields with interpolated values from regex data 146 | """ 147 | if not self.ua_data: 148 | return 149 | self.ua_data.pop('regex', False) 150 | self.ua_data.pop('models', False) 151 | groups = self.matched_regex and self.matched_regex.groups() 152 | 153 | if groups: 154 | if 'model' in self.ua_data: 155 | self.ua_data['model'] = ModelExtractor(self.ua_data, groups).extract() 156 | 157 | if 'name' in self.ua_data: 158 | self.ua_data['name'] = NameExtractor(self.ua_data, groups).extract() 159 | 160 | if 'version' in self.ua_data: 161 | self.ua_data['version'] = VersionExtractor(self.ua_data, groups).extract() 162 | 163 | # no version should be considered valid if the name can't be parsed 164 | if not self.ua_data.get('name') and self.ua_data.get('version'): 165 | self.ua_data['version'] = '' 166 | 167 | self.ua_data.update({ 168 | 'type': self.dtype(), 169 | 'model': (self.ua_data.get('model') or '').replace('_', ' '), 170 | 'version': self.set_version(self.ua_data.get('version', '')), 171 | }) 172 | 173 | def name(self) -> str: 174 | return self.ua_data.get('name', '') 175 | 176 | def version(self) -> str: 177 | return self.ua_data.get('version', '') 178 | 179 | def secondary_name(self): 180 | if self.secondary_client: 181 | return self.secondary_client['name'] 182 | return '' 183 | 184 | def secondary_version(self): 185 | if self.secondary_client: 186 | return self.secondary_client['version'] 187 | return '' 188 | 189 | def secondary_type(self): 190 | if self.secondary_client: 191 | return self.secondary_client['type'] 192 | return '' 193 | 194 | def is_known(self) -> bool: 195 | if self.ua_data: 196 | return True 197 | return False 198 | 199 | def set_version(self, version): 200 | return build_version(version, self.VERSION_TRUNCATION) 201 | 202 | def __str__(self): 203 | return self.name() 204 | 205 | def __repr__(self): 206 | return '%s(%s, %s, %s)' % ( 207 | self.__class__.__name__, 208 | self.user_agent, 209 | self.ua_hash, 210 | self.ua_spaceless, 211 | ) 212 | 213 | 214 | __all__ = [ 215 | 'Parser', 216 | ] 217 | -------------------------------------------------------------------------------- /device_detector/regexes/__init__.py: -------------------------------------------------------------------------------- 1 | # __init__.py file required so we can install package_data 2 | # and load .yml files from the .egg -------------------------------------------------------------------------------- /device_detector/regexes/local/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinkwelltwd/device_detector/7df1d95d4c6342499e7d04bcb4e08a2948fde0b1/device_detector/regexes/local/__init__.py -------------------------------------------------------------------------------- /device_detector/regexes/local/client/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinkwelltwd/device_detector/7df1d95d4c6342499e7d04bcb4e08a2948fde0b1/device_detector/regexes/local/client/__init__.py -------------------------------------------------------------------------------- /device_detector/regexes/local/client/antivirus.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # Supplemental regexes for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2018 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | - regex: McAfee \w+ Transmitter 9 | name: McAfee 10 | version: 11 | type: av 12 | 13 | - regex: ^cis_([\d\.]+)_ 14 | name: Comodo Internet Security 15 | version: '$1' 16 | type: av 17 | 18 | - regex: AV/,,ffl$ 19 | name: Avast 20 | version: '' 21 | type: av 22 | 23 | - regex: Avast!? (Antivirus|curl|SimpleHttp|NCC|Emergency|TUNEUP) 24 | name: Avast 25 | version: 26 | type: av 27 | 28 | - regex: AV/(\d+[\.\d]+) 29 | name: Avast 30 | version: '$1' 31 | type: av 32 | 33 | - regex: CCleaner, ?(\d+[\.\d]+) 34 | name: CCleaner 35 | version: '$1' 36 | type: av 37 | 38 | - regex: panda ?security ?nano 39 | name: Panda Security Nano 40 | version: '' 41 | type: av 42 | 43 | - regex: sophos autoupdate 44 | name: Sophos Autoupdate 45 | version: '' 46 | type: av 47 | -------------------------------------------------------------------------------- /device_detector/regexes/local/client/browsers.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # Supplemental regexes for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2018 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | # Avastium Browser - Avast browser based on Chrome 9 | - regex: 'Avastium Chrome(?:\/(\d+[\.\d]+))' 10 | name: 'Avast SafeZone' 11 | version: '$1' 12 | engine: 13 | default: 'WebKit' 14 | 15 | - regex: 'avastium[ \/]\(?(\d+[\.\d]+)' 16 | name: 'Avast SafeZone' 17 | version: '$1' 18 | engine: 19 | default: 'WebKit' 20 | -------------------------------------------------------------------------------- /device_detector/regexes/local/client/desktop_apps.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # Supplemental regexes for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2018 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | # General Desktop Apps 8 | 9 | - regex: aioremote_[a-z]*\/([\d\.]+) 10 | name: AiO Remote 11 | version: '$1' 12 | type: desktop app 13 | 14 | - regex: akamai netsession interface.+;([\d\.]+) 15 | name: Akamai NetSession Interface 16 | version: '$1' 17 | type: desktop app 18 | 19 | 20 | - regex: Apple Configurator [\w\d]/([\d\.]+) 21 | name: Apple Configurator 22 | version: '$1' 23 | type: desktop app 24 | 25 | - regex: com\.apple\.configurator\.xpc\.[a-z]+\/([\d\.]+) 26 | name: Apple Configurator 27 | version: '$1' 28 | type: desktop app 29 | 30 | - regex: ^Drive/(\d+[\.\d]+) 31 | name: Drive 32 | version: '$1' 33 | 34 | - regex: lastpass(ext)?\/([\d\.]+) 35 | name: LastPass 36 | version: '$2' 37 | type: desktop app 38 | 39 | - regex: mac[ +]os[ +]x\/.+\) addressbook\/([\d\.]+) 40 | name: Mac OS X Address Book 41 | version: '$1' 42 | type: desktop app 43 | 44 | - regex: mac[ +]os[ +]x\/.+\) calendaragent\/([\d\.]+) 45 | name: Mac OS X Calendar Agent 46 | version: '$1' 47 | type: desktop app 48 | 49 | - regex: nikon message center (\d)\/([\d\.]+) 50 | name: Nikon Message Center $1 51 | version: '$2' 52 | type: desktop app 53 | 54 | - regex: nvidia (gfe|update) v([\d\.]+) 55 | name: NVIDIA 56 | version: '$2' 57 | type: desktop app 58 | 59 | - regex: pinnaclenoti[tf]icationservice\/([\d\.]+) 60 | name: Pinnacle Notification Service 61 | version: '$1' 62 | type: desktop app 63 | 64 | - regex: panda devices agent *([\d\.]+) 65 | name: Panda Devices Agent 66 | version: '$1' 67 | type: desktop app 68 | 69 | - regex: pandasoftware\.informacionproactiva\.([\d\.]+) 70 | name: Panda Software 71 | version: '$1' 72 | type: desktop app 73 | 74 | - regex: bingmap(s\.3dcontrol)?\/([\d\.]+) 75 | name: Bing Maps 76 | version: '$2' 77 | type: desktop app 78 | 79 | - regex: bingfinance.+\/([\d\.]+) 80 | name: Bing Finance 81 | version: '$1' 82 | type: desktop app 83 | 84 | - regex: binghealthandfitness.+\/([\d\.]+) 85 | name: Bing Health and Fitness 86 | version: '$1' 87 | type: desktop app 88 | 89 | - regex: bingnews.+\/([\d\.]+) 90 | name: Bing News 91 | version: '$1' 92 | type: desktop app 93 | 94 | - regex: bingsports.+\/([\d\.]+) 95 | name: Bing Sports 96 | version: '$1' 97 | type: desktop app 98 | 99 | - regex: bingweather.+\/([\d\.]+) 100 | name: Bing Weather 101 | version: '$1' 102 | type: desktop app 103 | 104 | - regex: (mbam.*) - base:([\d\.]+) 105 | name: '$1' 106 | version: '$2' 107 | type: desktop app 108 | 109 | - regex: ms-office 110 | name: Microsoft Office 111 | type: productivity 112 | version: 113 | 114 | - regex: Microsoft (?:Office )?Access(?:[\/ ](\d+[\.\d]+))? 115 | name: Microsoft Office Access 116 | version: '$1' 117 | type: productivity 118 | 119 | - regex: Microsoft (?:Office )?Excel(?:[\/ ](\d+[\.\d]+))? 120 | name: Microsoft Office Excel 121 | version: '$1' 122 | type: productivity 123 | 124 | - regex: Microsoft (?:Office )?Lync(?:[\/ ](\d+[\.\d]+))? 125 | name: Microsoft Office Lync 126 | version: '$1' 127 | type: productivity 128 | 129 | - regex: Microsoft (?:Office )?Powerpoint(?:[\/ ](\d+[\.\d]+))? 130 | name: Microsoft Office PowerPoint 131 | version: '$1' 132 | type: productivity 133 | 134 | - regex: Microsoft (?:Office )?Visio(?:[\/ ](\d+[\.\d]+))? 135 | name: Microsoft Office Visio 136 | version: '$1' 137 | type: productivity 138 | 139 | - regex: Microsoft (?:Office )?Word(?:[\/ ](\d+[\.\d]+))? 140 | name: Microsoft Office Word 141 | version: '$1' 142 | type: productivity 143 | 144 | - regex: word/(\d+[\.\d]+) 145 | name: Microsoft Office Word 146 | version: '$1' 147 | type: productivity 148 | 149 | - regex: Microsoft Office SyncProc(?:[\/ ](\d+[\.\d]+))? 150 | name: Microsoft Office SyncProc 151 | version: '$1' 152 | type: apputility 153 | 154 | - regex: Microsoft (?:Office )?Upload Center(?:[\/ ](\d+[\.\d]+))? 155 | name: Microsoft Upload Center 156 | version: '$1' 157 | type: apputility 158 | 159 | # Match last after other Office apps - Microsoft Office 2014 & Microsoft Office/2014 160 | - regex: Microsoft Office 161 | name: Microsoft Office 162 | version: '$1' 163 | type: productivity 164 | 165 | - regex: xblwin([\d\.]+) 166 | name: xBox Live Windows 167 | version: '$1' 168 | type: desktop app 169 | 170 | - regex: dell sonicwall anti-spam desktop([\d\.]+) 171 | name: Dell SonicWall 172 | version: '$1' 173 | type: desktop app 174 | 175 | - regex: sendamessageapi; ([\d\.]+) 176 | name: Send A Message API 177 | version: '$1' 178 | type: desktop app 179 | 180 | - regex: ^server-bag 181 | name: server-bag 182 | version: '' 183 | type: desktop app 184 | 185 | - regex: ^windows assistant 186 | name: Windows Assistant 187 | version: '' 188 | type: desktop app 189 | 190 | -------------------------------------------------------------------------------- /device_detector/regexes/local/client/games.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # Supplemental regexes for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2018 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | - regex: 'angry ?birds ?([\w\d]+)\/([\d\.]+)' 9 | name: 'Angry Birds $1' 10 | version: '$2' 11 | -------------------------------------------------------------------------------- /device_detector/regexes/local/client/libraries.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # Supplemental regexes for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2018 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | - regex: 'aws-sdk-([a-z]+)\/(\d+[\.\d]+)' 9 | name: 'Amazon Web Services SDK for $1' 10 | version: '$2' 11 | 12 | - regex: 'FB(Android|iOS)SDK\.(\d+[\.\d]+)' 13 | name: 'Facebook SDK for $1' 14 | version: '$2' 15 | -------------------------------------------------------------------------------- /device_detector/regexes/local/client/mediaplayers.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # Supplemental regexes for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2018 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | - regex: 'vox/([\d\.?]+)' 8 | name: 'VOX' 9 | version: '$1' 10 | -------------------------------------------------------------------------------- /device_detector/regexes/local/client/mobile_apps.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # Supplemental regexes for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2018 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | # AFNetworking generic 9 | - regex: '([^/]+)/(\d+(?:\.\d+)+) \((?:iPhone|iPad); iOS [0-9\.]+; Scale/[0-9\.]+\)' 10 | name: '$1' 11 | version: '$2' 12 | 13 | - regex: 'Pocket Casts(?:, (?:Android|iOS) v([\d\.]+))?' 14 | name: 'Pocket Casts' 15 | version: '$1' 16 | 17 | - regex: 'krogerco\/(\d+[\.\d]+)\/(iOS|Android)' 18 | name: 'Kroger for $2' 19 | version: '$1' 20 | 21 | - regex: 'Arcus-(iOS|Android)\/(\d+[\.\d]+)' 22 | name: 'Arcus $1' 23 | version: '$2' 24 | 25 | - regex: 'Vungle(Droid|Windows)\/(\d+[\.\d]+)' 26 | name: 'Vungle$1' 27 | version: '$2' 28 | 29 | - regex: 'camscanner(_ad_\d)?_lite[\/@]([\.\d]+)' 30 | name: 'CamScanner Lite' 31 | version: '$2' 32 | 33 | - regex: 'Helpshift-(Android|iOS)\/(\d+[\.\d]+)' 34 | name: 'Helpshift for $1' 35 | version: '$2' 36 | 37 | - regex: 'GroupMe-(Android|iOS)\/(\d+[\.\d]+)' 38 | name: 'GroupMe for $1' 39 | version: '$2' 40 | 41 | - regex: 'NOAA (?:Weather)? Radar\/(\d+[\.\d]+)' 42 | name: 'NOAA Radar' 43 | version: '$1' 44 | 45 | - regex: '1password ([\w\d]+)\/([\d\.]+)' 46 | name: '1Password $1' 47 | version: '$2' 48 | 49 | - regex: '\w+\.com\.wunderground\.storm' 50 | name: 'Storm by Weather Underground' 51 | version: '' 52 | 53 | - regex: '\w+\.com\.avast\.osx\.secureline\.[a-z]+[\/ ]([\d\.]+)' 54 | name: 'Avast SecureLine VPN' 55 | version: '$1' 56 | 57 | - regex: '\w+\.com\.aws\.weatherbug\.pro' 58 | name: 'Weatherbug Pro' 59 | version: '' 60 | 61 | - regex: '\w+\.me\.scan\.qrcodereader' 62 | name: 'QR Code Reader by Scan' 63 | version: '' 64 | 65 | - regex: '\w+\.com\.precisionplanting\.FieldView' 66 | name: 'Precision Planting Fieldview' 67 | version: '' 68 | 69 | - regex: 'bibleverses\.bibleverse\.bible\.biblia\.verse\.devotion\/([\d\.]+)' 70 | name: 'Daily Bread Bible App' 71 | version: '$1' 72 | 73 | - regex: '^bugfender-[a-z]+\/([\d\.]+)' 74 | name: 'Bugfender' 75 | version: '$1' 76 | 77 | - regex: 'atkins carb tracker.+\/([\d\.]+)' 78 | name: 'Atkins Carb Tracker' 79 | version: '$1' 80 | 81 | - regex: '(ddg_[a-z]+|duckduckgo)\/([\d\.]+) \(com\.duckduckgo\.mobile' 82 | name: 'DuckDuckGo App' 83 | version: '$2' 84 | 85 | - regex: '^encirca-[a-z]+\/([\d\.]+)' 86 | name: 'EnCirca' 87 | version: '$1' 88 | 89 | - regex: '^e-sword ([a-z]+\/)?([\d\.]+)' 90 | name: 'e-Sword' 91 | version: '$2' 92 | 93 | - regex: '^gbis\.gb[a-z]+' 94 | name: 'Gas Buddy' 95 | version: '' 96 | 97 | - regex: '^gboard\/([\d\.]+)' 98 | name: 'Gboard' 99 | version: '1.27.0.199361458' 100 | 101 | - regex: '^google\/([\d\.]+)' 102 | name: 'Google' 103 | version: '$1' 104 | 105 | - regex: '^isc\.ios\.app\/iseecars version_([\d\.\_]+)' 106 | name: 'iSeeCars' 107 | version: '$1' 108 | 109 | - regex: '(\w+)\.com\.zillow\.zillowmap' 110 | name: 'Zillow' 111 | version: '$1' 112 | 113 | - regex: 'kayakmomondo[a-z]+([\d\.]+)' 114 | name: 'Kayak' 115 | version: '$1' 116 | 117 | - regex: 'kovacnica ([\d\.]+) com\.horseslivewallpaperhdhq' 118 | name: 'Horses Live Wallpaper' 119 | version: '$1' 120 | 121 | - regex: '(\w+)\.com\.telventdtn.agriculture' 122 | name: 'River Valley Offer Mgt' 123 | version: '$1' 124 | 125 | - regex: 'lnp \| lancasteronline\/([\d\.]+)' 126 | name: 'Lancaster Online' 127 | version: '$1' 128 | 129 | - regex: '^logos(\-imessageextension)?\/([\d\.]+)' 130 | name: 'Logos' 131 | version: '$2' 132 | 133 | - regex: 'mahjongepic[a-z]+lite\/([\d\.]+)' 134 | name: 'Mahjonge Pic Lite' 135 | version: '$1' 136 | 137 | - regex: '(\w+)\.com\.orgmentis\.buyandsell' 138 | name: 'Buy and Sell' 139 | version: '$1' 140 | 141 | - regex: '([\d\. ]+)\(com\.vilcsak\.bitcoin2\.price-widget' 142 | name: 'Bitcoin Price Widget' 143 | version: '$1' 144 | 145 | - regex: '(\w+)\.com\.dji\.pilot' 146 | name: 'DJI Pilot' 147 | version: '$1' 148 | 149 | - regex: 'abbyy\.lingvo\.([\d\.]+)' 150 | name: 'Abbyy Lingvo' 151 | version: '$1' 152 | 153 | - regex: '^acrobat[a-z ]*\/([\d\. ]+)' 154 | name: 'Acrobat' 155 | version: '$1' 156 | 157 | - regex: 'printershare v\.([\d\.]+)' 158 | name: 'Printer Share' 159 | version: '$1' 160 | 161 | - regex: 'adobe_ial_client(_mac_|_win_)([\d\.]+)' 162 | name: 'Adobe IAL Client' 163 | version: '$2' 164 | 165 | - regex: 'reincubate analytics client v([\d\.]+)' 166 | name: 'Reincubate Analytics Client' 167 | version: '$1' 168 | 169 | - regex: '([\d\.]+) \(com\.rockysandstudio\.app-for-whatsapp' 170 | name: 'Rocky Sand Studio' 171 | version: '$1' 172 | 173 | - regex: '^roadtrippers[a-z ]+([\d\.]+)' 174 | name: 'Road Trippers' 175 | version: '$1' 176 | 177 | - regex: 'searchprotect;([\d\.]+)' 178 | name: 'Search Protect' 179 | version: '$1' 180 | 181 | - regex: 'braintree\/[a-z]+/([\d\.]+)' 182 | name: 'Braintree' 183 | version: '$1' 184 | 185 | - regex: 'signal(r\.client\.winuap)?' 186 | name: 'Signal' 187 | version: '$2' 188 | 189 | - regex: 'talkable[ a-z]+v([\d\.]+)' 190 | name: 'Talkable' 191 | version: '$1' 192 | 193 | - regex: 'talkingtomfree[a-z]+' 194 | name: 'Talking Tom Free' 195 | version: '$1' 196 | 197 | - regex: 'venezia[a-z]+\/release-([\d\.]+)' 198 | name: 'Venezia' 199 | version: '$1' 200 | 201 | - regex: '(\w+)\.com\.weather\.twc' 202 | name: 'The Weather Channel' 203 | version: '$1' 204 | 205 | - regex: 'amap_location_sdk_[a-z]+' 206 | name: 'AMAP Location SDK' 207 | version: '$1' 208 | 209 | - regex: 'amap_sdk_[a-z]+_search_([\d\.]+)' 210 | name: 'AMAP SDK Search' 211 | version: '$1' 212 | 213 | - regex: 'aurora hdr ([\d\.]+)\/([\d\.]+)' 214 | name: 'Aurora HDR $1' 215 | version: '$2' 216 | 217 | - regex: '^avgsetup.+ver=([\d\.]+)' 218 | name: 'AVG Setup' 219 | version: '$1' 220 | 221 | - regex: '^backupassistantdaemon' 222 | name: 'Backup Assistant Daemon' 223 | version: '' 224 | 225 | - regex: '^crashlytics[ a-z]+\/([\d\.]+)' 226 | name: 'Crashlytics' 227 | version: '$1' 228 | 229 | - regex: 'weatherapptodaywidget_weatherfoundation' 230 | name: 'Weather App Today Widget' 231 | version: '' 232 | 233 | - regex: 'wells_fargo_mobile_banking_[a-z]+\/([\d\.]+)' 234 | name: 'Wells Fargo Mobile Banking' 235 | version: '$1' 236 | -------------------------------------------------------------------------------- /device_detector/regexes/local/client/osutility.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # Supplemental regexes for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2018 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | - regex: '^(.*) \(unknown version\) CFNetwork' 8 | name: '$1' 9 | version: '' 10 | type: osutility 11 | 12 | - regex: 'HP-Inkjet-WebUpdate-(\d+[\.\d]+)-cl' 13 | name: 'HP-Inkjet-WebUpdate' 14 | version: '$1' 15 | type: osutility 16 | -------------------------------------------------------------------------------- /device_detector/regexes/local/client/p2p.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # Supplemental regexes for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2018 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | - regex: Solid Core/([\d\.]+) 9 | name: SolidCore P2P 10 | version: '$1' 11 | -------------------------------------------------------------------------------- /device_detector/regexes/local/client/pim.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # Supplemental regexes for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2018 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | - regex: 'Microsoft (?:Office )?OneNote(?:[\/ ](\d+[\.\d]+))?' 9 | name: 'Microsoft OneNote' 10 | version: '$1' 11 | 12 | - regex: '(?:G-)?eM Client\/GOAuth2RequestFactory-CS-Version=(\d+[\.\d]+)' 13 | name: 'eM Client' 14 | version: '$1' 15 | -------------------------------------------------------------------------------- /device_detector/regexes/local/client/vpnproxy.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # Supplemental regexes for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2018 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | - regex: '^Tor[\w\d]+/([\d\.]+)' 9 | name: 'The Onion Router (TOR)' 10 | version: '$1' 11 | -------------------------------------------------------------------------------- /device_detector/regexes/local/device/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinkwelltwd/device_detector/7df1d95d4c6342499e7d04bcb4e08a2948fde0b1/device_detector/regexes/local/device/__init__.py -------------------------------------------------------------------------------- /device_detector/regexes/local/device/normalize.yml: -------------------------------------------------------------------------------- 1 | # @0ji-uFfHoaGvxwqY8DTh 2 | # @0NFXk9WqajoaFy3D1PKB 3 | - regex: ^@([\d\w\-\|%\=]{20,})$ 4 | groups: Gibberish 5 | # %%92d381458c2b9fe3d436c7e9e21ce03cf0873c99aad261fc71e3af1159a42ea%%% 6 | - regex: ^^%([\d\w\-\|%]{20,})%$ 7 | groups: Gibberish 8 | # *BkhBAAQAqpcr-X2cXpK9OlSjOYP1fppAz_54DhGqKw0ULQiEVp0AAYmAAAAAAABA 9 | - regex: ^\*([\d\w]{12}-[\d\w]{12,}) 10 | groups: Gibberish 11 | # NGC/22.14.0.54 MID/{00000000-0000-0000-0000-000000000000} SID/0UYYWwAAAAA LUE/1.15.1.10 (Windows;10.0;SP0.0;X64;ENU) 12 | # Symantec/3.00.10.2737 MID/{00000000-0000-0000-0000-000000000000} SID/zIRxWwAAAAA LUE/1.12.5.5 (Windows;6.2;SP0.0;X64;ENC) 13 | - regex: '([\w\d]+/[\d+\.]+) (?:MID\/.* SID\/.*) (\(Windows.*)' 14 | groups: '\g<1> \g<2>' 15 | 16 | # N360/22.9.3.13 MID/{xCX4MyatyOnekxRFqX39bDtUkg4} SID/7jiIWwAAAAA SEQ/180829061 17 | # N360/22.9.3.13 MID/{xCX4MyatyOnekxRFqX39bDtUkg4} SID/7jiIWwAAAAA 18 | # NS/22.10.0.85 MID/{00000000-0000-0000-0000-000000000000} SID/Aoh4WwAAAAA SEQ/180818310 19 | - regex: '([\w\d]+/[\d+\.]+)( MID\/.+ SID\/.+)' 20 | groups: '\g<1>' 21 | 22 | - regex: '(@AUVI@[\d+\.]+;\w+-\w+/[\d+\.]+) (\(\w+; \w+; \w+; (?:\w+ [\d+\.]+)?; (?:\w+ [\d+\.]+)?; )([\w ]+); ; ([\w ]+)(?:.*)' 23 | groups: '\g<1> (\g<3>; \g<4>)' 24 | 25 | - regex: '((:?EIS|EAV) Update \(\w+; \w+; \w+)(?:.*)' 26 | groups: '\g<1>)' 27 | 28 | - regex: '(AVG(?:INET16|SETUP|FMW)-WV?\d+H?X+\d+)(?:.*)' 29 | groups: '\g<1>' 30 | 31 | # ACC_2.00.3019_10DCBCD54296E5C55D3 32 | # ALU_1.02.3502_1C882B536E6F02E3FCE 33 | - regex: '([A-Z]+_[\d+\.]+)_(?:.*)' 34 | groups: '\g<1>' 35 | 36 | # A/6.0.1/samsung/SAMSUNG-SM-G930A/msm8996/unknown/QCX3/s182727395/35550207/4204296470/310|410+310|410/samsung/495892/-/- 37 | # A/5.1.1/kyocera/KYOCERA-E6560/E6560/AT&T 38 | # A/8.0.0/Sony/H3123/sdm660/unknown/QCX3/l15563066056580133316/35276709/1722758893/310|410 310|410/Sony/5612/5597/-/2.5/1/M 39 | # A/7.0/samsung/SM-G935V 40 | - regex: '^(A\/[\d\.]+\/\w+\/[\w\d\-\#\(\)]+)(?:\/(?:.*)|$)' 41 | groups: '\g<1>' 42 | 43 | # 1.0,win/6.3.9600,AV/18.4.2338,avl/push/18.4.3895.325,ffl 44 | - regex: '(\d\.\d,win\/[\d+\.]+,AV)(\/[\d+\.]+)(,avl\/[\w]+)\/.*' 45 | groups: '\g<1>\g<3>' 46 | 47 | # UAs starting with OTA or FC3 and containing no spaces or punctuation 48 | # 30001924uLE8rxqZkFP1XQy5RqGlRVly 49 | # OTA100503107AC282A850DB733C 50 | - regex: '^(FC3|OTA)[a-zA-Z0-9]*$' 51 | groups: Gibberish 52 | 53 | # 471306wp2FvTHfnLdwJIY25Ipa9XszPj 54 | - regex: '(^\d+)[a-zA-Z0-9]{20,}' 55 | groups: Gibberish 56 | 57 | # Android 5.1.1 58 | # Android Client 59 | - regex: '^Android[ -\.]?([\d\.]+|app|client|sdk|os)$' 60 | groups: 'Generic Android' 61 | 62 | # ArcGIS.Android-10.2.8-1 63 | - regex: '^([\w\d ]+)[\. ]?Android[ -]?([\d\.\-]+|client|sdk)$' 64 | groups: '\g<1> for Android' 65 | 66 | # TalentLMS Android - 3.2.1 67 | # TalentScout - 45 68 | - regex: '([\w\d ]+) - [\d\.]+$' 69 | groups: '\g<1>' 70 | 71 | # build 201, Android 72 | - regex: '^build \d+,? Android$' 73 | groups: 'Generic Android' 74 | 75 | # 15.5.41 CFNetwork 76 | # 15 iOS 77 | - regex: '^[\d\.,]+ (AppleWebKit|Darwin|CFNetwork|iOS|Dalvik)$' 78 | groups: 'Generic \g<1>' 79 | 80 | # ios/274 CFNetwork/1121.2.2 Darwin 81 | - regex: '^(ios|iphone|mobile|client|build)/[\d\.]+ CFNetwork/[\d\.]+ Darwin$' 82 | groups: 'Generic iOS' 83 | 84 | # 1.146.1.2 - LIVE - Mar 13 2019 85 | - regex: '^[\d\.]+ - (LIVE).*' 86 | groups: '\g<1>' 87 | 88 | # 1.172.6_ios_LIVE 89 | - regex: '^[\d\.]+_[\w]+_(LIVE)$' 90 | groups: '\g<1>' 91 | 92 | # 2001 AutodeskClient 93 | # 3.3.5-moat 94 | - regex: '^[\d\.]+[ -]?(AutodeskClient|moat)$' 95 | groups: '\g<1>' 96 | 97 | # 1010/42 CFNetwork/978.0.7 Darwin 98 | - regex: '^[\d\/\.]+ (CFNetwork).*(Darwin)$' 99 | groups: '\g<1> \g<2>' 100 | 101 | # 500px/852 CFNetwork/1121.2.2 Darwin 102 | # Blink/8019 CFNetwork/1125.2 Darwin 103 | # BS2012/1.0.11 CFNetwork/808.2.16 Darwin 104 | # Chrome/71.0.3578.89 CFNetwork/811.5.4 Darwin 105 | - regex: '^([\w\d \.]+)/[\d\.]+ CFNetwork/[\d\.]+ Darwin$' 106 | groups: '\g<1> for iOS/macOS' 107 | -------------------------------------------------------------------------------- /device_detector/regexes/local/osfragments.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # @link http://piwik.org 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | iOS: 9 | - '\biPhone' 10 | - 'iPhone\b' 11 | - '\biPad' 12 | - 'iPad\b' 13 | - '\bios' 14 | - 'ios\b' 15 | 16 | Android: 17 | - '\bAndroid' 18 | - 'Android\b' 19 | - 'VungleDroid' 20 | 21 | Mac: 22 | - 'com.apple' 23 | - '_MAC_' 24 | - '\bMAC\b' 25 | 26 | Windows: 27 | - 'Windows' 28 | - 'WinInet' 29 | - 'MSFT-WIN' 30 | - 'XBLWIN10' 31 | -------------------------------------------------------------------------------- /device_detector/regexes/local/oss.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # @link http://piwik.org 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | - regex: 'Win10PC' 9 | name: 'Windows' 10 | version: '10' 11 | -------------------------------------------------------------------------------- /device_detector/regexes/upstream/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinkwelltwd/device_detector/7df1d95d4c6342499e7d04bcb4e08a2948fde0b1/device_detector/regexes/upstream/__init__.py -------------------------------------------------------------------------------- /device_detector/regexes/upstream/client/__init__.py: -------------------------------------------------------------------------------- 1 | # __init__.py file required so we can install package_data 2 | # and load .yml files from the .egg -------------------------------------------------------------------------------- /device_detector/regexes/upstream/client/browser_engine.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # @link https://matomo.org 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | - regex: 'NetFront' 9 | name: 'NetFront' 10 | 11 | - regex: 'Edge' 12 | name: 'Edge' 13 | 14 | - regex: 'Trident' 15 | name: 'Trident' 16 | 17 | - regex: '(? 100 | client: 101 | name: Apple Newdeviceoutreach 102 | version: '' 103 | - 104 | user_agent: com.applicatsia.iroofing/3.4.7 GMS-SDK/2.5.30219.0 iPad/12.1.1 hw/iPad5_4 (gzip) 105 | client: 106 | name: Applicatsia Iroofing 107 | version: 3.4.7 108 | - 109 | user_agent: com.batch.ios/1.11.0 com.gemsoftware.textme/3.12.8 (iPad6.12;iOS 12.1.4) 110 | client: 111 | name: Gemsoftware Textme 112 | version: 3.12.8 113 | - 114 | user_agent: com.foursquare.pilgrimsdk.ios:1.2.1:20180226:iOS 11.4.1:iPhone8,1:release 115 | client: 116 | name: Foursquare Pilgrimsdk Ios 117 | version: 1.2.1 118 | - 119 | user_agent: com.google.b612/9.2.42 iPhone/12.1 hw/iPhone8_1 120 | client: 121 | name: Google B612 122 | version: 9.2.42 123 | - 124 | user_agent: com.google.Gmail/5.0.180715 iPhone/11.4.1 hw/iPhone9_2 125 | client: 126 | name: Gmail 127 | version: 5.0.180715 128 | - 129 | user_agent: com.google.GoogleMobile.TrendsTodayExtension/72.0.0 iPhone/12.2 hw/iPhone11_2 130 | client: 131 | name: GoogleMobile 132 | version: 72.0.0 133 | - 134 | user_agent: com.google.calendar.TodayExtension/2.90.0 iSL/3.3 iPhone/12.2 hw/iPhone8_4 (gzip) 135 | client: 136 | name: Google Calendar 137 | version: 2.90.0 138 | - 139 | user_agent: YWeather (iPhone/11.4.1; iOS; en_US;) 140 | client: 141 | name: YWeather 142 | version: '' 143 | - 144 | user_agent: Akamai NetSession C-API (win;AdDLMgr;capi_1.9.2;Vista) 145 | client: 146 | name: Akamai NetSession C-API 147 | version: '' 148 | - 149 | user_agent: 1.172.0.1 - LIVE - Mar 5 2020 150 | client: 151 | name: LIVE 152 | version: 1.172.0.1 153 | - 154 | user_agent: 17build 113411 LIVE Sep 17 20180 155 | client: 156 | name: LIVE 157 | version: 113411 158 | - 159 | user_agent: 11.3.27 Boxcar 160 | client: 161 | name: Boxcar 162 | version: 11.3.27 163 | - 164 | user_agent: 1.3.7 CoalCar 165 | client: 166 | name: CoalCar 167 | version: 1.3.7 168 | - 169 | user_agent: 12.31.79/Caboose 170 | client: 171 | name: Caboose 172 | version: 12.31.79 173 | # -------------------------------------------------------------------------------- 174 | # UA Strings with no interesting pairs should fall back to browser details 175 | - 176 | user_agent: Mozilla/5.0 (iPhone; CPU iPhone OS 12_1_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/16C101 (5802868304) 177 | client: 178 | name: Mobile Safari 179 | version: '' 180 | - 181 | user_agent: Mozilla/5.0 (iPhone; CPU iPhone OS 11_2_1 like Mac OS X) AppleWebKit/604.4.7 (KHTML, like Gecko) Mobile/15C153 WFAppId/650647 WFexperience/phone WFVersion/3.125.4_PROD 182 | client: 183 | name: Mobile Safari 184 | version: '' 185 | - 186 | user_agent: Mozilla/5.0 (Linux; U; Android 4.1.1; fr-be; MPQC784 IPS Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30 187 | client: 188 | name: Android Browser 189 | version: '' 190 | # -------------------------------------------------------------------------------- 191 | -------------------------------------------------------------------------------- /device_detector/tests/fixtures/upstream/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinkwelltwd/device_detector/7df1d95d4c6342499e7d04bcb4e08a2948fde0b1/device_detector/tests/fixtures/upstream/__init__.py -------------------------------------------------------------------------------- /device_detector/tests/fixtures/upstream/camera.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - 3 | user_agent: Mozilla/5.0 (Linux; U; Android 2.3.3; ja-jp; COOLPIX S800c Build/CP01_WW) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1 4 | os: 5 | name: Android 6 | version: "2.3.3" 7 | platform: "" 8 | client: 9 | type: browser 10 | name: Android Browser 11 | version: "" 12 | engine: WebKit 13 | engine_version: "533.1" 14 | device: 15 | type: camera 16 | brand: Nikon 17 | model: Coolpix S800c 18 | os_family: Android 19 | browser_family: Android Browser 20 | - 21 | user_agent: Mozilla/5.0 (Linux; Android 5.0.2; DMC-CM1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.92 Mobile Safari/537.36 22 | os: 23 | name: Android 24 | version: "5.0.2" 25 | platform: "" 26 | client: 27 | type: browser 28 | name: Chrome Mobile 29 | version: "77.0.3865.92" 30 | engine: Blink 31 | engine_version: "" 32 | device: 33 | type: camera 34 | brand: Panasonic 35 | model: Lumix DMC-CM1 36 | os_family: Android 37 | browser_family: Chrome 38 | - 39 | user_agent: Mozilla/5.0 (Linux; U; Android 4.0; de-DE; EK-GC100 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30 40 | os: 41 | name: Android 42 | version: "4.0" 43 | platform: "" 44 | client: 45 | type: browser 46 | name: Android Browser 47 | version: "" 48 | engine: WebKit 49 | engine_version: "534.30" 50 | device: 51 | type: camera 52 | brand: Samsung 53 | model: Galaxy Camera 54 | os_family: Android 55 | browser_family: Android Browser 56 | - 57 | user_agent: Mozilla/5.0 (Linux; Android 4.1.2; EK-GC100 Build/JZO54K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.63 Mobile Safari/537.36 OPR/15.0.1162.60140 58 | os: 59 | name: Android 60 | version: "4.1.2" 61 | platform: "" 62 | client: 63 | type: browser 64 | name: Opera Mobile 65 | version: "15.0.1162.60140" 66 | engine: Blink 67 | engine_version: "" 68 | device: 69 | type: camera 70 | brand: Samsung 71 | model: Galaxy Camera 72 | os_family: Android 73 | browser_family: Opera 74 | - 75 | user_agent: Mozilla/5.0 (Linux; Android 4.3; EK-GC200 Build/JSS15J) AppleWebKit/537.36 (KHTML like Gecko) Chrome/35.0.1916.141 Mobile Safari/537.36 76 | os: 77 | name: Android 78 | version: "4.3" 79 | platform: "" 80 | client: 81 | type: browser 82 | name: Chrome Mobile 83 | version: "35.0.1916.141" 84 | engine: Blink 85 | engine_version: "" 86 | device: 87 | type: camera 88 | brand: Samsung 89 | model: Galaxy Camera 2 90 | os_family: Android 91 | browser_family: Chrome 92 | - 93 | user_agent: Mozilla/5.0 (Linux; Android 4.1.2; EK-GC110 Build/JZO54K) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19 94 | os: 95 | name: Android 96 | version: "4.1.2" 97 | platform: "" 98 | client: 99 | type: browser 100 | name: Chrome Mobile 101 | version: "18.0.1025.166" 102 | engine: WebKit 103 | engine_version: "535.19" 104 | device: 105 | type: camera 106 | brand: Samsung 107 | model: Galaxy Camera WiFi only 108 | os_family: Android 109 | browser_family: Chrome 110 | - 111 | user_agent: Mozilla/5.0 (Linux; Android 4.2.2; en-nz; SAMSUNG EK-GN120 Build/JDQ39) AppleWebKit/535.19 (KHTML, like Gecko) Version/1.0 Chrome/18.0.1025.308 Mobile Safari/535.19 112 | os: 113 | name: Android 114 | version: "4.2.2" 115 | platform: "" 116 | client: 117 | type: browser 118 | name: Chrome Webview 119 | version: "18.0.1025.308" 120 | engine: WebKit 121 | engine_version: "535.19" 122 | device: 123 | type: camera 124 | brand: Samsung 125 | model: Galaxy NX 126 | os_family: Android 127 | browser_family: Chrome 128 | -------------------------------------------------------------------------------- /device_detector/tests/fixtures/upstream/smart_display.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - 3 | user_agent: Mozilla/5.0 (Linux; U; Android 4.0.4; fr-be; DA220HQL Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30 4 | os: 5 | name: Android 6 | version: "4.0.4" 7 | platform: "" 8 | client: 9 | type: browser 10 | name: Android Browser 11 | version: "" 12 | engine: WebKit 13 | engine_version: "534.30" 14 | device: 15 | type: smart display 16 | brand: Acer 17 | model: DA220HQL 18 | os_family: Android 19 | browser_family: Android Browser 20 | - 21 | user_agent: Mozilla/5.0 (Linux; Android 4.2.1; DA241HL Build/JOP40D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.114 Safari/537.36 22 | os: 23 | name: Android 24 | version: "4.2.1" 25 | platform: "" 26 | client: 27 | type: browser 28 | name: Chrome 29 | version: "34.0.1847.114" 30 | engine: Blink 31 | engine_version: "" 32 | device: 33 | type: smart display 34 | brand: Acer 35 | model: DA241HL 36 | os_family: Android 37 | browser_family: Chrome 38 | - 39 | user_agent: Mozilla/5.0 (Linux; U; Android 4.0.4; de-de; VSD220 Build/IMM76D.UI23ED12_VSC) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30 40 | os: 41 | name: Android 42 | version: "4.0.4" 43 | platform: "" 44 | client: 45 | type: browser 46 | name: Android Browser 47 | version: "" 48 | engine: WebKit 49 | engine_version: "534.30" 50 | device: 51 | type: smart display 52 | brand: ViewSonic 53 | model: VSD220 54 | os_family: Android 55 | browser_family: Android Browser 56 | - 57 | user_agent: Mozilla/5.0 (Linux; Android 8.0.0; IFP7550-3 Build/OPR5.170623.014) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/61.0.3163.98 Safari/537.36 58 | os: 59 | name: Android 60 | version: 8.0.0 61 | platform: "" 62 | client: 63 | type: browser 64 | name: Chrome Webview 65 | version: 61.0.3163.98 66 | engine: Blink 67 | engine_version: "" 68 | device: 69 | type: smart display 70 | brand: ViewSonic 71 | model: ViewBoard IFP7550-3 75" 4K 72 | os_family: Android 73 | browser_family: Chrome 74 | - 75 | user_agent: Mozilla/5.0 (Linux; Android 8.0.0; IFP9850-3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3739.0 Safari/537.36 76 | os: 77 | name: Android 78 | version: 8.0.0 79 | platform: "" 80 | client: 81 | type: browser 82 | name: Chrome 83 | version: 75.0.3739.0 84 | engine: Blink 85 | engine_version: "" 86 | device: 87 | type: smart display 88 | brand: ViewSonic 89 | model: ViewBoard IFP9850-3 98" 4K 90 | os_family: Android 91 | browser_family: Chrome 92 | - 93 | user_agent: Mozilla/5.0 (Linux; Android 8.0.0; IFP8650-3 Build/OPR5.170623.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/61.0.3163.98 Safari/537.36 94 | os: 95 | name: Android 96 | version: 8.0.0 97 | platform: "" 98 | client: 99 | type: browser 100 | name: Chrome Webview 101 | version: 61.0.3163.98 102 | engine: Blink 103 | engine_version: "" 104 | device: 105 | type: smart display 106 | brand: ViewSonic 107 | model: ViewBoard IFP8650-3 86" 4K 108 | os_family: Android 109 | browser_family: Chrome 110 | - 111 | user_agent: Mozilla/5.0 (Linux; Android 8.0.0; IFP6550-3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.85 Safari/537.36 112 | os: 113 | name: Android 114 | version: 8.0.0 115 | platform: "" 116 | client: 117 | type: browser 118 | name: Chrome 119 | version: 76.0.3809.85 120 | engine: Blink 121 | engine_version: "" 122 | device: 123 | type: smart display 124 | brand: ViewSonic 125 | model: ViewBoard IFP6550-3 65" 4K 126 | os_family: Android 127 | browser_family: Chrome 128 | - 129 | user_agent: Mozilla/5.0 (Linux; Android 8.0.0; IFP7550-3 Build/OPR5.170623.014) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/61.0.3163.98 Safari/537.36 130 | os: 131 | name: Android 132 | version: 8.0.0 133 | platform: "" 134 | client: 135 | type: browser 136 | name: Chrome Webview 137 | version: 61.0.3163.98 138 | engine: Blink 139 | engine_version: "" 140 | device: 141 | type: smart display 142 | brand: ViewSonic 143 | model: ViewBoard IFP7550-3 75" 4K 144 | os_family: Android 145 | browser_family: Chrome 146 | - 147 | user_agent: Mozilla/5.0 (Linux; Android 8.0.0; IFP9850-3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3739.0 Safari/537.36 148 | os: 149 | name: Android 150 | version: 8.0.0 151 | platform: "" 152 | client: 153 | type: browser 154 | name: Chrome 155 | version: 75.0.3739.0 156 | engine: Blink 157 | engine_version: "" 158 | device: 159 | type: smart display 160 | brand: ViewSonic 161 | model: ViewBoard IFP9850-3 98" 4K 162 | os_family: Android 163 | browser_family: Chrome 164 | - 165 | user_agent: Mozilla/5.0 (Linux; Android 8.0.0; IFP8650-3 Build/OPR5.170623.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/61.0.3163.98 Safari/537.36 166 | os: 167 | name: Android 168 | version: 8.0.0 169 | platform: "" 170 | client: 171 | type: browser 172 | name: Chrome Webview 173 | version: 61.0.3163.98 174 | engine: Blink 175 | engine_version: "" 176 | device: 177 | type: smart display 178 | brand: ViewSonic 179 | model: ViewBoard IFP8650-3 86" 4K 180 | os_family: Android 181 | browser_family: Chrome 182 | - 183 | user_agent: Mozilla/5.0 (Linux; Android 8.0.0; IFP6550-3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.85 Safari/537.36 184 | os: 185 | name: Android 186 | version: 8.0.0 187 | platform: "" 188 | client: 189 | type: browser 190 | name: Chrome 191 | version: 76.0.3809.85 192 | engine: Blink 193 | engine_version: "" 194 | device: 195 | type: smart display 196 | brand: ViewSonic 197 | model: ViewBoard IFP6550-3 65" 4K 198 | os_family: Android 199 | browser_family: Chrome 200 | - 201 | user_agent: Dalvik/1.6.0 (Linux; U; Android 4.4.4; WT22M-FI Build/KTU84Q) 202 | os: 203 | name: Android 204 | version: 4.4.4 205 | platform: "" 206 | client: 207 | type: browser 208 | name: Android Browser 209 | version: "" 210 | engine: WebKit 211 | engine_version: "" 212 | device: 213 | type: smart display 214 | brand: AOpen 215 | model: eTILE WT22M-FI 216 | os_family: Android 217 | browser_family: Android Browser 218 | - 219 | user_agent: Dalvik/1.6.0 (Linux; U; Android 4.3.1; WT19M-FI Build/JLS36I) 220 | os: 221 | name: Android 222 | version: 4.3.1 223 | platform: "" 224 | client: 225 | type: browser 226 | name: Android Browser 227 | version: "" 228 | engine: WebKit 229 | engine_version: "" 230 | device: 231 | type: smart display 232 | brand: AOpen 233 | model: eTILE WT19M-FI 234 | os_family: Android 235 | browser_family: Android Browser 236 | -------------------------------------------------------------------------------- /device_detector/tests/fixtures/upstream/smart_speaker.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - 3 | user_agent: AppleCoreMedia/1.0.0.15F80 (HomePod; U; CPU OS 11_4 like Mac OS X; fr_fr) 4 | os: 5 | name: iOS 6 | version: "11.4" 7 | platform: "" 8 | client: null 9 | device: 10 | type: smart speaker 11 | brand: Apple 12 | model: HomePod 13 | os_family: iOS 14 | browser_family: Unknown 15 | - 16 | user_agent: Dalvik/2.1.0 (Linux; U; Android 5.1.1; AEOBC Build/LVY48F) 17 | os: 18 | name: Android 19 | version: "5.1.1" 20 | platform: "" 21 | client: 22 | type: browser 23 | name: Android Browser 24 | version: "" 25 | engine: WebKit 26 | engine_version: "" 27 | device: 28 | type: smart speaker 29 | brand: Amazon 30 | model: Echo 31 | os_family: Android 32 | browser_family: Android Browser 33 | - 34 | user_agent: Mozilla/5.0 (Linux; Android 5.1.1; AEOKN Build/LVY48F; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/59.0.3071.125 Safari/537.36 35 | os: 36 | name: Android 37 | version: "5.1.1" 38 | platform: "" 39 | client: 40 | type: browser 41 | name: Chrome Webview 42 | version: "59.0.3071.125" 43 | engine: Blink 44 | engine_version: "" 45 | device: 46 | type: smart speaker 47 | brand: Amazon 48 | model: Echo 49 | os_family: Android 50 | browser_family: Chrome 51 | - 52 | user_agent: Echo/1.0(APNG) 53 | os: [ ] 54 | client: null 55 | device: 56 | type: smart speaker 57 | brand: Amazon 58 | model: Echo 59 | os_family: Unknown 60 | browser_family: Unknown 61 | - 62 | user_agent: Mozilla/5.0 (Linux; Android 7.1.2; AEOCH Build/NS6548; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.115 Mobile Safari/537.36 63 | os: 64 | name: Android 65 | version: 7.1.2 66 | platform: "" 67 | client: 68 | type: browser 69 | name: Chrome Webview 70 | version: 92.0.4515.115 71 | engine: Blink 72 | engine_version: "" 73 | device: 74 | type: smart speaker 75 | brand: Amazon 76 | model: Echo Show 8 77 | os_family: Android 78 | browser_family: Chrome 79 | - 80 | user_agent: Mozilla/5.0 (Linux; Android 7.1.2; AEOCW Build/NS6543; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/88.0.4324.152 Safari/537.36 81 | os: 82 | name: Android 83 | version: 7.1.2 84 | platform: "" 85 | client: 86 | type: browser 87 | name: Chrome Webview 88 | version: 88.0.4324.152 89 | engine: Blink 90 | engine_version: "" 91 | device: 92 | type: smart speaker 93 | brand: Amazon 94 | model: Echo Show 8 95 | os_family: Android 96 | browser_family: Chrome 97 | -------------------------------------------------------------------------------- /device_detector/tests/parser/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinkwelltwd/device_detector/7df1d95d4c6342499e7d04bcb4e08a2948fde0b1/device_detector/tests/parser/__init__.py -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/local/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinkwelltwd/device_detector/7df1d95d4c6342499e7d04bcb4e08a2948fde0b1/device_detector/tests/parser/fixtures/local/__init__.py -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/local/client/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinkwelltwd/device_detector/7df1d95d4c6342499e7d04bcb4e08a2948fde0b1/device_detector/tests/parser/fixtures/local/client/__init__.py -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/local/client/antivirus.yml: -------------------------------------------------------------------------------- 1 | - 2 | user_agent: 'clamav/0.99.4 (os: linux-gnu, arch: x86_64, cpu: x86_64)' 3 | client: 4 | type: av 5 | name: ClamAV 6 | version: "0.99.4" 7 | - 8 | user_agent: SophosUpdateManager/1.5.7.50 SDDS/2.0 (u= EO2ANA123G c= 6342da15-f351-4ab7-9656-3f5f2d50885d ) 9 | client: 10 | type: av 11 | name: Sophos 12 | version: "1.5.7.50" 13 | - 14 | user_agent: SophosAgent/1.0 (type= spa ) 15 | client: 16 | type: av 17 | name: Sophos 18 | version: "1.0" 19 | - 20 | user_agent: SophosUpdate/5.12.85 21 | client: 22 | type: av 23 | name: Sophos 24 | version: "5.12.85" 25 | - 26 | user_agent: 1.0,win/6.1.7601,AV/18.4.2338,avl/myavast/18.4.3895.325,ffl 27 | client: 28 | type: av 29 | name: Avast 30 | version: "18.4.2338" 31 | - 32 | user_agent: 1.0,win/10.0.16299,AV/18.5.2342,,ffl 33 | client: 34 | type: av 35 | name: Avast 36 | version: "18.5.2342" 37 | - 38 | user_agent: 1.0,win/10.0.16299,AV/17.1.2286,avl/devcontrol/17.1.3394.0,ffl 39 | client: 40 | type: av 41 | name: Avast 42 | version: "17.1.2286" 43 | - 44 | user_agent: avast! Antivirus (instup) 45 | client: 46 | type: av 47 | name: Avast 48 | version: "" 49 | - 50 | user_agent: Avast Emergency Update Agent 51 | client: 52 | type: av 53 | name: Avast 54 | version: "" 55 | - 56 | user_agent: Mozilla/4.0 (CCleaner, 5.21.5700) 57 | client: 58 | type: av 59 | name: CCleaner 60 | version: "5.21.5700" 61 | - 62 | user_agent: CCleaner Update Agent 63 | client: 64 | type: av 65 | name: CCleaner 66 | version: "" 67 | - 68 | user_agent: McAfee CA Transmitter 69 | client: 70 | type: av 71 | name: McAfee 72 | version: "" 73 | - 74 | user_agent: McAfee ga_rest transmitter_{90DD38E0-111E-47EA-8E0A-B46F9D6B50D4} 75 | client: 76 | type: av 77 | name: McAfee 78 | version: "" 79 | - 80 | user_agent: McAfee Agent 81 | client: 82 | type: av 83 | name: McAfee 84 | version: "" 85 | - 86 | user_agent: ZoneAlarm/14.1.48.0 (oem-1043; en-US) ZSP/2.2 87 | client: 88 | type: av 89 | name: ZoneAlarm 90 | version: "14.1.48.0" 91 | - 92 | user_agent: ngc/22.14.0.54 mid/{00000000-0000-0000-0000-000000000000} sid/h4yvwwaaaaa lue/1.15.1.10 (windows;10.0;sp0.0;x64;enu) 93 | client: 94 | type: av 95 | name: Norton Antivirus 96 | version: "22.14.0.54" 97 | - 98 | user_agent: antivirus emergency update agent 99 | client: 100 | type: av 101 | name: Antivirus Emergency Update Agent 102 | version: "" 103 | - 104 | user_agent: CIS_5.10.228257.2253_646E514C51BFF23DBBB5B9487F142670 105 | client: 106 | type: av 107 | name: Comodo Internet Security 108 | version: "5.10.228257.2253" 109 | - 110 | user_agent: Norton/1.0 111 | client: 112 | type: av 113 | name: Norton 114 | version: "1.0" 115 | - 116 | user_agent: PandaSecurityNano (PID=4151;CID=181305;V=18.01.00.0000;SPID=4252;SVTID=8;MUID=2315AD38-5C23-527F-6B88-6C25A83506D5) 117 | client: 118 | type: av 119 | name: Panda Security Nano 120 | version: "" 121 | - 122 | user_agent: sophos autoupdate/ cfnetwork/897.15 darwin/17.5.0 (x86_64) 123 | client: 124 | type: av 125 | name: Sophos Autoupdate 126 | version: "" 127 | - 128 | user_agent: Webroot NCSI 129 | client: 130 | type: av 131 | name: Webroot 132 | version: "" -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/local/client/browser.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - 3 | user_agent: Avastium Chrome/3.2987.1601(Win) (18.4.2338) 4 | client: 5 | type: browser 6 | name: Avast SafeZone 7 | short_name: "AZ" 8 | version: "3.2987.1601" 9 | - 10 | user_agent: mozilla/5.0 applewebkit/537.36 (khtml, like gecko) chrome/57.3.2987.1601 safari/537.36 avastium (17.8.2527) 11 | client: 12 | type: browser 13 | name: Avast SafeZone 14 | short_name: "AZ" 15 | version: "17.8.2527" 16 | - 17 | user_agent: mozilla/5.0 applewebkit/537.36 (khtml, like gecko) chrome/57.3.2987.1601 safari/537.36 avastium/17.8.2527 18 | client: 19 | type: browser 20 | name: Avast SafeZone 21 | short_name: "AZ" 22 | version: "17.8.2527" 23 | -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/local/client/dictua.yml: -------------------------------------------------------------------------------- 1 | - 2 | user_agent: '{"ac":"CCDesktop_app","av":"4.8.1.435"}' 3 | client: 4 | type: generic 5 | name: CCDesktop_app 6 | version: 4.8.1.435 7 | - 8 | user_agent: '{"locale":"en_US","appVersion":"5.2.5","os":"iOS","deviceName":"iPhone","deviceType":"phone","iOSApiLevel":"12.1.4","gzip":true,"appId":"KHAiOS","buildNumber":"31667"}' 9 | client: 10 | type: generic 11 | name: KHAiOS 12 | version: 5.2.5 13 | - 14 | user_agent: target=LetGo; appVersion=1.58.0; bundle=com.letgo.ios; build=524; os=iOS 9.2.1; device=Apple iPad4,2; httpLibrary=Alamofire/4.7.3 15 | client: 16 | type: generic 17 | name: LetGo 18 | version: 1.58.0 19 | - 20 | user_agent: AppName=iOSProApp;AppId=3;Platform=iOS;Model=iPad Pro 9.7-inch (Wi-Fi Cellular);OSVersion=12.0;Carrier=iPad;AppVersion=3.27.0.4 21 | client: 22 | type: generic 23 | name: iOSProApp 24 | version: 3.27.0.4 25 | - 26 | user_agent: '{"appVersion":"1.15.1","bundleId":"com.claritymoney.ios.prod","region":"US","buildNumber":"253","device":"iPhone 7","OS":"iOS 11.4.1","networking":"Alamofire 4.7.3","language":"en"}' 27 | client: 28 | type: generic 29 | name: com.claritymoney.ios.prod 30 | version: 1.15.1 31 | - 32 | user_agent: app=customstreetracer2; ver=2.1.0; dev=iPhone8,4; os=iOS; osver=12.1; res=1136x640 33 | client: 34 | type: generic 35 | name: customstreetracer2 36 | version: 2.1.0 37 | - 38 | user_agent: app=kmail; ver=2.1.8; dev=iPhone8,4; os=iOS; osver=12.1; res=1136x640 39 | client: 40 | type: pim 41 | name: kmail 42 | version: 2.1.8 43 | -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/local/client/extractor_name_version.yml: -------------------------------------------------------------------------------- 1 | - 2 | user_agent: AldiSued_US/18255 CFNetwork/974.2.1 Darwin/18.0.0 3 | client: 4 | type: generic 5 | name: AldiSued_US 6 | version: 18255 7 | - 8 | user_agent: AldiSued_US/3.9 (iPhone; iOS 12.0; Scale/2.00) 9 | client: 10 | type: generic 11 | name: AldiSued_US 12 | version: 3.9 13 | - 14 | user_agent: Android; samsung-SAMSUNG-SM-T377A; 6.0.1; AutoTrader.com; 2.6.4.3.236 15 | client: 16 | type: generic 17 | name: AutoTrader.com 18 | version: 2.6.4.3.236 19 | - 20 | user_agent: iPad; 10.3.3; cartrader.com; 3.0.10 21 | client: 22 | type: generic 23 | name: cartrader.com 24 | version: 3.0.10 25 | - 26 | user_agent: generic ua string/1.23.2 27 | client: 28 | type: generic 29 | name: generic ua string 30 | version: '1.23.2' 31 | - 32 | user_agent: generic ua string 2/1.23.2 misc data that we don't want 33 | client: 34 | type: generic 35 | name: generic ua string 2 36 | version: '1.23.2' 37 | - 38 | user_agent: Microsoft SkyDriveSync 18.091.0506.0007 ship; Windows NT 10.0 (17134) 39 | client: 40 | type: generic 41 | name: Microsoft SkyDriveSync 42 | version: '18.091.0506.0007' 43 | - 44 | user_agent: Call & Chat/1 CFNetwork/902.2 Darwin/17.7.0 45 | client: 46 | type: generic 47 | name: Call & Chat 48 | version: 1 49 | - 50 | user_agent: o7talkingtom2 53386 51 | client: 52 | type: generic 53 | name: o7talkingtom2 54 | version: 53386 55 | - 56 | user_agent: Callpod Keeper for Android 1.0 (11.2.1/303) Dalvik/2.1.0 (Linux; U; Android 6.0.1; SAMSUNG-SM-G900A Build/MMB29M) 57 | client: 58 | type: generic 59 | name: Callpod Keeper for Android 60 | version: 1.0 61 | - 62 | user_agent: "DICK'S/621 CFNetwork/974.2.1 Darwin/18.0.0" 63 | client: 64 | type: generic 65 | name: "DICK'S" 66 | version: 621 67 | - 68 | user_agent: edX/2.15.2 CFNetwork/901.1 Darwin/17.6.0 69 | client: 70 | type: generic 71 | name: edX 72 | version: '2.15.2' 73 | - 74 | user_agent: DigiCal (v1.8.2b; http://digibites.nl/digical) 75 | client: 76 | type: generic 77 | name: DigiCal 78 | version: '1.8.2' 79 | - 80 | user_agent: "Wood Puzzle!/1 CFNetwork/902.2 Darwin/17.7.0" 81 | client: 82 | type: generic 83 | name: "Wood Puzzle!" 84 | version: 1 85 | - 86 | user_agent: American Girl®/4.0.9 CFNetwork/974.2.1 Darwin/18.0.0 87 | client: 88 | type: generic 89 | name: American Girl® 90 | version: '4.0.9' 91 | - 92 | user_agent: Access, Lift & Handlers/4024 CFNetwork/893.14.2 Darwin/17.3.0 93 | client: 94 | type: generic 95 | name: Access, Lift & Handlers 96 | version: 4024 97 | - 98 | user_agent: What's it worth?/21 CFNetwork/811.5.4 Darwin/16.6.0 99 | client: 100 | type: generic 101 | name: What's it worth? 102 | version: 21 103 | - 104 | user_agent: Thrift/Swift (Session) 105 | client: 106 | type: generic 107 | name: Thrift 108 | version: '' 109 | - 110 | user_agent: Cocoa/THTTPClient 111 | client: 112 | type: generic 113 | name: Cocoa 114 | version: '' 115 | - 116 | user_agent: aws-sdk-android/unknown-version Linux/3.10.9-11788437 Dalvik/2.1.0/0 en_US 117 | client: 118 | type: library 119 | name: aws-sdk-android 120 | version: '' 121 | - 122 | user_agent: uservoice-android-1.2.4 123 | client: 124 | type: generic 125 | name: uservoice-android 126 | version: '1.2.4' 127 | - 128 | user_agent: Microsoft URL Control - 6.01.9782 129 | client: 130 | type: generic 131 | name: Microsoft URL Control 132 | version: '6.01.9782' 133 | - 134 | user_agent: openshot-qt-2.4.2 135 | client: 136 | type: generic 137 | name: openshot-qt 138 | version: '2.4.2' 139 | - 140 | user_agent: Microsoft.VisualStudio.Help (2.3) 141 | client: 142 | type: generic 143 | name: Microsoft.VisualStudio.Help 144 | version: '2.3' 145 | - 146 | user_agent: Zacks 4.0.4 (iPhone; iOS 12.0.1; UALib 8.6.3; s_8AaMoWTtiAUalpymsUqA; en_US) 147 | client: 148 | type: generic 149 | name: Zacks 150 | version: '4.0.4' 151 | - 152 | user_agent: ZillowRentals 12.0.2 (iPad; iOS 12.1; UALib 9.0.5; YuafAVKCR8GYdldn6nycbw; en_US) 153 | client: 154 | type: generic 155 | name: ZillowRentals 156 | version: '12.0.2' 157 | - 158 | user_agent: YouCam Fun/22261949 (iPhone; iOS 12.0.1; Scale/2.00) 159 | client: 160 | type: generic 161 | name: YouCam Fun 162 | version: 22261949 163 | - 164 | user_agent: WallPapers HD 1.4.2 (iPhone; iOS 11.4.1; en_US) 165 | client: 166 | type: generic 167 | name: WallPapers HD 168 | version: 1.4.2 169 | - 170 | user_agent: VZC/19.4.9/1/iOS/iPhone iPhone11,8 171 | client: 172 | type: generic 173 | name: VZC 174 | version: 19.4.9 175 | - 176 | user_agent: Version/2.94 (834) FLFitCloudAPI/1.0 iOS/12.1.1 Device/F0783886-45BF-470A-9B84-0706C6CB42D4 iPhone11,8 177 | client: 178 | type: library 179 | name: FLFitCloudAPI 180 | version: 1.0 181 | - 182 | user_agent: The Columbia Bank, MD/5.11.0.42 BundleID/com.intuit.mobilebanking01368 BundleDeviceFamily/iPhone,iPad (iPhone; iPhone11,2; iPhone XS; iOS 12.1.4) 183 | client: 184 | type: generic 185 | name: The Columbia Bank, MD 186 | version: 5.11.0.42 187 | - 188 | user_agent: The Economist on iPad NA/69 CFNetwork/893.14.2 Darwin/17.3.0 189 | client: 190 | type: generic 191 | name: The Economist on iPad NA 192 | version: 69 193 | - 194 | user_agent: tv.platomedia.hopsterapp/3.36.36 google-api-objc-client/3.0 iPhone/12.1.4 hw/iPhone6_1 (gzip) 195 | client: 196 | type: generic 197 | name: tv.platomedia.hopsterapp 198 | version: 3.36.36 199 | - 200 | user_agent: ArcGISiOS-10.2.5/12.0/iPad7,4 201 | client: 202 | type: generic 203 | name: ArcGISiOS-10.2.5 204 | version: 12.0 205 | - 206 | user_agent: 500px/771 CFNetwork/978.0.7 Darwin 207 | client: 208 | type: generic 209 | name: 500px 210 | version: 771 211 | - 212 | user_agent: BW/808 CFNetwork/978.0.7 Darwin 213 | client: 214 | type: generic 215 | name: BW 216 | version: 808 217 | -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/local/client/extractor_no_name.yml: -------------------------------------------------------------------------------- 1 | - 2 | user_agent: 0000956gUSchj6WSF7ahRyK4lJHFnprL 3 | client: 4 | - 5 | user_agent: 4UWWU-WBDXC-VYFN3-QDJMH 6 | client: 7 | - 8 | user_agent: 13F7BD1A-F6FF-411E-BF5E 9 | client: 10 | - 11 | user_agent: 13F7BD1A-F6FF-411E-BF5E 12 | - 13 | user_agent: Ya0eXACkoylZb9TS9qacxCw8XxYNu 14 | -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/local/client/extractor_whole_name.yml: -------------------------------------------------------------------------------- 1 | - 2 | user_agent: Evernote Windows 3 | client: 4 | type: mobile app 5 | name: Evernote Windows 6 | version: 7 | - 8 | user_agent: toys::file 9 | client: 10 | type: generic 11 | name: toys::file 12 | version: 13 | - 14 | user_agent: Chicken Feeder v4.0 15 | client: 16 | type: generic 17 | name: Chicken Feeder 18 | version: v4.0 19 | - 20 | user_agent: Turkey Barn Thermo 4.3.123.1 21 | client: 22 | type: generic 23 | name: Turkey Barn Thermo 24 | version: 4.3.123.1 25 | - 26 | user_agent: SpringBoard_WeatherFoundation[1]_16A 27 | client: 28 | type: generic 29 | name: SpringBoard_WeatherFoundation 30 | version: '' 31 | # --------------------------------------------------------------- 32 | # UA normalized, so no version extracted 33 | - 34 | user_agent: Weather_WeatherFoundation[1]_15E302 35 | client: 36 | type: generic 37 | name: Weather_WeatherFoundation 38 | version: '' 39 | - 40 | user_agent: AVGSETUP-WV7XXX64 41 | client: 42 | type: generic 43 | name: AVGSETUP 44 | version: '' 45 | - 46 | user_agent: AVGSETUP-W10HXX64 47 | client: 48 | type: generic 49 | name: AVGSETUP 50 | version: '' 51 | # --------------------------------------------------------------- 52 | - 53 | user_agent: samsung SAMSUNG-SM-T337A SyncML_DM Client 54 | client: 55 | type: generic 56 | name: SyncML_DM Client 57 | version: '' 58 | - 59 | user_agent: samsung SMT377P SPDClient to samsung SM-T377P SPD-Client 60 | client: 61 | type: generic 62 | name: SPD-Client 63 | version: '' 64 | - 65 | user_agent: WXCommonUtils 1.0 rv:1 (Macintosh; Mac OS X 10.14.0; en_US) 66 | client: 67 | type: generic 68 | name: WXCommonUtils 69 | version: '' 70 | - 71 | user_agent: Voltage Security LLC. Voltage SDM Client 72 | client: 73 | type: generic 74 | name: Voltage Security LLC. Voltage SDM Client 75 | version: '' 76 | - 77 | user_agent: NeroInfo 78 | client: 79 | type: mediaplayer 80 | name: Nero 81 | version: "" 82 | - 83 | user_agent: Windows Media Player 84 | client: 85 | type: mediaplayer 86 | name: Windows Media Player 87 | version: "" 88 | - 89 | user_agent: Adobe Update Manager 5 90 | client: 91 | type: mobile app 92 | name: Adobe Update Manager 93 | version: 5 94 | - 95 | user_agent: Adobe Real Time 96 | client: 97 | type: mobile app 98 | name: Adobe Real Time 99 | version: '' 100 | - 101 | user_agent: Adobe Online Services 102 | client: 103 | type: mobile app 104 | name: Adobe Online Services 105 | version: '' 106 | - 107 | user_agent: Adobe Log Transport 108 | client: 109 | type: mobile app 110 | name: Adobe Log Transport 111 | version: '' 112 | - 113 | user_agent: Adobe Flash Player 10 114 | client: 115 | type: mobile app 116 | name: Adobe Flash Player 117 | version: 10 118 | - 119 | user_agent: Adobe Downloader App 120 | client: 121 | type: mobile app 122 | name: Adobe Downloader App 123 | version: 124 | - 125 | user_agent: Adobe Application Manager 2.0 126 | client: 127 | type: mobile app 128 | name: Adobe Application Manager 129 | version: 2.0 130 | - 131 | user_agent: Acrobat viewer 132 | client: 133 | type: mobile app 134 | name: Acrobat Viewer 135 | version: 136 | - 137 | user_agent: Abbyy.Internet 138 | client: 139 | type: mobile app 140 | name: Abbyy 141 | version: 142 | - 143 | user_agent: appbox 144 | client: 145 | type: mobile app 146 | name: App Box 147 | version: 148 | - 149 | user_agent: APPCORE 150 | client: 151 | type: mobile app 152 | name: App Core 153 | version: 154 | - 155 | user_agent: AESMService 156 | client: 157 | type: mobile app 158 | name: AESM Service 159 | version: 160 | - 161 | user_agent: AKLocationSignInAlert 162 | client: 163 | type: mobile app 164 | name: AK Location Sign In Alert 165 | version: 166 | - 167 | user_agent: alcatraz 168 | client: 169 | type: mobile app 170 | name: Alcatraz 171 | version: 172 | - 173 | user_agent: AMPVConnector 174 | client: 175 | type: mobile app 176 | name: AMPV Connector 177 | version: 178 | - 179 | user_agent: arc-intouch 180 | client: 181 | type: mobile app 182 | name: ARC Intouch 183 | version: 184 | - 185 | user_agent: avdroid 186 | client: 187 | type: mobile app 188 | name: AvDroid 189 | version: 190 | - 191 | user_agent: avira 192 | client: 193 | type: mobile app 194 | name: Avira 195 | version: 196 | - 197 | user_agent: Corridor5.0.1011 198 | client: 199 | type: mobile app 200 | name: Corridor 201 | version: '5.0.1011' 202 | - 203 | user_agent: CUIPromotion 204 | client: 205 | type: mobile app 206 | name: CUIPromotion 207 | version: '' 208 | - 209 | user_agent: CuriosityAndroid169 210 | client: 211 | type: mobile app 212 | name: Curiosity Android 213 | version: '169' 214 | - 215 | user_agent: Densimeter Axiom iOS 216 | client: 217 | type: mobile app 218 | name: Densimeter Axiom 219 | version: '' 220 | - 221 | user_agent: Mapbox iOS SDK (iPhone/11.1.1) 222 | client: 223 | type: library 224 | name: Mapbox iOS SDK 225 | version: '' 226 | - 227 | user_agent: SCSDK/(iPhone;iOS;12.2) 228 | client: 229 | type: library 230 | name: SCSDK 231 | version: '' 232 | - 233 | user_agent: TopBuzz (iPhone; iOS 12.1.3;) 234 | client: 235 | type: generic 236 | name: TopBuzz 237 | version: '' 238 | # should auto-assign type to "desktop app" 239 | - 240 | user_agent: DieselDetect Updater 241 | client: 242 | type: desktop app 243 | name: DieselDetect Updater 244 | version: '' 245 | - 246 | user_agent: ACC14C605413729036F0B 247 | client: 248 | type: generic 249 | name: 'ACC' 250 | version: '' 251 | - 252 | user_agent: ACC_4.00.3019_1BF78BFDF84E7EEFDA 253 | client: 254 | type: generic 255 | name: 'ACC' 256 | version: '' 257 | - 258 | user_agent: UBT_1DF14945D8BA5CBDD 259 | client: 260 | type: generic 261 | name: 'UBT' 262 | version: '' 263 | -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/local/client/games.yml: -------------------------------------------------------------------------------- 1 | - 2 | user_agent: Angry Birds 2/2.20.2 CFNetwork/901.1 Darwin/17.6.0 3 | client: 4 | type: game 5 | name: Angry Birds 2 6 | version: 2.20.2 7 | - 8 | user_agent: AngryBirdsSpace/2.2.13.1 CFNetwork/901.1 Darwin/17.6.0 9 | client: 10 | type: game 11 | name: Angry Birds Space 12 | version: 2.2.13.1 13 | - 14 | user_agent: Asphalt8/1.1.3 CFNetwork/901.1 Darwin/17.6.0 15 | client: 16 | type: game 17 | name: Asphalt 8 18 | version: 1.1.3 19 | - 20 | user_agent: At Bat/29045 CFNetwork/901.1 Darwin/17.6.0 21 | client: 22 | type: game 23 | name: At Bat 24 | version: 29045 25 | -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/local/client/library.yml: -------------------------------------------------------------------------------- 1 | - 2 | user_agent: aws-sdk-cpp/1.2.9 windows/10.0.15063.608 3 | client: 4 | type: library 5 | name: Amazon Web Services SDK for cpp 6 | version: 1.2.9 7 | - 8 | user_agent: FBiOSSDK.4.14.0 9 | client: 10 | type: library 11 | name: Facebook SDK for iOS 12 | version: 4.14.0 13 | - 14 | user_agent: FBAndroidSDK.4.23.0 15 | client: 16 | type: library 17 | name: Facebook SDK for Android 18 | version: 4.23.0 19 | - 20 | user_agent: xboxservicesapi/2017.08.0.0 21 | client: 22 | type: library 23 | name: Xbox Services API 24 | version: 2017.08.0.0 -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/local/client/mediaplayer.yml: -------------------------------------------------------------------------------- 1 | - 2 | user_agent: Spotify/108000474 3 | client: 4 | type: mediaplayer 5 | name: Spotify 6 | version: "108000474" 7 | - 8 | user_agent: NeroUpdate/11.0.0042 9 | client: 10 | type: mediaplayer 11 | name: Nero 12 | version: "11.0.0042" 13 | - 14 | user_agent: Pandora/2092 CFNetwork/901.1 Darwin/17.6.0 15 | client: 16 | type: mediaplayer 17 | name: Pandora 18 | version: "2092" 19 | - 20 | user_agent: vox/2.5.6 (iPhone; iOS 14.6; Scale/2.00) 21 | client: 22 | type: mediaplayer 23 | name: VOX 24 | version: "2.5.6" 25 | - 26 | user_agent: VOX/2607 CFNetwork/1240.0.4 Darwin/20.5.0 27 | client: 28 | type: mediaplayer 29 | name: VOX 30 | version: 2607 31 | -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/local/client/messaging.yml: -------------------------------------------------------------------------------- 1 | - 2 | user_agent: mozilla/5.0 (windows nt 10.0; win64; x64) applewebkit/537.36 (khtml, like gecko) mattermost/3.6.0 chrome/53.0.2785.143 electron/1.4.13 safari/537.36 3 | client: 4 | type: messaging 5 | name: Mattermost 6 | version: "3.6.0" 7 | - 8 | user_agent: X-Lite/5.2.0.90534 X-Lite/5.2.0.90534 Mozilla/5.0 (Windows NT 6.1; WOW64) 9 | client: 10 | type: messaging 11 | name: X-Lite Softphone 12 | version: 5.2.0.90534 13 | - 14 | user_agent: ZoiperPremium/3.16.8 CFNetwork/897.15 Darwin/17.5.0 15 | client: 16 | type: messaging 17 | name: Zoiper Premium Softphone 18 | version: 3.16.8 -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/local/client/osutility.yml: -------------------------------------------------------------------------------- 1 | - 2 | user_agent: Actualización de software (unknown version) CFNetwork/974.1 Darwin/18.0.0 (x86_64) 3 | client: 4 | type: osutility 5 | name: Actualización de software 6 | version: '' 7 | - 8 | user_agent: dataaccessd (unknown version) CFNetwork/889.9 Darwin/17.2.0 9 | client: 10 | type: osutility 11 | name: dataaccessd 12 | version: '' 13 | - 14 | user_agent: mobileassetd (unknown version) CFNetwork/811.5.4 Darwin/16.7.0 (x86_64) 15 | client: 16 | type: osutility 17 | name: mobileassetd 18 | version: '' 19 | - 20 | user_agent: systemmigrationd (unknown version) CFNetwork/887 Darwin/17.0.0 (x86_64) 21 | client: 22 | type: osutility 23 | name: systemmigrationd 24 | version: '' 25 | - 26 | user_agent: nesessionmanager (unknown version) cfnetwork/897.15 darwin/17.5.0 27 | client: 28 | type: osutility 29 | name: nesessionmanager 30 | version: 31 | - 32 | user_agent: networkserviceproxy (unknown version) cfnetwork/894 darwin/17.4.0 33 | client: 34 | type: osutility 35 | name: networkserviceproxy 36 | version: 37 | - 38 | user_agent: securityd (unknown version) cfnetwork/758.2.8 darwin/15.0.0 39 | client: 40 | type: osutility 41 | name: securityd 42 | version: '' 43 | - 44 | user_agent: trustd (unknown version) cfnetwork/811.5.4 darwin/16.7.0 (x86_64) 45 | client: 46 | type: osutility 47 | name: trustd 48 | version: '' 49 | - 50 | user_agent: debian apt-http/1.3 51 | client: 52 | type: osutility 53 | name: Apt Updater 54 | version: "1.3" 55 | - 56 | user_agent: windowsshellclient/9.0.40929.0 57 | client: 58 | type: osutility 59 | name: Windows Shell Client 60 | version: "9.0.40929.0" 61 | - 62 | user_agent: windowsstore/11804.1001.10.0 63 | client: 64 | type: osutility 65 | name: Windows Store 66 | version: "11804.1001.10.0" 67 | - 68 | user_agent: windows store/1.0 69 | client: 70 | type: osutility 71 | name: Windows Store 72 | version: "1.0" 73 | - 74 | user_agent: windows-update-agent/10.0.10011.16384 client-protocol/1.40 75 | client: 76 | type: osutility 77 | name: Windows Update Agent 78 | version: "10.0.10011.16384" 79 | - 80 | user_agent: Microsoft-CryptoAPI/6.3 81 | client: 82 | type: osutility 83 | name: Microsoft CryptoAPI 84 | version: "6.3" 85 | - 86 | user_agent: Microsoft-NCSI 87 | client: 88 | type: osutility 89 | name: Microsoft NCSI 90 | version: "" 91 | - 92 | user_agent: Microsoft-BITS/7.8 93 | client: 94 | type: osutility 95 | name: Microsoft BITS 96 | version: "7.8" 97 | - 98 | user_agent: Microsoft-WNS/6.3 99 | client: 100 | type: osutility 101 | name: Microsoft WNS 102 | version: "6.3" 103 | - 104 | user_agent: MICROSOFT_DEVICE_METADATA_RETRIEVAL_CLIENT 105 | client: 106 | type: osutility 107 | name: Microsoft Device Metadata Retrieval Client 108 | version: "" 109 | - 110 | user_agent: Microsoft-Delivery-Optimization/10.0 111 | client: 112 | type: osutility 113 | name: Microsoft Delivery Optimization 114 | version: "10.0" 115 | - 116 | user_agent: HP-Inkjet-WebUpdate-1.1-cl 117 | client: 118 | type: osutility 119 | name: HP-Inkjet-WebUpdate 120 | version: "1.1" 121 | -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/local/client/p2p.yml: -------------------------------------------------------------------------------- 1 | #- 2 | # user_agent: Azureus 2.45.12 3 | # client: 4 | # type: p2p 5 | # name: Vuze BitTorrent Client 6 | # version: '2.45.12' 7 | - 8 | user_agent: BTWebClient/180B(9704) 9 | client: 10 | type: p2p 11 | name: BitTorrent Client 12 | version: 180B 13 | - 14 | user_agent: ET P2P Torrent Client User-Agent (Solid Core/0.82) 15 | client: 16 | type: p2p 17 | name: SolidCore P2P 18 | version: '0.82' 19 | - 20 | user_agent: µtorrent/43796 cfnetwork/897.15 darwin/17.5.0 (x86_64) 21 | client: 22 | type: p2p 23 | name: uTorrent 24 | version: '43796' 25 | -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/local/client/pim.yml: -------------------------------------------------------------------------------- 1 | - 2 | user_agent: G-eM Client/GOAuth2RequestFactory-CS-Version=2.2.0.29859 3 | client: 4 | type: pim 5 | name: eM Client 6 | version: '2.2.0.29859' 7 | - 8 | user_agent: gmail/5.0.180506.764439 cfnetwork/894 darwin/17.4.0 9 | client: 10 | type: pim 11 | name: Gmail 12 | version: 5.0.180506.764439 13 | - 14 | user_agent: outlook/31007 cfnetwork/897.15 darwin/17.5.0 15 | client: 16 | type: pim 17 | name: Microsoft Outlook 18 | version: 31007 19 | - 20 | user_agent: BlueMail/9207 CFNetwork/897.15 Darwin/17.5.0 21 | client: 22 | type: pim 23 | name: Blue Mail 24 | version: 9207 -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/local/client/vpnproxy.yml: -------------------------------------------------------------------------------- 1 | - 2 | user_agent: PxBroker/0.3.1/3308 3 | client: 4 | type: vpnproxy 5 | name: ProxyBroker 6 | version: '0.3.1' 7 | - 8 | user_agent: Tor2016Dec14JU/1.0 CFNetwork/901.1 Darwin/17.6.0 9 | client: 10 | type: vpnproxy 11 | name: The Onion Router (TOR) 12 | version: '1.0' 13 | -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/local/collisions.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # Supplemental regexes for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2018 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | # ----------------------------------------------------------------- 9 | # The "Focus" UAs below collide with Firefox Focus 10 | - 11 | user_agent: Be Focused/104 CFNetwork/976 Darwin/18.2.0 12 | client: 13 | name: Be Focused 14 | - 15 | user_agent: Focus/1 CFNetwork/1240.0.4 Darwin/20.5.0 16 | client: 17 | name: Focus 18 | - 19 | user_agent: Focus Matrix/155 CFNetwork/976 Darwin/18.2.0 20 | client: 21 | name: Focus Matrix 22 | - 23 | user_agent: Klara/1 CFNetwork/1128.0.1 Darwin/19.6.0 24 | client: 25 | name: Klara 26 | - 27 | user_agent: FLCore v3 - Window Focus 28 | client: 29 | name: FLCore 30 | - 31 | user_agent: Focuskeeper/2.0.15.4 CFNetwork/1240.0.4 Darwin/20.5.0 32 | client: 33 | name: Focuskeeper 34 | # ----------------------------------------------------------------- 35 | -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/local/extractor/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinkwelltwd/device_detector/7df1d95d4c6342499e7d04bcb4e08a2948fde0b1/device_detector/tests/parser/fixtures/local/extractor/__init__.py -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/local/extractor/app_id_override_name.yml: -------------------------------------------------------------------------------- 1 | - 2 | user_agent: Mozilla/5.0 (Linux; Android 8.0.0; SM-G935V Build/R16NW; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/68.0.3440.91 Mobile Safari/537.36 [FBAN/AudienceNetworkForAndroid;FBSN/Android;FBSV/8.0.0;FBAB/com.smilerlee.klondike;FBAV/2.5;FBBV/15;FBVS/4.23.0;FBLC/en_US] 3 | client: 4 | app_id: com.smilerlee.klondike 5 | pretty_name: Smilerlee Klondike 6 | - 7 | user_agent: Dalvik/2.1.0 (Linux; U; Android 6.0.1; SAMSUNG-SM-G930A Build/MMB29M) [FBAN/AudienceNetworkForAndroid;FBSN/Android;FBSV/6.0.1;FBAB/bible.wordgame.words.connect.crossword.cookies;FBAV/2.11.6;FBBV/86;FBVS/5.0.1;FBLC/en_US] 8 | client: 9 | app_id: bible.wordgame.words.connect.crossword.cookies 10 | pretty_name: Wordgame Words Connect Crossword Cookies 11 | - 12 | user_agent: Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-T237P Build/LMY47X) [FBAN/AudienceNetworkForAndroid;FBSN/Android;FBSV/5.1.1;FBAB/com.surpax.ledflashlight.panel;FBAV/1.2.4;FBBV/63;FBVS/4.28.0;FBLC/en_US] 13 | client: 14 | app_id: com.surpax.ledflashlight.panel 15 | pretty_name: Surpax Ledflashlight Panel 16 | - 17 | user_agent: Dalvik/2.1.0 (Linux; U; Android 5.1.1; SAMSUNG-SM-T337A Build/LMY47X) [FBAN/AudienceNetworkForAndroid;FBSN/Android;FBSV/5.1.1;FBAB/org.whiteglow.keepmynotes;FBAV/1.60.7;FBBV/86;FBVS/4.28.2;FBLC/en_US] 18 | client: 19 | app_id: org.whiteglow.keepmynotes 20 | pretty_name: Whiteglow Keepmynotes 21 | - 22 | user_agent: Mozilla/5.0 (Linux; Android 7.0; SM-G930R6 Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/72.0.3626.105 Mobile Safari/537.36 [FBAN/AudienceNetworkForAndroid;FBSN/Android;FBSV/7.0;FBAB/kjv.bible.kingjamesbible;FBAV/2.0;FBBV/11;FBVS/4.23.0;FBLC/en_US] 23 | client: 24 | app_id: kjv.bible.kingjamesbible 25 | pretty_name: Bible Kingjamesbible 26 | - 27 | user_agent: YHOO YahooMobile/1.0 (com.aol.mapquest; 5.18.6) (Apple; iPhone; iOS/12.1.4); 28 | client: 29 | app_id: com.aol.mapquest 30 | pretty_name: Aol Mapquest 31 | - 32 | user_agent: YHOO YahooMobile/1.0 (com.softacular.Sportacular; 7.10.1) (Apple; iPhone; iOS/11.4.1); 33 | client: 34 | app_id: com.softacular.Sportacular 35 | pretty_name: Softacular Sportacular 36 | - 37 | user_agent: YHOO YahooMobile/1.0 (com.yahoo.Aerogram; 4.41) (Apple; iPhone; iOS/12.1); 38 | client: 39 | app_id: com.yahoo.Aerogram 40 | pretty_name: Yahoo Aerogram 41 | - 42 | user_agent: YHOO YahooMobile/1.0 (com.yahoo.weather; 1.18.0) (Apple; iPhone; iOS/11.4.1); 43 | client: 44 | app_id: com.yahoo.weather 45 | pretty_name: Yahoo Weather 46 | -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/local/osfragments.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # Supplemental regexes for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2018 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | - 9 | user_agent: eBayiPad/5.21.0 10 | name: iOS 11 | - 12 | user_agent: eBayiPhone/5.17.0 13 | name: iOS 14 | - 15 | user_agent: Arcus-iOS/1.0.200220 16 | name: iOS 17 | - 18 | user_agent: com.apple.trustd/2.0 19 | name: Mac 20 | - 21 | user_agent: com.apple.Maps 22 | name: Mac 23 | - 24 | user_agent: ADOBE_IAL_CLIENT_MAC_1.0 25 | name: Mac 26 | - 27 | user_agent: GroupMe-Android/7130001 28 | name: Android 29 | - 30 | user_agent: placed-agent-android/4.0.3/19bfc846cfa1/475_4.4 31 | name: Android 32 | - 33 | user_agent: Helpshift-Android/6.4.1/6.0.1 34 | name: Android 35 | - 36 | user_agent: VeneziaAndroid/release-10.05990820 37 | name: Android 38 | - 39 | user_agent: eBayAndroid/5.21.0.19 40 | name: Android 41 | -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/local/oss.yml: -------------------------------------------------------------------------------- 1 | ############### 2 | # Supplemental regexes for Device Detector - The Universal Device Detection library for parsing User Agents 3 | # 4 | # Copyright (c) 2018 Dave Burkholder, Thinkwell Designs 5 | # @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 6 | ############### 7 | 8 | - 9 | user_agent: Viber/6.6.21745.1000 Win10PC/10.0.17134.48 10 | os: 11 | name: Windows 12 | short_name: WIN 13 | version: 10 14 | platform: 15 | -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/upstream/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinkwelltwd/device_detector/7df1d95d4c6342499e7d04bcb4e08a2948fde0b1/device_detector/tests/parser/fixtures/upstream/__init__.py -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/upstream/client/feed_reader.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - 3 | user_agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.21 (KHTML, like Gecko) akregator/4.11.5 Safari/537.21 4 | client: 5 | type: feed reader 6 | name: Akregator 7 | version: "4.11.5" 8 | - 9 | user_agent: Akregator/4.12.3; syndication SUSE 10 | client: 11 | type: feed reader 12 | name: Akregator 13 | version: "4.12.3" 14 | - 15 | user_agent: Akregator/1.2.9; librss/remnants 16 | client: 17 | type: feed reader 18 | name: Akregator 19 | version: "1.2.9" 20 | - 21 | user_agent: Apple-PubSub/65.28 22 | client: 23 | type: feed reader 24 | name: Apple PubSub 25 | version: "65.28" 26 | - 27 | user_agent: FeedDemon/4.5 (http://www.feeddemon.com/; Microsoft Windows) 28 | client: 29 | type: feed reader 30 | name: FeedDemon 31 | version: "4.5" 32 | - 33 | user_agent: FeedDemon/4.5 (http://www.feeddemon.com/; Microsoft Windows XP) 34 | client: 35 | type: feed reader 36 | name: FeedDemon 37 | version: "4.5" 38 | - 39 | user_agent: FeeddlerPro/2.4 CFNetwork/672.0.8 Darwin/14.0.0 40 | client: 41 | type: feed reader 42 | name: Feeddler RSS Reader 43 | version: "2.4" 44 | - 45 | user_agent: FeeddlerRSS/2.4 CFNetwork/548.1.4 Darwin/11.0.0 46 | client: 47 | type: feed reader 48 | name: Feeddler RSS Reader 49 | version: "2.4" 50 | - 51 | user_agent: FeeddlerRSS 2.4 (iPad; iPhone OS 5.1.1; en_US) 52 | client: 53 | type: feed reader 54 | name: Feeddler RSS Reader 55 | version: "2.4" 56 | - 57 | user_agent: JetBrains Omea Reader 2.2 (http://www.jetbrains.com/omea/reader/) 58 | client: 59 | type: feed reader 60 | name: JetBrains Omea Reader 61 | version: "2.2" 62 | - 63 | user_agent: Liferea/1.6.4 (Linux; en_US.UTF-8; http://liferea.sf.net/) 64 | client: 65 | type: feed reader 66 | name: Liferea 67 | version: "1.6.4" 68 | - 69 | user_agent: Liferea/1.10-RC1 (Linux; en_GB.UTF-8; http://liferea.sf.net/) 70 | client: 71 | type: feed reader 72 | name: Liferea 73 | version: "1.10" 74 | - 75 | user_agent: Liferea/1.10.6 (Linux; en_US.UTF8; http://liferea.sf.net/) AppleWebKit (KHTML, like Gecko) 76 | client: 77 | type: feed reader 78 | name: Liferea 79 | version: "1.10.6" 80 | - 81 | user_agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.74.9 (KHTML, like Gecko) Version/6.0 NetNewsWire/4.0.0 82 | client: 83 | type: feed reader 84 | name: NetNewsWire 85 | version: "4.0.0" 86 | - 87 | user_agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.74.9 (KHTML, like Gecko) NetNewsWire/3.3.2 88 | client: 89 | type: feed reader 90 | name: NetNewsWire 91 | version: "3.3.2" 92 | - 93 | user_agent: NetNewsWire/4.0.0 (Mac OS X; http://netnewswireapp.com/mac/; gzip-happy) 94 | client: 95 | type: feed reader 96 | name: NetNewsWire 97 | version: "4.0.0" 98 | - 99 | user_agent: newsbeuter/2.7 (Linux x86_64) 100 | client: 101 | type: feed reader 102 | name: Newsbeuter 103 | version: "2.7" 104 | - 105 | user_agent: NewsBlur iPhone App v3.6 106 | client: 107 | type: feed reader 108 | name: NewsBlur Mobile App 109 | version: "3.6" 110 | - 111 | user_agent: NewsBlur iPad App v3.6 112 | client: 113 | type: feed reader 114 | name: NewsBlur Mobile App 115 | version: "3.6" 116 | - 117 | user_agent: NewsBlur/4.0.1 CFNetwork/672.1.13 Darwin/14.0.0 118 | client: 119 | type: feed reader 120 | name: NewsBlur 121 | version: "4.0.1" 122 | - 123 | user_agent: newsbeuter/2.4 (Linux 3.2.0-23-generic; i686; http://www.newsbeuter.org/) libcurl/7.22.0 GnuTLS/2.12.14 zlib/1.2.3.4 libidn/1.23 librtmp/2.3 124 | client: 125 | type: feed reader 126 | name: Newsbeuter 127 | version: "2.4" 128 | - 129 | user_agent: Pulp/1.5.2 (iPad; http://www.acrylicapps.com/pulp/) 130 | client: 131 | type: feed reader 132 | name: Pulp 133 | version: "1.5.2" 134 | - 135 | user_agent: ReadKit/2.4.0 (Mac OS X Version 10.9.2 (Build 13C64)) 136 | client: 137 | type: feed reader 138 | name: ReadKit 139 | version: "2.4.0" 140 | - 141 | user_agent: 'ReadKit/7017 CFNetwork/673.2.1 Darwin/13.1.0 (x86_64) (MacBookPro10%2C1)' 142 | client: 143 | type: feed reader 144 | name: ReadKit 145 | version: "7017" 146 | - 147 | user_agent: Reeder/3.2 CFNetwork/672.1.12 Darwin/14.0.0 148 | client: 149 | type: feed reader 150 | name: Reeder 151 | version: "3.2" 152 | - 153 | user_agent: RssBandit/1.9.0.1002 154 | client: 155 | type: feed reader 156 | name: RSS Bandit 157 | version: "1.9.0.1002" 158 | - 159 | user_agent: RssBandit/1.9.0.1002 (.NET CLR 2.0.50727.7512; WinNT 6.2.9200.0; http://www.rssbandit.org) 160 | client: 161 | type: feed reader 162 | name: RSS Bandit 163 | version: "1.9.0.1002" 164 | - 165 | user_agent: RSS Junkie Daemon 166 | client: 167 | type: feed reader 168 | name: RSS Junkie 169 | version: "" 170 | - 171 | user_agent: RSSOwl/2.2.1.201312301314 (Windows; U; en) 172 | client: 173 | type: feed reader 174 | name: RSSOwl 175 | version: "2.2.1.201312301314" 176 | - 177 | user_agent: RSSOwl/2.2.1.201312301316 (X11; U; en) 178 | client: 179 | type: feed reader 180 | name: RSSOwl 181 | version: "2.2.1.201312301316" 182 | - 183 | user_agent: Stringer (https://github.com/swanson/stringer) 184 | client: 185 | type: feed reader 186 | name: Stringer 187 | version: "" 188 | -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/upstream/client/library.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - 3 | user_agent: Wget/1.10+devel 4 | client: 5 | type: library 6 | name: Wget 7 | version: "1.10" 8 | - 9 | user_agent: Wget/1.11.4 Red Hat modified 10 | client: 11 | type: library 12 | name: Wget 13 | version: "1.11.4" 14 | - 15 | user_agent: Wget/ (linux-gnu) 16 | client: 17 | type: library 18 | name: Wget 19 | version: "" 20 | - 21 | user_agent: curl/7.21.0 (i386-redhat-linux-gnu) libcurl/7.21.0 NSS/3.12.10.0 zlib/1.2.5 libidn/1.18 libssh2/1.2.4 22 | client: 23 | type: library 24 | name: curl 25 | version: "7.21.0" 26 | - 27 | user_agent: PycURL/7.19.3.1 libcurl/7.26.0 GnuTLS/2.12.20 zlib/1.2.7 libidn/1.25 libssh2/1.4.2 librtmp/2.3 28 | client: 29 | type: library 30 | name: curl 31 | version: "7.26.0" 32 | - 33 | user_agent: python-requests/1.2.0 CPython/2.7.3 Linux/3.8.0-33-generic 34 | client: 35 | type: library 36 | name: Python Requests 37 | version: "1.2.0" 38 | - 39 | user_agent: python-requests/1.2.0 CPython/2.7.5 Windows/7 40 | client: 41 | type: library 42 | name: Python Requests 43 | version: "1.2.0" 44 | - 45 | user_agent: Python-urllib/2.6 46 | client: 47 | type: library 48 | name: Python urllib 49 | version: "2.6" 50 | - 51 | user_agent: Mozilla/5.0 (Python-urllib2) 52 | client: 53 | type: library 54 | name: Python urllib 55 | version: "" 56 | - 57 | user_agent: Java/1.7.0_51 58 | client: 59 | type: library 60 | name: Java 61 | version: "1.7.0" 62 | - 63 | user_agent: Java1.1.4 64 | client: 65 | type: library 66 | name: Java 67 | version: "1.1.4" 68 | - 69 | user_agent: libwww-perl/5.69 70 | client: 71 | type: library 72 | name: Perl 73 | version: "5.69" 74 | - 75 | user_agent: perlclient/1.0 76 | client: 77 | type: library 78 | name: Perl 79 | version: "1.0" 80 | - 81 | user_agent: Guzzle/3.9.3 curl/7.38.0 PHP/5.6.14-0+deb8u1 82 | client: 83 | type: library 84 | name: Guzzle (PHP HTTP Client) 85 | version: "3.9.3" 86 | - 87 | user_agent: HTTP_Request2/2.3.0 (http://pear.php.net/package/http_request2) PHP/5.3.3 88 | client: 89 | type: library 90 | name: HTTP_Request2 91 | version: "2.3.0" 92 | - 93 | user_agent: Mechanize/2.7.3 Ruby/1.9.3p551 (http://github.com/sparklemotion/mechanize/) 94 | client: 95 | type: library 96 | name: Mechanize 97 | version: "2.7.3" 98 | - 99 | user_agent: Python/3.5 aiohttp/1.0.5 100 | client: 101 | type: library 102 | name: aiohttp 103 | version: "1.0.5" 104 | - 105 | user_agent: Google-HTTP-Java-Client/1.17.0-rc (gzip) 106 | client: 107 | type: library 108 | name: Google HTTP Java Client 109 | version: "1.17.0-rc" 110 | - 111 | user_agent: WWW-Mechanize/1.73 112 | client: 113 | type: library 114 | name: WWW-Mechanize 115 | version: "1.73" 116 | - 117 | user_agent: Faraday v0.9.1 118 | client: 119 | type: library 120 | name: Faraday 121 | version: "0.9.1" 122 | - 123 | user_agent: Go-http-client/1.1 124 | client: 125 | type: library 126 | name: Go-http-client 127 | version: "1.1" 128 | - 129 | user_agent: Go-http-client/2.0 130 | client: 131 | type: library 132 | name: Go-http-client 133 | version: "2.0" 134 | - 135 | user_agent: Go 1.1 package http 136 | client: 137 | type: library 138 | name: Go-http-client 139 | version: "1.1" 140 | - 141 | user_agent: urlgrabber/3.9.1 yum/3.2.29 142 | client: 143 | type: library 144 | name: urlgrabber (yum) 145 | version: "3.9.1" 146 | - 147 | user_agent: urlgrabber/3.10 yum/3.4.3 148 | client: 149 | type: library 150 | name: urlgrabber (yum) 151 | version: "3.10" 152 | - 153 | user_agent: libdnf/0.11.1 154 | client: 155 | type: library 156 | name: libdnf 157 | version: "0.11.1" 158 | - 159 | user_agent: HTTPie/1.0.2 160 | client: 161 | type: library 162 | name: HTTPie 163 | version: "1.0.2" 164 | - 165 | user_agent: rest-client/2.0.2 (linux-gnu x86_64) ruby/2.5.3p105 166 | client: 167 | type: library 168 | name: REST Client for Ruby 169 | version: "2.0.2" 170 | - 171 | user_agent: RestSharp/106.2.1.0 172 | client: 173 | type: library 174 | name: RestSharp 175 | version: "106.2.1.0" 176 | - 177 | user_agent: scalaj-http/2.4.0 178 | client: 179 | type: library 180 | name: ScalaJ HTTP 181 | version: "2.4.0" 182 | - 183 | user_agent: REST::Client/273 184 | client: 185 | type: library 186 | name: Perl REST::Client 187 | version: "273" 188 | - 189 | user_agent: node-fetch/1.0 (+https://github.com/bitinn/node-fetch) 190 | client: 191 | type: library 192 | name: Node Fetch 193 | version: "1.0" 194 | - 195 | user_agent: ReactorNetty/0.9.10.RELEASE 196 | client: 197 | type: library 198 | name: ReactorNetty 199 | version: "0.9.10" 200 | - 201 | user_agent: PostmanRuntime/7.9.1 202 | client: 203 | type: library 204 | name: Postman Desktop 205 | version: "7.9.1" 206 | - 207 | user_agent: Jakarta Commons-HttpClient/3.1 208 | client: 209 | type: library 210 | name: Jakarta Commons HttpClient 211 | version: "3.1" 212 | - 213 | user_agent: Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5) 214 | client: 215 | type: library 216 | name: WinHttp WinHttpRequest 217 | version: "5" 218 | - 219 | user_agent: Embarcadero URI Client/1.0 220 | client: 221 | type: library 222 | name: Embarcadero URI Client 223 | version: "1.0" 224 | - 225 | user_agent: Mikrotik/6.x Fetch 226 | client: 227 | type: library 228 | name: Mikrotik Fetch 229 | version: "6" 230 | - 231 | user_agent: akka-http/10.1.11 232 | client: 233 | type: library 234 | name: Akka HTTP 235 | version: 10.1.11 236 | - 237 | user_agent: BTWebClient/2020(19648) 238 | client: 239 | type: library 240 | name: uTorrent 241 | version: "" 242 | - 243 | user_agent: aria2/1.33.1 244 | client: 245 | type: library 246 | name: Aria2 247 | version: 1.33.1 248 | - 249 | user_agent: gvfs/1.34.1 250 | client: 251 | type: library 252 | name: gvfs 253 | version: 1.34.1 254 | - 255 | user_agent: uclient-fetch 256 | client: 257 | type: library 258 | name: uclient-fetch 259 | version: "" 260 | - 261 | user_agent: cpprestsdk/2.8.0 262 | client: 263 | type: library 264 | name: C++ REST SDK 265 | version: 2.8.0 266 | - 267 | user_agent: lua-resty-http/0.10 (Lua) ngx_lua/10019 268 | client: 269 | type: library 270 | name: LUA OpenResty NGINX 271 | version: "0.10" 272 | -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/upstream/client/mediaplayer.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - 3 | user_agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.28) Gecko/20130316 Songbird/1.12.1 (20140112193149) 4 | client: 5 | type: mediaplayer 6 | name: Songbird 7 | version: "1.12.1" 8 | - 9 | user_agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.28) Gecko/20130316 Nightingale/1.12.2 (20140112193149) 10 | client: 11 | type: mediaplayer 12 | name: Nightingale 13 | version: "1.12.2" 14 | - 15 | user_agent: iTunes/10.2.1 (Macintosh; Intel Mac OS X 10.7) AppleWebKit/534.20.8 16 | client: 17 | type: mediaplayer 18 | name: iTunes 19 | version: "10.2.1" 20 | - 21 | user_agent: iTunes/10.2.1 (Windows; Microsoft Windows 7 Enterprise Edition Service Pack 1 (Build 7601)) AppleWebKit/533.20.25 22 | client: 23 | type: mediaplayer 24 | name: iTunes 25 | version: "10.2.1" 26 | - 27 | user_agent: VLC/2.1.0 LibVLC/2.1.0 28 | client: 29 | type: mediaplayer 30 | name: VLC 31 | version: "2.1.0" 32 | - 33 | user_agent: LibVLC/2.2.3 (LIVE555 Streaming Media v2015.10.12) 34 | client: 35 | type: mediaplayer 36 | name: VLC 37 | version: "2.2.3" 38 | - 39 | user_agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; FunWebProducts; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506; Windows-Media-Player/10.00.00.3990) 40 | client: 41 | type: mediaplayer 42 | name: Windows Media Player 43 | version: "10.00.00.3990" 44 | - 45 | user_agent: Windows-Media-Player/11.0.6001.7000 46 | client: 47 | type: mediaplayer 48 | name: Windows Media Player 49 | version: "11.0.6001.7000" 50 | - 51 | user_agent: SAMSUNG-GT-S3850/S3850CXKD1 SHP/VPP/R5 Dolfin/2.0 NexPlayer/3.0 SMM-MMS/1.2.0 profile/MIDP-2.1 configuration/CLDC-1.1 OPN-B 52 | client: 53 | type: mediaplayer 54 | name: NexPlayer 55 | version: "3.0" 56 | - 57 | user_agent: Banshee 1.5.1 (http://banshee-project.org/) 58 | client: 59 | type: mediaplayer 60 | name: Banshee 61 | version: "1.5.1" 62 | - 63 | user_agent: Banshee/2.6.2 (http://banshee-project.org/) 64 | client: 65 | type: mediaplayer 66 | name: Banshee 67 | version: "2.6.2" 68 | - 69 | user_agent: QuickTime/7.6.6 (qtver=7.6.6;cpu=IA32;os=Mac 10.6.8) 70 | client: 71 | type: mediaplayer 72 | name: QuickTime 73 | version: "7.6.6" 74 | - 75 | user_agent: QuickTime.7.7.4 (qtver=7.7.4;os=Windows NT 6.0Service Pack 2) 76 | client: 77 | type: mediaplayer 78 | name: QuickTime 79 | version: "7.7.4" 80 | - 81 | user_agent: QuickTime (qtver=7.0.2a26;os=Windows NT 6.0) 82 | client: 83 | type: mediaplayer 84 | name: QuickTime 85 | version: "7.0.2" 86 | - 87 | user_agent: QuickTime E-/7.7.5 (qtver=7.7.5;os=Windows NT 6.1) 88 | client: 89 | type: mediaplayer 90 | name: QuickTime 91 | version: "7.7.5" 92 | - 93 | user_agent: FlyCast/1.34 (BlackBerry; 8330/4.5.0.131 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/-1) 94 | client: 95 | type: mediaplayer 96 | name: FlyCast 97 | version: "1.34" 98 | - 99 | user_agent: XBMC/9.04 r19840 (Mac OS X; Darwin 9.6.0; http://www.xbmc.org) 100 | client: 101 | type: mediaplayer 102 | name: XBMC 103 | version: "9.04" 104 | - 105 | user_agent: XBMC/9.04-beta1 r19639 (Windows; Windows XP Professional Service Pack 2 build 2600; http://www.xbmc.org) 106 | client: 107 | type: mediaplayer 108 | name: XBMC 109 | version: "9.04" 110 | - 111 | user_agent: SubStream/0.7 CFNetwork/485.12.30 Darwin/10.4.0 112 | client: 113 | type: mediaplayer 114 | name: SubStream 115 | version: "0.7" 116 | - 117 | user_agent: MediaMonkey 4.1.1.1703 118 | client: 119 | type: mediaplayer 120 | name: MediaMonkey 121 | version: "4.1.1.1703" 122 | - 123 | user_agent: Clementine 1.2.2 124 | client: 125 | type: mediaplayer 126 | name: Clementine 127 | version: "1.2.2" 128 | - 129 | user_agent: WAFA/1.2.10 (Linux; Android 4.1; Winamp) Replicant/1.0 130 | client: 131 | type: mediaplayer 132 | name: Winamp 133 | version: "" 134 | - 135 | user_agent: WinampMPEG/5.66, Ultravox/2.1 136 | client: 137 | type: mediaplayer 138 | name: Winamp 139 | version: "5.66" 140 | - 141 | user_agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9) Gecko Miro/2.0.4 (http://www.getmiro.com/) 142 | client: 143 | type: mediaplayer 144 | name: Miro 145 | version: "2.0.4" 146 | - 147 | user_agent: Miro/3.0.1 (http://www.getmiro.com/; Darwin 8.11.1 i386) 148 | client: 149 | type: mediaplayer 150 | name: Miro 151 | version: "3.0.1" 152 | - 153 | user_agent: Kodi/14.0 (Macintosh; Intel Mac OS X 10_10_3) App_Bitness/64 Version/14.0-Git:2014-12-23-ad747d9-dirty 154 | client: 155 | type: mediaplayer 156 | name: Kodi 157 | version: "14.0" 158 | - 159 | user_agent: foobar2000/1.3.10 160 | client: 161 | type: mediaplayer 162 | name: Foobar2000 163 | version: "1.3.10" 164 | - 165 | user_agent: Linux UPnP/1.0 Sonos/61.1-83220 (ZPS1) 166 | client: 167 | type: mediaplayer 168 | name: SONOS 169 | version: 61.1 170 | - 171 | user_agent: Linux UPnP/1.0 Sonos/57.4-83220 (ZP120) 172 | client: 173 | type: mediaplayer 174 | name: SONOS 175 | version: 57.4 176 | - 177 | user_agent: Linux UPnP/1.0 Sonos/61.1-83220 (ZPS3) 178 | client: 179 | type: mediaplayer 180 | name: SONOS 181 | version: 61.1 182 | - 183 | user_agent: HTC Streaming Player htc / 1.0 / o2_de / 4.2.2 184 | client: 185 | type: mediaplayer 186 | name: HTC Streaming Player 187 | version: "" 188 | - 189 | user_agent: MPlayer 1.1-4.6 190 | client: 191 | type: mediaplayer 192 | name: MPlayer 193 | version: "1.1" 194 | -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/upstream/client/pim.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - 3 | user_agent: Outlook-Express/7.0 (MSIE 7.0; Windows NT 6.1; WOW64; Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; AskTbORJ/5.15.9.29495; .NET4.0E; TmstmpExt) 4 | client: 5 | type: pim 6 | name: Outlook Express 7 | version: "7.0" 8 | - 9 | user_agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Win64; x64; Trident/7.0; .NET CLR 2.0.50727; SLCC2; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; InfoPath.3; .NET CLR 1.1.4322; FDM; Tablet PC 2.0; .NET4.0E; Microsoft Outlook 14.0.7113; ms-office; MSOffice 14) 10 | client: 11 | type: pim 12 | name: Microsoft Outlook 13 | version: "14.0.7113" 14 | - 15 | user_agent: Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20130330 Thunderbird/17.0.5 16 | client: 17 | type: pim 18 | name: Thunderbird 19 | version: "17.0.5" 20 | - 21 | user_agent: Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Thunderbird/24.4.0 Lightning/2.6.4 22 | client: 23 | type: pim 24 | name: Thunderbird 25 | version: "24.4.0" 26 | - 27 | user_agent: Airmail 1.4 rv:238 (Macintosh; Mac OS X 10.9.2; hr_HR) 28 | client: 29 | type: pim 30 | name: Airmail 31 | version: "1.4" 32 | - 33 | user_agent: Airmail 1.3.3 rv:237 (Macintosh; Mac OS X 10.9.2; en_US) 34 | client: 35 | type: pim 36 | name: Airmail 37 | version: "1.3.3" 38 | - 39 | user_agent: Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Icedove/24.4.0 40 | client: 41 | type: pim 42 | name: Thunderbird 43 | version: "24.4.0" 44 | - 45 | user_agent: Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Icedove/24.4.0 Lightning/2.6.5 46 | client: 47 | type: pim 48 | name: Thunderbird 49 | version: "24.4.0" 50 | - 51 | user_agent: Mozilla/4.0 (compatible; Lotus-Notes/6.0; Windows-NT) 52 | client: 53 | type: pim 54 | name: Lotus Notes 55 | version: "6.0" 56 | - 57 | user_agent: Barca/2.8.4400 58 | client: 59 | type: pim 60 | name: Barca 61 | version: "2.8.4400" 62 | - 63 | user_agent: BarcaPro/1.4 L.1001 64 | client: 65 | type: pim 66 | name: Barca 67 | version: "1.4" 68 | - 69 | user_agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.1.8) Gecko/20100317 Postbox/1.1.3 70 | client: 71 | type: pim 72 | name: Postbox 73 | version: "1.1.3" 74 | - 75 | user_agent: Postbox 1.0b14 (Windows/2009072715) 76 | client: 77 | type: pim 78 | name: Postbox 79 | version: "1.0" 80 | - 81 | user_agent: MailBar/1.3.2 (Mac OS X Version 10.11.1 (Build 15B42)) 82 | client: 83 | type: pim 84 | name: MailBar 85 | version: "1.3.2" 86 | - 87 | user_agent: The Bat! 4.0.0.22 88 | client: 89 | type: pim 90 | name: The Bat! 91 | version: "4.0.0.22" 92 | - 93 | user_agent: The Bat! Voyager 4.0.18.4 94 | client: 95 | type: pim 96 | name: The Bat! 97 | version: "4.0.18.4" 98 | - 99 | user_agent: DAVdroid/1.6.2-ose (2017/06/23; dav4android; okhttp3) Android/7.0 100 | client: 101 | type: pim 102 | name: DAVdroid 103 | version: "1.6.2" 104 | - 105 | user_agent: Mozilla/5.0 (X11; Linux x86_64; rv:10.0.12) Gecko/20130823 Firefox/10.0.11esrpre Iceape/2.7.12 106 | client: 107 | type: pim 108 | name: SeaMonkey 109 | version: "2.7.12" 110 | - 111 | user_agent: Mozilla/5.0 (X11; Linux x86_64; rv:29.0) Gecko/20100101 Firefox/29.0 SeaMonkey/2.26a1 Lightning/3.1a1 112 | client: 113 | type: pim 114 | name: SeaMonkey 115 | version: "2.26" 116 | - 117 | user_agent: Monazilla/1.00 Live5ch/1.52 Windows/10.0.17134 118 | client: 119 | type: pim 120 | name: Live5ch 121 | version: "1.52" 122 | - 123 | user_agent: Monazilla/1.00 (JaneView/1501150412) 124 | client: 125 | type: pim 126 | name: JaneView 127 | version: "" 128 | - 129 | user_agent: Monazilla/1.00 BathyScaphe/1057 Mac OS X/10.11.6 130 | client: 131 | type: pim 132 | name: BathyScaphe 133 | version: "" 134 | - 135 | user_agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Raindrop.io/5.2.0 Chrome/82.0.4048.0 Electron/9.0.0-beta.3 Safari/537.36 136 | client: 137 | type: pim 138 | name: Raindrop.io 139 | version: 5.2.0 140 | - 141 | user_agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Franz/5.4.1 Chrome/76.0.3809.146 Electron/6.0.10 Safari/537.36 142 | client: 143 | type: pim 144 | name: Franz 145 | version: 5.4.1 146 | - 147 | user_agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Mailspring/1.7.4 Chrome/69.0.3497.128 Electron/4.2.2 Safari/537.36 148 | client: 149 | type: pim 150 | name: Mailspring 151 | version: 1.7.4 152 | - 153 | user_agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Notion/2.0.8 Chrome/76.0.3809.146 Electron/6.1.5 Safari/537.36 154 | client: 155 | type: pim 156 | name: Notion 157 | version: 2.0.8 158 | - 159 | user_agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Basecamp3/2.1.0 Chrome/78.0.3904.130 Electron/7.1.5 Safari/537.36 160 | client: 161 | type: pim 162 | name: Basecamp 163 | version: 2.1.0 164 | - 165 | user_agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Evernote/10.8.5 Chrome/87.0.4280.88 Electron/11.1.1 Safari/537.36 166 | client: 167 | type: pim 168 | name: Evernote 169 | version: 10.8.5 170 | - 171 | user_agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ramboxpro/1.5.2 Chrome/83.0.4103.122 Electron/9.4.4 Safari/537.36 172 | client: 173 | type: pim 174 | name: Rambox Pro 175 | version: 1.5.2 176 | - 177 | user_agent: Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36 Mailbird/2.4.30.0/ 178 | client: 179 | type: pim 180 | name: Mailbird 181 | version: 2.4.30.0 182 | - 183 | user_agent: MacOutlook/16.46.21021202 (Intelx64 Mac OS X 11.2.2 (Build 20D80)) 184 | client: 185 | type: pim 186 | name: Microsoft Outlook 187 | version: 16.46.21021202 188 | - 189 | user_agent: WindowsMail/17.5.9600.22013 190 | client: 191 | type: pim 192 | name: Windows Mail 193 | version: 17.5.9600.22013 194 | -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/upstream/device/camera.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - 3 | user_agent: Mozilla/5.0 (Linux; U; Android 4.0; de-DE; EK-GC100 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30 4 | device: 5 | type: camera 6 | brand: Samsung 7 | model: Galaxy Camera 8 | - 9 | user_agent: Mozilla/5.0 (Linux; Android 4.1.2; EK-GC100 Build/JZO54K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.63 Mobile Safari/537.36 OPR/15.0.1162.60140 10 | device: 11 | type: camera 12 | brand: Samsung 13 | model: Galaxy Camera 14 | - 15 | user_agent: Mozilla/5.0 (Linux; U; Android 2.3.3; ja-jp; COOLPIX S800c Build/CP01_WW) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1 16 | device: 17 | type: camera 18 | brand: Nikon 19 | model: Coolpix S800c 20 | -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/upstream/device/car_browser.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - 3 | user_agent: Mozilla/5.0 (X11; u; Linux; C) AppleWebKit /533.3 (Khtml, like Gheko) QtCarBrowser Safari/533.3 4 | device: 5 | type: car browser 6 | brand: Tesla 7 | model: Model S 8 | - 9 | user_agent: Mozilla/5.0 (X11; GNU/Linux) AppleWebKit/537.36 (KHTML, like Gecko) Chromium/75.0.3770.100 Chrome/75.0.3770.100 Safari/537.36 Tesla/2019.40.50.7-ad132c7b057e 10 | device: 11 | type: car browser 12 | brand: Tesla 13 | model: "" 14 | -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/upstream/device/console.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - 3 | user_agent: Mozilla/5.0 (Linux; Android 4.1.1; ARCHOS GAMEPAD Build/JRO03H) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19 4 | device: 5 | type: console 6 | brand: Archos 7 | model: Gamepad 8 | - 9 | user_agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; Xbox) 10 | device: 11 | type: console 12 | brand: Microsoft 13 | model: Xbox 360 14 | - 15 | user_agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0; Xbox; Xbox One) 16 | device: 17 | type: console 18 | brand: Microsoft 19 | model: Xbox One 20 | - 21 | user_agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox One) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586 22 | device: 23 | type: console 24 | brand: Microsoft 25 | model: Xbox One 26 | - 27 | user_agent: Mozilla/5.0 (Nintendo 3DS; U; ; en) Version/1.7498.EU 28 | device: 29 | type: console 30 | brand: Nintendo 31 | model: 3DS 32 | - 33 | user_agent: Bunjalloo/0.7.6(Nintendo DS;U;en) 34 | device: 35 | type: console 36 | brand: Nintendo 37 | model: DS 38 | - 39 | user_agent: Opera/9.30 (Nintendo Wii; U; ; 3642; en) 40 | device: 41 | type: console 42 | brand: Nintendo 43 | model: Wii 44 | - 45 | user_agent: Mozilla/5.0 (Nintendo WiiU) AppleWebKit/534.52 (KHTML, like Gecko) NX/2.1.0.8.21 NintendoBrowser/1.0.0.7494.US 46 | device: 47 | type: console 48 | brand: Nintendo 49 | model: WiiU 50 | - 51 | user_agent: Mozilla/5.0 (Linux; U; Android OUYA 4.1.2; en-us; OUYA Build/JZO54L-OUYA) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30 52 | device: 53 | type: console 54 | brand: OUYA 55 | model: OUYA 56 | - 57 | user_agent: Mozilla/5.0 (PLAYSTATION 3 4.46) AppleWebKit/531.22.8 (KHTML, like Gecko) 58 | device: 59 | type: console 60 | brand: Sony 61 | model: PlayStation 3 62 | - 63 | user_agent: Mozilla/5.0 (PlayStation 4 1.52) AppleWebKit/536.26 (KHTML, like Gecko) 64 | device: 65 | type: console 66 | brand: Sony 67 | model: PlayStation 4 68 | - 69 | user_agent: Mozilla/4.0 (PlayStation Portable); 2.00) 70 | device: 71 | type: console 72 | brand: Sony 73 | model: PlayStation Portable 74 | - 75 | user_agent: Mozilla/5.0 (PlayStation Vita 3.01) AppleWebKit/536.26 (KHTML, like Gecko) Silk/3.2 76 | device: 77 | type: console 78 | brand: Sony 79 | model: PlayStation Vita 80 | - 81 | user_agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; XBOX_ONE_ED) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393 82 | device: 83 | type: console 84 | brand: Microsoft 85 | model: Xbox One S 86 | - 87 | user_agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox One X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36 Edge/20.02 88 | device: 89 | type: console 90 | brand: Microsoft 91 | model: Xbox One X 92 | - 93 | user_agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox Series X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36 Edge/20.02 94 | device: 95 | type: console 96 | brand: Microsoft 97 | model: Xbox Series X 98 | -------------------------------------------------------------------------------- /device_detector/tests/parser/fixtures/upstream/device/notebook.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - 3 | user_agent: 'Mozilla/5.0 (Windows NT 10.0.16299.125; osmeta 10.3.3308) AppleWebKit/602.1.1 (KHTML, like Gecko) Version/9.0 Safari/602.1.1 osmeta/10.3.3308 Build/3308 [FBAN/FBW;FBAV/140.0.0.232.179;FBBV/83145113;FBDV/WindowsDevice;FBMD/80VR;FBSN/Windows;FBSV/10.0.16299.371;FBSS/1;FBCR/;FBID/desktop;FBLC/ru_RU;FBOP/45;FBRV/0]' 4 | device: 5 | type: desktop 6 | brand: Lenovo 7 | model: Legion Y720 8 | -------------------------------------------------------------------------------- /device_detector/tests/parser/test_bot.py: -------------------------------------------------------------------------------- 1 | from ..base import ParserBaseTest 2 | from ...parser import Bot 3 | from ...utils import ua_hash 4 | 5 | 6 | class TestBot(ParserBaseTest): 7 | 8 | def test_get_info_from_ua_bot(self): 9 | ua = 'Googlebot/2.1 (http://www.googlebot.com/bot.html)' 10 | spaceless = ua.lower().replace(' ', '') 11 | bot = Bot( 12 | ua=ua, 13 | ua_hash=ua_hash(ua), 14 | ua_spaceless=spaceless, 15 | version_truncation=self.VERSION_TRUNCATION, 16 | ) 17 | expected = { 18 | 'name': 'Googlebot', 19 | 'category': 'Search bot', 20 | 'url': 'http://www.google.com/bot.html', 21 | 'producer': { 22 | 'name': 'Google Inc.', 23 | 'url': 'http://www.google.com', 24 | }, 25 | 'model': '', 26 | 'version': '', 27 | } 28 | bot = bot.parse() 29 | bot.ua_data.pop('type', None) 30 | self.assertDictEqual(expected, bot.ua_data) 31 | 32 | def test_parse_no_bot(self): 33 | ua = 'Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1; SV1; SE 2.x)' 34 | spaceless = ua.lower().replace(' ', '') 35 | bot = Bot( 36 | ua=ua, 37 | ua_hash=ua_hash(ua), 38 | ua_spaceless=spaceless, 39 | version_truncation=self.VERSION_TRUNCATION, 40 | ) 41 | bot.parse() 42 | self.assertEqual(bot.ua_data, {}) 43 | 44 | 45 | __all__ = [ 46 | 'TestBot', 47 | ] 48 | -------------------------------------------------------------------------------- /device_detector/tests/parser/test_cache.py: -------------------------------------------------------------------------------- 1 | from ..base import ParserBaseTest 2 | from ...device_detector import DeviceDetector 3 | 4 | 5 | class TestCache(ParserBaseTest): 6 | """ 7 | Values should remain the same on multiple runs when 8 | the parsing on followup runs pulls from cached values. 9 | """ 10 | 11 | def test_is_bot(self): 12 | ua = "Mozilla/5.0 (compatible; CloudFlare-AlwaysOnline/1.0; +http://www.cloudflare.com/always-online) AppleWebKit/534.34" 13 | 14 | first_run = DeviceDetector(ua).parse() 15 | self.assertTrue(first_run.is_bot()) 16 | 17 | second_run = DeviceDetector(ua).parse() 18 | self.assertTrue(second_run.is_bot()) 19 | 20 | def test_device(self): 21 | ua = "Mozilla/5.0 (Linux; U; Android 2.3.3; ja-jp; COOLPIX S800c Build/CP01_WW) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1" 22 | 23 | first_run = DeviceDetector(ua).parse() 24 | self.assertEqual(first_run.device_type(), 'camera') 25 | self.assertEqual(first_run.device_brand(), 'Nikon') 26 | 27 | second_run = DeviceDetector(ua).parse() 28 | self.assertEqual(second_run.device_type(), 'camera') 29 | self.assertEqual(second_run.device_brand(), 'Nikon') 30 | 31 | def test_client(self): 32 | ua = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.162 Safari/537.36" 33 | 34 | first_run = DeviceDetector(ua).parse() 35 | self.assertEqual(first_run.client_name(), 'Chrome') 36 | self.assertEqual(first_run.client_version(), '80.0.3987.162') 37 | 38 | second_run = DeviceDetector(ua).parse() 39 | self.assertEqual(second_run.client_name(), 'Chrome') 40 | self.assertEqual(second_run.client_version(), '80.0.3987.162') 41 | 42 | def test_os(self): 43 | ua = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:74.0) Gecko/20100101 Firefox/74.0" 44 | 45 | first_run = DeviceDetector(ua).parse() 46 | self.assertEqual(first_run.os_name(), 'Ubuntu') 47 | 48 | second_run = DeviceDetector(ua).parse() 49 | self.assertEqual(second_run.os_name(), 'Ubuntu') 50 | 51 | 52 | __all__ = [ 53 | 'TestCache', 54 | ] 55 | -------------------------------------------------------------------------------- /device_detector/tests/parser/test_clients.py: -------------------------------------------------------------------------------- 1 | from ..base import GenericParserTest, ParserBaseTest 2 | from ...parser import ( 3 | Browser, 4 | DesktopApp, 5 | DictUA, 6 | FeedReader, 7 | Game, 8 | Library, 9 | MediaPlayer, 10 | Messaging, 11 | MobileApp, 12 | P2P, 13 | PIM, 14 | NameVersionExtractor, 15 | VPNProxy, 16 | WholeNameExtractor, 17 | ) 18 | from ...utils import ua_hash 19 | 20 | 21 | class TestBrowser(ParserBaseTest): 22 | 23 | fixture_files = [ 24 | 'tests/parser/fixtures/local/client/browser.yml', 25 | 'tests/parser/fixtures/upstream/client/browser.yml', 26 | ] 27 | fields = ('name', 'type') 28 | Parser = Browser 29 | 30 | 31 | class TestDictUA(ParserBaseTest): 32 | 33 | fixture_files = [ 34 | 'tests/parser/fixtures/local/client/dictua.yml', 35 | ] 36 | fields = ('name', 'version', 'type') 37 | Parser = DictUA 38 | 39 | 40 | class TestFeedReader(ParserBaseTest): 41 | 42 | fixture_files = [ 43 | 'tests/parser/fixtures/upstream/client/feed_reader.yml', 44 | ] 45 | fields = ('name', 'type', 'version') 46 | Parser = FeedReader 47 | 48 | 49 | class TestGame(ParserBaseTest): 50 | 51 | fixture_files = [ 52 | 'tests/parser/fixtures/local/client/games.yml', 53 | ] 54 | fields = ('name', 'type', 'version') 55 | Parser = Game 56 | 57 | 58 | class TestLibrary(ParserBaseTest): 59 | 60 | fixture_files = [ 61 | 'tests/parser/fixtures/local/client/library.yml', 62 | 'tests/parser/fixtures/upstream/client/library.yml', 63 | ] 64 | fields = ('name', 'type', 'version') 65 | Parser = Library 66 | 67 | 68 | class TestMediaPlayer(ParserBaseTest): 69 | 70 | fixture_files = [ 71 | 'tests/parser/fixtures/local/client/mediaplayer.yml', 72 | 'tests/parser/fixtures/upstream/client/mediaplayer.yml', 73 | ] 74 | fields = ('name', 'type', 'version') 75 | Parser = MediaPlayer 76 | 77 | 78 | class TestMessaging(ParserBaseTest): 79 | 80 | fixture_files = [ 81 | 'tests/parser/fixtures/local/client/messaging.yml', 82 | ] 83 | fields = ('name', 'type', 'version') 84 | Parser = Messaging 85 | 86 | 87 | class TestMobileApp(ParserBaseTest): 88 | 89 | fixture_files = [ 90 | 'tests/parser/fixtures/local/client/mobile_app.yml', 91 | 'tests/parser/fixtures/upstream/client/mobile_app.yml', 92 | ] 93 | fields = ('name', 'type', 'version') 94 | Parser = MobileApp 95 | 96 | 97 | class TestP2P(ParserBaseTest): 98 | 99 | fixture_files = [ 100 | 'tests/parser/fixtures/local/client/p2p.yml', 101 | ] 102 | fields = ('name', 'type', 'version') 103 | Parser = P2P 104 | 105 | 106 | class TestPIM(ParserBaseTest): 107 | 108 | fixture_files = [ 109 | 'tests/parser/fixtures/local/client/pim.yml', 110 | 'tests/parser/fixtures/upstream/client/pim.yml', 111 | ] 112 | fields = ('name', 'type', 'version') 113 | Parser = PIM 114 | 115 | 116 | class TestDesktopApp(ParserBaseTest): 117 | 118 | fixture_files = [ 119 | 'tests/parser/fixtures/local/client/antivirus.yml', 120 | 'tests/parser/fixtures/local/client/osutility.yml', 121 | 'tests/parser/fixtures/local/client/desktop_apps.yml', 122 | ] 123 | fields = ('name', 'type', 'version') 124 | Parser = DesktopApp 125 | 126 | 127 | class TestVPNProxy(ParserBaseTest): 128 | 129 | fixture_files = [ 130 | 'tests/parser/fixtures/local/client/vpnproxy.yml', 131 | ] 132 | fields = ('name', 'type', 'version') 133 | Parser = VPNProxy 134 | 135 | 136 | class TestNameVersionExtractor(ParserBaseTest): 137 | 138 | fixture_files = [ 139 | 'tests/parser/fixtures/local/client/extractor_name_version.yml', 140 | ] 141 | fields = ('name', 'type', 'version') 142 | Parser = NameVersionExtractor 143 | skipped = ( 144 | '$(PRODUCT_NAME)/4839 CFNetwork/894 Darwin/17.4.0', 145 | 'MotionXGPSFull24.2b5063R-iOS12.0-iPhone7,2', 146 | '1530819907iOSv1.6.5', 147 | 'ISUA11.00MP', 148 | ) 149 | 150 | 151 | class TestWholeNameExtractor(GenericParserTest): 152 | 153 | fixture_files = [ 154 | 'tests/parser/fixtures/local/client/extractor_whole_name.yml', 155 | ] 156 | fields = ('name', 'type', 'version') 157 | Parser = WholeNameExtractor 158 | skipped = [ 159 | '646E514C51BFF23DBBB5B9487F142670_5_5.10.228257.2253_1_0', 160 | ] 161 | 162 | 163 | # class TestNoNameExtracted(ParserBaseTest): 164 | # 165 | # fixture_files = [ 166 | # 'tests/parser/fixtures/local/client/extractor_no_name.yml', 167 | # ] 168 | # 169 | # def test_parsing(self): 170 | # fixtures = self.load_fixtures() 171 | # fields = ('name', 'version') 172 | # extractors = (WholeNameExtractor, NameVersionExtractor) 173 | # 174 | # for fixture in fixtures: 175 | # ua = fixture.pop('user_agent') 176 | # 177 | # for extractor in extractors: 178 | # parsed = extractor(ua, ua_hash(ua), ua.lower().replace(' ', '')).parse() 179 | # 180 | # for field in fields: 181 | # self.assertNotIn(field, parsed.ua_data) 182 | 183 | __all__ = ( 184 | 'TestBrowser', 185 | 'TestDesktopApp', 186 | 'TestFeedReader', 187 | 'TestGame', 188 | 'TestLibrary', 189 | 'TestMediaPlayer', 190 | 'TestMessaging', 191 | 'TestMobileApp', 192 | 'TestP2P', 193 | 'TestPIM', 194 | 'TestVPNProxy', 195 | 'TestNameVersionExtractor', 196 | 'TestWholeNameExtractor', 197 | # 'TestNoNameExtracted', 198 | ) 199 | -------------------------------------------------------------------------------- /device_detector/tests/parser/test_device.py: -------------------------------------------------------------------------------- 1 | from urllib.parse import unquote 2 | from ..base import ParserBaseTest 3 | from ...parser import Device 4 | from ...utils import ua_hash 5 | 6 | 7 | class TestDevices(ParserBaseTest): 8 | 9 | fixture_files = [ 10 | 'tests/parser/fixtures/upstream/device/console.yml', 11 | 'tests/parser/fixtures/upstream/device/car_browser.yml', 12 | 'tests/parser/fixtures/upstream/device/camera.yml', 13 | 'tests/parser/fixtures/upstream/device/notebook.yml', 14 | ] 15 | Parser = Device 16 | 17 | def test_parse(self): 18 | fixtures = self.load_fixtures() 19 | 20 | for fixture in fixtures: 21 | self.user_agent = unquote(fixture.pop('user_agent')) 22 | expect = fixture['device'] 23 | hashed = ua_hash(self.user_agent) 24 | spaceless = self.user_agent.lower().replace(' ', '') 25 | parsed = Device(self.user_agent, hashed, spaceless, self.VERSION_TRUNCATION).parse() 26 | 27 | data = parsed.ua_data 28 | 29 | for field in ('type', 'brand', 'model'): 30 | if field not in data: 31 | continue 32 | self.assertEqual( 33 | str(expect[field]), 34 | str(data[field]), 35 | field=field, 36 | ) 37 | 38 | 39 | __all__ = [ 40 | 'TestDevices', 41 | ] 42 | -------------------------------------------------------------------------------- /device_detector/tests/parser/test_extractor.py: -------------------------------------------------------------------------------- 1 | from urllib.parse import unquote 2 | from ..base import ParserBaseTest 3 | from ...parser import ( 4 | ApplicationIDExtractor, 5 | NameExtractor, 6 | ModelExtractor, 7 | VersionExtractor, 8 | ) 9 | 10 | 11 | class TestApplicationIDExtractor(ParserBaseTest): 12 | 13 | fixture_files = [ 14 | 'tests/parser/fixtures/local/extractor/applicationid.yml', 15 | 'tests/parser/fixtures/local/extractor/app_id_override_name.yml', 16 | ] 17 | 18 | def test_parsing(self): 19 | fixtures = self.load_fixtures() 20 | error = 'Error parsing {}.\n Parsed value "{}" != expected value "{}"' 21 | 22 | for fixture in fixtures: 23 | self.user_agent = unquote(fixture.pop('user_agent')) 24 | expected = fixture['client']['app_id'] 25 | app_id = ApplicationIDExtractor(self.user_agent) 26 | parsed = app_id.extract().get('app_id', '') 27 | 28 | self.assertEqual(expected, parsed, msg=error.format(self.user_agent, parsed, expected)) 29 | 30 | expected = fixture['client']['pretty_name'] 31 | parsed = app_id.pretty_name() 32 | self.assertEqual(expected, parsed, msg=error.format(self.user_agent, parsed, expected)) 33 | 34 | 35 | class TestNameExtractor(ParserBaseTest): 36 | 37 | def test_name(self): 38 | name = NameExtractor({'name': 'Candy $1'}, ['Cane']).extract() 39 | self.assertEqual(name, 'Candy Cane') 40 | 41 | def test_multiple_substitutions(self): 42 | name = NameExtractor({'name': 'Candy $1 ($2)'}, ['Cane', 'Curve']).extract() 43 | self.assertEqual(name, 'Candy Cane (Curve)') 44 | 45 | 46 | class TestModelExtractor(ParserBaseTest): 47 | 48 | def test_underscore_substitution(self): 49 | model = ModelExtractor({'model': 'Candy_Canes'}, []).extract() 50 | self.assertEqual(model, 'Candy Canes') 51 | 52 | def test_substitutions(self): 53 | model = ModelExtractor({'model': 'Blu-ray Player (BDP$1)'}, [5600]).extract() 54 | self.assertEqual(model, 'Blu-ray Player (BDP5600)') 55 | 56 | 57 | class TestVersionExtractor(ParserBaseTest): 58 | 59 | def test_underscore_substitution(self): 60 | version = VersionExtractor({'version': '8_2'}, []).extract() 61 | self.assertEqual(version, '8.2') 62 | 63 | def test_multiple_substitutions(self): 64 | version = VersionExtractor({'version': '$1 ($2)'}, [8, 5]).extract() 65 | self.assertEqual(version, '8 (5)') 66 | 67 | def test_trailing_dot(self): 68 | version = VersionExtractor({'version': '$1'}, ['2.']).extract() 69 | self.assertEqual(version, '2') 70 | 71 | 72 | __all__ = ( 73 | 'TestApplicationIDExtractor', 74 | 'TestNameExtractor', 75 | 'TestModelExtractor', 76 | 'TestVersionExtractor', 77 | ) 78 | -------------------------------------------------------------------------------- /device_detector/tests/parser/test_key_value_pairs.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from ...parser.key_value_pairs import key_value_pairs 4 | 5 | 6 | 7 | class TestKeyValuePairs(unittest.TestCase): 8 | 9 | def test_trailing_key_space_version(self): 10 | ua = 'Mozilla/5.0 (Linux; Android 8.0.0; SM-A530W Build/R16NW; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/73.0.3683.90 Mobile Safari/537.36 Android SermonAudio.com 1.9.8' 11 | 12 | def test_2(self): 13 | ua = 'YouCam Fun/22261949 (iPhone; iOS 12.0.1; Scale/2.00)' 14 | self.assertListEqual( 15 | key_value_pairs(ua), 16 | [('youcamfun', 'YouCam Fun', '22261949')], 17 | ) 18 | 19 | def test_3(self): 20 | ua = 'Aurora HDR 2018/1.1.2 Sparkle/1.13.1' 21 | self.assertListEqual( 22 | key_value_pairs(ua), 23 | [('aurorahdr2018', 'Aurora HDR 2018', '1.1.2')], 24 | ) 25 | 26 | def test_4(self): 27 | ua = 'libreoffice 5.4.3.2 (92a7159f7e4af62137622921e809f8546db437e5; windows; x86;)' 28 | self.assertListEqual( 29 | key_value_pairs(ua), 30 | [('libreoffice', 'libreoffice', '5.4.3.2')], 31 | ) 32 | 33 | 34 | def test_5(self): 35 | ua = 'Microsoft Office Access 2013 (15.0.4693) Windows NT 6.2' 36 | self.assertListEqual( 37 | key_value_pairs(ua), 38 | [('microsoftofficeaccess2013', 'Microsoft Office Access 2013', '15.0.4693')], 39 | ) 40 | 41 | def test_with_url1(self): 42 | ua = 'DigiCal (v1.8.2b; http://digibites.nl/digical)' 43 | self.assertListEqual( 44 | key_value_pairs(ua), 45 | [('digical', 'DigiCal', '1.8.2')], 46 | ) 47 | 48 | def test_with_url2(self): 49 | ua = 'Mozilla/5.0 (Stellarium Bright Novae Plugin 0.1.3; http://stellarium.org/)' 50 | self.assertListEqual( 51 | key_value_pairs(ua), 52 | [('stellariumbrightnovaeplugin', 'Stellarium Bright Novae Plugin', '0.1.3')], 53 | ) 54 | 55 | def test_6(self): 56 | ua = 'The Economist on iPad NA/69 CFNetwork/893.14.2 Darwin/17.3.0' 57 | self.assertListEqual( 58 | key_value_pairs(ua), 59 | [('theeconomistonipadna', 'The Economist on iPad NA', '69')], 60 | ) 61 | 62 | def test_7(self): 63 | ua = 'The Columbia Bank, MD/5.11.0.42 BundleID/com.intuit.mobilebanking01368 BundleDeviceFamily/iPhone,iPad (iPhone; iPhone11,2; iPhone XS; iOS 12.1.4)' 64 | self.assertListEqual( 65 | key_value_pairs(ua), 66 | [('thecolumbiabank,md', 'The Columbia Bank, MD', '5.11.0.42'), ('bundleid', 'BundleID', 'com.intuit.mobilebanking01368'), ('bundledevicefamily', 'BundleDeviceFamily', 'iPhone')], 67 | ) 68 | 69 | def test_8(self): 70 | ua = 'Podbean/iOS (http://podbean.com) 5.0 - ba7575612c9a5554c0b8f5ffc378d1c7' 71 | self.assertListEqual( 72 | key_value_pairs(ua), 73 | [('podbean', 'Podbean', 'iOS')], 74 | ) 75 | 76 | def test_9(self): 77 | ua = 'wsdl2objc iPhone SermonAudio.com 4.4.5' 78 | self.assertListEqual( 79 | key_value_pairs(ua), 80 | [('wsdl2objciphonesermonaudio.com', 'wsdl2objc iPhone SermonAudio.com', '4.4.5')], 81 | ) 82 | 83 | def test_10(self): 84 | ua = 'AAM 1.1 rv:1.0 (iPhone; iOS 12.0; en_US)' 85 | self.assertListEqual( 86 | key_value_pairs(ua), 87 | [('aam', 'AAM', '1.1')], 88 | ) 89 | 90 | def test_11(self): 91 | ua = 'Microsoft URL Control - 6.01.9782' 92 | self.assertListEqual( 93 | key_value_pairs(ua), 94 | [('microsofturlcontrol', 'Microsoft URL Control', '6.01.9782')], 95 | ) 96 | 97 | def test_12(self): 98 | ua = '1.0,win/10.0.16299,AV/17.1.2286,avl/devcontrol/17.1.3394.0,ffl' 99 | self.assertListEqual( 100 | key_value_pairs(ua), 101 | [('av', 'AV', '17.1.2286'), ('avl', 'avl', 'devcontrol')], 102 | ) 103 | 104 | def test_13(self): 105 | ua = 'ArcGISiOS-10.2.5/10.3.2/iPad6,12' 106 | self.assertListEqual( 107 | key_value_pairs(ua), 108 | [('arcgisios-10.2.5', 'ArcGISiOS-10.2.5', '10.3.2')], 109 | ) 110 | 111 | def test_is_browser_nothing_interesting1(self): 112 | ua = 'mozilla/5.0 (windows nt 10.0; win64; x64) applewebkit/537.36 (khtml, like gecko) chrome/46.0.2486.0 safari/537.36 edge/13.10586 edge 13.0' 113 | self.assertListEqual( 114 | key_value_pairs(ua), 115 | [], 116 | ) 117 | 118 | def test_is_browser_nothing_interesting2(self): 119 | ua = 'Safari/14607.1.39 CFNetwork/978.0.6 Darwin/18.5.0 (x86_64)' 120 | self.assertListEqual( 121 | key_value_pairs(ua), 122 | [], 123 | ) 124 | 125 | def test_is_browser_nothing_interesting3(self): 126 | ua = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.88 Safari/537.36 Vivaldi/2.4.1488.35' 127 | self.assertListEqual( 128 | key_value_pairs(ua), 129 | [], 130 | ) 131 | 132 | def test_is_browser_nothing_interesting4(self): 133 | ua = 'Vivaldi/2.0.1309.37 WinSparkle/0.5.2 (Win64)' 134 | self.assertListEqual( 135 | key_value_pairs(ua), 136 | [], 137 | ) 138 | 139 | def test_is_browser_nothing_interesting5(self): 140 | ua = 'Opera/9.62 (J2ME/MIDP; Opera Mini/5.1.37933421/28.62; U; en) Presto/2.521.26 Version/11.521' 141 | self.assertListEqual( 142 | key_value_pairs(ua), 143 | [('version', 'Version', '11.521')], 144 | ) 145 | 146 | def test_is_browser_nothing_interesting6(self): 147 | ua = 'Opera/9.62 (Android 4.1.2; Linux; Opera Mobi/ADR-25672775) Presto/2.520.13 Version/12.520' 148 | self.assertListEqual( 149 | key_value_pairs(ua), 150 | [('version', 'Version', '12.520')], 151 | ) 152 | 153 | -------------------------------------------------------------------------------- /device_detector/tests/parser/test_os.py: -------------------------------------------------------------------------------- 1 | from urllib.parse import unquote 2 | from ..base import ParserBaseTest 3 | from ...parser import OS, OSFragment 4 | from ...utils import ua_hash 5 | from device_detector.device_detector import VERSION_TRUNCATION_NONE 6 | 7 | 8 | class TestOS(ParserBaseTest): 9 | 10 | fixture_files = [ 11 | 'tests/parser/fixtures/local/oss.yml', 12 | 'tests/parser/fixtures/upstream/oss.yml', 13 | ] 14 | fields = ('name', 'version') 15 | fixture_key = 'os' 16 | Parser = OS 17 | VERSION_TRUNCATION = VERSION_TRUNCATION_NONE 18 | 19 | 20 | class TestOSFragment(ParserBaseTest): 21 | 22 | fixture_files = [ 23 | 'tests/parser/fixtures/local/osfragments.yml', 24 | ] 25 | fields = ('name',) 26 | fixture_key = 'name' 27 | 28 | def test_parsing(self): 29 | fixtures = self.load_fixtures() 30 | 31 | for fixture in fixtures: 32 | self.user_agent = unquote(fixture.pop('user_agent')) 33 | hashed = ua_hash(self.user_agent) 34 | spaceless = self.user_agent.lower().replace(' ', '') 35 | expect = fixture['name'] 36 | parsed = OSFragment( 37 | self.user_agent, 38 | hashed, 39 | spaceless, 40 | self.VERSION_TRUNCATION, 41 | ).parse() 42 | 43 | self.assertEqual(expect, parsed.ua_data['name']) 44 | 45 | 46 | __all__ = ( 47 | 'TestOS', 48 | 'TestOSFragment', 49 | ) 50 | -------------------------------------------------------------------------------- /device_detector/tests/parser/test_vendorfragment.py: -------------------------------------------------------------------------------- 1 | from urllib.parse import unquote 2 | from ..base import ParserBaseTest 3 | from ...parser import VendorFragment 4 | from ...utils import ua_hash 5 | 6 | 7 | class TestVendorFragment(ParserBaseTest): 8 | 9 | fixture_files = [ 10 | 'tests/parser/fixtures/upstream/vendorfragments.yml', 11 | ] 12 | 13 | def test_parsing(self): 14 | fixtures = self.load_fixtures() 15 | 16 | for fixture in fixtures: 17 | self.user_agent = unquote(fixture.pop('useragent')) 18 | hashed = ua_hash(self.user_agent) 19 | spaceless = self.user_agent.lower().replace(' ', '') 20 | expect = fixture['vendor'] 21 | parsed = VendorFragment( 22 | self.user_agent, 23 | hashed, 24 | spaceless, 25 | self.VERSION_TRUNCATION, 26 | ).parse() 27 | self.assertEqual(expect, parsed.ua_data['brand']) 28 | 29 | 30 | __all__ = [ 31 | 'TestVendorFragment', 32 | ] 33 | -------------------------------------------------------------------------------- /device_detector/yaml_loader.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import yaml 3 | try: 4 | from yaml import CSafeLoader as SafeLoader 5 | except ImportError: 6 | from yaml import SafeLoader 7 | from pathlib import Path 8 | 9 | import device_detector 10 | from .lazy_regex import RegexLazyIgnore 11 | from .settings import BOUNDED_REGEX, DDCache, ROOT 12 | 13 | 14 | class RegexLoader: 15 | 16 | # Paths to yml files of regexes 17 | fixture_files = [] 18 | 19 | # Constant used as value for unknown browser / os 20 | UNKNOWN = 'UNK' 21 | 22 | def __init__(self, version_truncation=1): 23 | self.VERSION_TRUNCATION = version_truncation 24 | 25 | @staticmethod 26 | def load_from_yaml(yfile): 27 | """ 28 | Load yaml from regexes directory, or extract from the egg 29 | """ 30 | if Path('{}/{}'.format(ROOT, yfile)).exists(): 31 | with open('{}/{}'.format(ROOT, yfile), 'r', encoding="utf-8") as yf: 32 | return yaml.load(yf, SafeLoader) 33 | 34 | try: 35 | yfile = 'device_detector/{}'.format(yfile) 36 | return yaml.load(device_detector.__loader__.get_data(yfile), SafeLoader) 37 | except OSError: 38 | print('{} does not exist'.format(yfile)) 39 | return [] 40 | 41 | def load_app_id_sets(self, name) -> set: 42 | """ 43 | Load App IDs by key name into python set 44 | """ 45 | cache_key = 'appids_%s' % name 46 | app_ids = DDCache[cache_key] 47 | if app_ids: 48 | return app_ids 49 | 50 | app_ids = set(self.load_from_yaml('appids/%s.yml' % name)) 51 | DDCache['appids_%s' % name] = app_ids 52 | return app_ids 53 | 54 | def yaml_to_list(self, yfile) -> list: 55 | """ 56 | Override method on subclasses if yaml format varies. 57 | 58 | Load yaml file to list of dicts 59 | """ 60 | regexes = self.load_from_yaml(yfile) 61 | if isinstance(regexes, list): 62 | return regexes 63 | 64 | reg_list = [] 65 | for entry in regexes: 66 | regexes[entry]['name'] = entry 67 | reg_list.append(regexes[entry]) 68 | 69 | return reg_list 70 | 71 | @property 72 | def regex_list(self) -> list: 73 | regexes = DDCache['regexes'].get(self.cache_name, []) 74 | if regexes: 75 | return regexes 76 | 77 | for fixture in self.fixture_files: 78 | regexes.extend(self.yaml_to_list('regexes/{}'.format(fixture))) 79 | 80 | for regex in regexes: 81 | if 'regex' in regex: 82 | regex['regex'] = RegexLazyIgnore(BOUNDED_REGEX.format(regex['regex'])) 83 | for model in regex.get('models', []): 84 | model['regex'] = RegexLazyIgnore(BOUNDED_REGEX.format(model['regex'])) 85 | for version in regex.get('versions', []): 86 | version['regex'] = RegexLazyIgnore(BOUNDED_REGEX.format(version['regex'])) 87 | 88 | DDCache['regexes'][self.cache_name] = regexes 89 | 90 | return regexes 91 | 92 | @property 93 | def normalized_regex_list(self) -> list: 94 | regexes = DDCache.get('normalize_regexes', []) 95 | if regexes: 96 | return regexes 97 | 98 | for fixture in self.fixture_files: 99 | regexes.extend(self.yaml_to_list('regexes/{}'.format(fixture))) 100 | 101 | for regex in regexes: 102 | regex['regex'] = RegexLazyIgnore(regex['regex']) 103 | 104 | DDCache['normalize_regexes'] = regexes 105 | 106 | return regexes 107 | 108 | @property 109 | def appdetails_data(self) -> dict: 110 | """ 111 | Load App Details data into dictionary. 112 | 113 | General regex extracts all name/version entries of interest from the UA 114 | string, and each ParserClass will check to see if any of those names is 115 | contained in the relevant appdetails.yml file. Much faster than writing 116 | individual regexes for each app. 117 | """ 118 | appdetails = DDCache['appdetails'] 119 | if appdetails: 120 | return appdetails 121 | 122 | all_app_details = {} 123 | for fixture in ( 124 | 'appdetails/desktop_app.yml', 125 | 'appdetails/game.yml', 126 | 'appdetails/library.yml', 127 | 'appdetails/mediaplayer.yml', 128 | 'appdetails/messaging.yml', 129 | 'appdetails/mobile_app.yml', 130 | 'appdetails/p2p.yml', 131 | 'appdetails/pim.yml', 132 | 'appdetails/vpnproxy.yml', 133 | ): 134 | # Fixture file names are significant! 135 | # Normalized file name must be a "dtype" of an Client Parser class 136 | name = fixture.split('/')[-1] 137 | default_type = name[:-4].replace('_', ' ') 138 | all_app_details[default_type] = self.yaml_to_list('{}'.format(fixture)) 139 | 140 | # convert uaname value to dict key and remove spaces and add that key as well. 141 | generalized_details = defaultdict(dict) 142 | for dtype, entries in all_app_details.items(): 143 | for entry in entries: 144 | name = entry['name'] 145 | key = entry['uaname'].lower().replace(' ', '') 146 | data = { 147 | 'name': name, 148 | 'type': entry.get('type', dtype), 149 | } 150 | generalized_details[dtype][key] = data 151 | 152 | # Match airmail, airmail-android, airmail-iphone 153 | suffixes = str(entry.get('suffixes', '')).lower().replace(' ', '') 154 | for suffix in suffixes.split('|'): 155 | if not suffix: 156 | continue 157 | generalized_details[dtype]['%s%s' % (key, suffix)] = data 158 | generalized_details[dtype]['%s %s' % (key, suffix)] = data 159 | 160 | DDCache['appdetails'] = generalized_details 161 | 162 | return generalized_details 163 | 164 | def clear_cache(self): 165 | """ 166 | Helper method to clear cache on tests. 167 | """ 168 | DDCache.clear() 169 | return self 170 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyyaml 2 | regex 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import io 3 | import os 4 | import re 5 | from setuptools import setup, find_packages 6 | 7 | 8 | def get_version(): 9 | with open('device_detector/__init__.py', 'r') as f: 10 | line = f.readline() 11 | match = re.match(r'__version__ = \'([\d\.]+)\'', line) 12 | 13 | if not match: 14 | raise ImportError("Can't read the version of device_detector") 15 | 16 | version = match.group(1) 17 | return version 18 | 19 | 20 | here = os.path.abspath(os.path.dirname(__file__)) 21 | with io.open(os.path.join(here, 'README.md'), encoding='utf-8') as f: 22 | long_description = '\n' + f.read() 23 | 24 | setup( 25 | name='device_detector', 26 | version=get_version(), 27 | description="Python3 port of matomo's Device Detector", 28 | long_description=long_description, 29 | long_description_content_type='text/markdown', 30 | author='Dave Burkholder', 31 | author_email='dave@thinkwelldesigns.com', 32 | packages=find_packages(exclude=["tests"]), 33 | package_dir={'': '.'}, 34 | license='MIT', 35 | zip_safe=True, 36 | url='https://github.com/thinkwelltwd/device_detector', 37 | include_package_data=True, 38 | package_data={ 39 | '': ['*.yml'], 40 | }, 41 | install_requires=[ 42 | 'pyyaml', 43 | 'regex', 44 | ], 45 | classifiers=[ 46 | 'Environment :: Web Environment', 47 | 'Intended Audience :: Developers', 48 | 'Operating System :: OS Independent', 49 | 'License :: OSI Approved :: MIT License', 50 | 'Programming Language :: Python', 51 | 'Topic :: Internet :: WWW/HTTP', 52 | 'Topic :: Software Development :: Libraries :: Python Modules', 53 | 'Programming Language :: Python :: 3.6', 54 | 'Programming Language :: Python :: 3.7', 55 | 'Programming Language :: Python :: Implementation :: CPython', 56 | ], 57 | ) 58 | --------------------------------------------------------------------------------