diff --git a/src/client_hints.rs b/src/client_hints.rs index 07129ef..e307fb9 100644 --- a/src/client_hints.rs +++ b/src/client_hints.rs @@ -47,6 +47,7 @@ pub struct ClientHint { pub platform_version: Option, pub full_version_list: Vec<(String, String)>, pub app: Option, + pub form_factors: Vec, } impl ClientHint { @@ -59,12 +60,15 @@ impl ClientHint { let mut platform = None; let mut platform_version = None; let mut app = None; - + let mut form_factors: Vec = Vec::new(); let mut full_version_list: Vec<(String, String)> = Vec::new(); static BRAND_REGEX: Lazy = Lazy::new(|| Regex::new(r#""([^"]+)"; ?v="([^"]+)"?"#).unwrap()); + static FORM_FACTOR_REGEX: Lazy = + Lazy::new(|| Regex::new(r#"~"([a-z]+)"~i"#).unwrap()); + // println!("headers: {:?}", headers); for (header, value) in headers { let header = header.replace('_', "-").to_lowercase(); @@ -137,6 +141,14 @@ impl ClientHint { full_version_list.push((brand.to_owned(), brand_version.to_owned())); } } + + "sec-ch-ua-form-factors" => { + form_factors = FORM_FACTOR_REGEX + .captures_iter(&value) + .filter_map(|x| x.ok()?.get(1).map(|x| x.as_str().to_lowercase())) + .collect(); + } + _ => {} } } @@ -151,6 +163,7 @@ impl ClientHint { platform_version, full_version_list, app, + form_factors, }; // println!("client hints: {:?}", res); diff --git a/src/known_browsers.rs b/src/known_browsers.rs index 12400cb..ff7473b 100644 --- a/src/known_browsers.rs +++ b/src/known_browsers.rs @@ -123,6 +123,7 @@ fn available_browsers() -> HashMap { ("4A", "Acoo Browser"), ("BW", "AdBlock Browser"), ("A7", "Adult Browser"), + ("8A", "Ai Browser"), ("A9", "Airfind Secure Browser"), ("AF", "ANT Fresco"), ("AG", "ANTGalio"), @@ -140,6 +141,7 @@ fn available_browsers() -> HashMap { ("A4", "AOL Shield Pro"), ("2A", "Aplix"), ("A6", "AppBrowzer"), + ("0A", "AppTec Secure Browser"), ("AP", "APUS Browser"), ("AR", "Arora"), ("AX", "Arctic Fox"), @@ -148,6 +150,7 @@ fn available_browsers() -> HashMap { ("PN", "APN Browser"), ("6A", "Arachne"), ("RA", "Arc"), + ("R5", "Armorfly Browser"), ("AI", "Arvin"), ("AK", "Ask.com"), ("AU", "Asus Browser"), @@ -193,6 +196,7 @@ fn available_browsers() -> HashMap { ("BX", "BrowseX"), ("BZ", "Browzar"), ("B7", "Browlser"), + ("M9", "Browser Mini"), ("4B", "BrowsBit"), ("BY", "Biyubi"), ("BF", "Byffox"), @@ -237,6 +241,7 @@ fn available_browsers() -> HashMap { ("C5", "Chromium GOST"), ("CY", "Cyberfox"), ("CS", "Cheshire"), + ("8C", "Cromite"), ("RC", "Crow Browser"), ("CT", "Crusta"), ("CG", "Craving Explorer"), @@ -309,6 +314,7 @@ fn available_browsers() -> HashMap { ("FH", "Flash Browser"), ("FS", "Flast"), ("F5", "Flyperlink"), + ("F9", "FOSS Browser"), ("FU", "FreeU"), ("F6", "Freedom Browser"), ("FT", "Frost"), @@ -329,6 +335,7 @@ fn available_browsers() -> HashMap { ("G2", "GO Browser"), ("RN", "GreenBrowser"), ("HW", "Habit Browser"), + ("H7", "Halo Browser"), ("HB", "Harman Browser"), ("HS", "HasBrowser"), ("HA", "Hawk Turbo Browser"), @@ -340,6 +347,7 @@ fn available_browsers() -> HashMap { ("H4", "Holla Web Browser"), ("H5", "HotBrowser"), ("HJ", "HotJava"), + ("H6", "HONOR Browser"), ("HT", "HTC Browser"), ("HU", "Huawei Browser Mobile"), ("HP", "Huawei Browser"), @@ -364,10 +372,11 @@ fn available_browsers() -> HashMap { ("I9", "Insta Browser"), ("IE", "Internet Explorer"), ("I7", "Internet Browser Secure"), + ("5I", "Internet Webbrowser"), ("3I", "Intune Managed Browser"), ("I5", "Indian UC Mini Browser"), ("Z0", "InBrowser"), - ("IG", "Involt Go"), + ("IG", "Involta Go"), ("IM", "IE Mobile"), ("IR", "Iron"), ("JB", "Japan Browser"), @@ -380,6 +389,7 @@ fn available_browsers() -> HashMap { ("JZ", "JUZI Browser"), ("KB", "K.Browser"), ("KF", "Keepsafe Browser"), + ("K7", "KeepSolid Browser"), ("KS", "Kids Safe Browser"), ("KI", "Kindle Browser"), ("KM", "K-meleon"), @@ -404,7 +414,7 @@ fn available_browsers() -> HashMap { ("LF", "LieBaoFast"), ("LG", "LG Browser"), ("LH", "Light"), - ("L4", "lightning Browser Plus"), + ("L4", "Lightning Browser Plus"), ("L1", "Lilo"), ("LI", "Links"), ("RI", "Liri Browser"), @@ -434,7 +444,7 @@ fn available_browsers() -> HashMap { ("M3", "Midori Lite"), ("M6", "MixerBox AI"), ("MO", "Mobicip"), - ("MU", "MIUI Browser"), + ("MU", "Mi Browser"), ("MS", "Mobile Silk"), ("MK", "Mogok Browser"), ("M8", "Motorola Internet Browser"), @@ -443,6 +453,7 @@ fn available_browsers() -> HashMap { ("MX", "Maxthon"), ("M4", "MaxTube Browser"), ("MA", "Maelstrom"), + ("3M", "Mises"), ("MM", "Mmx Browser"), ("NM", "MxNitro"), ("MY", "Mypal"), @@ -467,6 +478,7 @@ fn available_browsers() -> HashMap { ("NP", "NetPositive"), ("NS", "Netscape"), ("WR", "NextWord Browser"), + ("N8", "Ninesky"), ("NT", "NTENT Browser"), ("NU", "Nuanti Meta"), ("NI", "Nuviu"), @@ -485,6 +497,7 @@ fn available_browsers() -> HashMap { ("OL", "OnBrowser Lite"), ("OE", "ONE Browser"), ("N4", "Onion Browser"), + ("1N", "ONIONBrowser"), ("Y1", "Opera Crypto"), ("OX", "Opera GX"), ("OG", "Opera Neon"), @@ -506,10 +519,12 @@ fn available_browsers() -> HashMap { ("O4", "Open Browser"), ("4U", "Open Browser 4U"), ("5G", "Open Browser fast 5G"), + ("5O", "Open Browser Lite"), ("O7", "Open TV Browser"), ("OW", "OmniWeb"), ("OT", "Otter Browser"), ("4O", "Owl Browser"), + ("JR", "OJR Browser"), ("PL", "Palm Blazer"), ("PM", "Pale Moon"), ("PY", "Polypane"), @@ -517,17 +532,22 @@ fn available_browsers() -> HashMap { ("PP", "Oppo Browser"), ("P6", "Opus Browser"), ("PR", "Palm Pre"), - ("PU", "Puffin"), + ("7I", "Puffin Cloud Browser"), + ("6I", "Puffin Incognito Browser"), + ("PU", "Puffin Secure Browser"), ("2P", "Puffin Web Browser"), ("PW", "Palm WebPro"), ("PA", "Palmscape"), ("P7", "Pawxy"), + ("0P", "Peach Browser"), ("PE", "Perfect Browser"), + ("K6", "Perk"), ("P1", "Phantom.me"), ("PH", "Phantom Browser"), ("PX", "Phoenix"), ("PB", "Phoenix Browser"), ("5P", "Photon"), + ("N9", "Pintar Browser"), ("P9", "PirateBrowser"), ("P8", "PICO Browser"), ("PF", "PlayFree Browser"), @@ -535,12 +555,17 @@ fn available_browsers() -> HashMap { ("PO", "Polaris"), ("PT", "Polarity"), ("LY", "PolyBrowser"), + ("9P", "Presearch"), + ("BP", "Privacy Browser"), ("PI", "PrivacyWall"), ("P4", "Privacy Explorer Fast Safe"), + ("X5", "Privacy Pioneer Browser"), ("P3", "Private Internet Browser"), ("P5", "Proxy Browser"), ("7P", "Proxyium"), ("6P", "Proxynet"), + ("2F", "ProxyFox"), + ("2M", "ProxyMax"), ("P2", "Pi Browser"), ("P0", "PronHub Browser"), ("PC", "PSI Secure Browser"), @@ -560,10 +585,12 @@ fn available_browsers() -> HashMap { ("QU", "Quark"), ("QZ", "QupZilla"), ("QM", "Qwant Mobile"), + ("Q5", "QtWeb"), ("QW", "QtWebEngine"), ("R3", "Rakuten Browser"), ("R4", "Rakuten Web Search"), ("R2", "Raspbian Chromium"), + ("RT", "RCA Tor Explorer"), ("RE", "Realme Browser"), ("RK", "Rekonq"), ("RM", "RockMelt"), @@ -611,6 +638,7 @@ fn available_browsers() -> HashMap { ("LE", "Smart Lenovo Browser"), ("OZ", "Smooz"), ("SN", "Snowshoe"), + ("K5", "Spark"), ("B1", "Spectre Browser"), ("S2", "Splash"), ("SI", "Sputnik Browser"), @@ -648,10 +676,14 @@ fn available_browsers() -> HashMap { ("TI", "Tint Browser"), ("TL", "TrueLocation Browser"), ("TC", "TUC Mini Browser"), + ("TK", "TUSK"), ("TU", "Tungsten"), ("TG", "ToGate"), + ("T3", "Total Browser"), + ("TQ", "TQ Browser"), ("TS", "TweakStyle"), ("TV", "TV Bro"), + ("T4", "TV-Browser Internet"), ("U0", "U Browser"), ("UB", "UBrowser"), ("UC", "UC Browser"), @@ -666,6 +698,7 @@ fn available_browsers() -> HashMap { ("V0", "vBrowser"), ("VA", "Vast Browser"), ("V3", "VD Browser"), + ("VR", "Veera"), ("VE", "Venus Browser"), ("WD", "Vewd Browser"), ("V5", "VibeMate"), @@ -702,6 +735,7 @@ fn available_browsers() -> HashMap { ("YG", "YAGI"), ("YJ", "Yahoo! Japan Browser"), ("YA", "Yandex Browser"), + ("Y4", "Yandex Browser Corp"), ("YL", "Yandex Browser Lite"), ("YN", "Yaani Browser"), ("Y2", "Yo Browser"), @@ -710,6 +744,7 @@ fn available_browsers() -> HashMap { ("Y3", "YouBrowser"), ("YZ", "Yuzu Browser"), ("XR", "xBrowser"), + ("X3", "MMBOX XBrowser"), ("XB", "X Browser Lite"), ("X0", "X-VPN"), ("X1", "xBrowser Pro Super Fast"), @@ -717,6 +752,7 @@ fn available_browsers() -> HashMap { ("XT", "XtremeCast"), ("XS", "xStand"), ("XI", "Xiino"), + ("X4", "XnBrowse"), ("XO", "Xooloo Internet"), ("XV", "Xvast"), ("ZE", "Zetakey"), @@ -732,9 +768,9 @@ fn available_browsers() -> HashMap { fn browser_families() -> HashMap> { [ - ("Android Browser", vec!["AN", "MU"]), + ("Android Browser", vec!["AN"]), ("BlackBerry Browser", vec!["BB"]), - ("Baidu", vec!["BD", "BS"]), + ("Baidu", vec!["BD", "BS", "H6"]), ("Amiga", vec!["AV", "AW"]), ( "Chrome", @@ -757,7 +793,9 @@ fn browser_families() -> HashMap> { "V4", "H4", "1T", "M5", "0S", "0C", "ZR", "D6", "F6", "RC", "WD", "P3", "FT", "A9", "X2", "N3", "GD", "O9", "Q3", "F7", "K2", "P5", "H5", "V3", "K3", "Q4", "G2", "R2", "WX", "XP", "3I", "BG", "R0", "JO", "OL", "GN", "W4", "QI", "E1", "RI", "8B", "5B", - "K4", "WK", + "K4", "WK", "T3", "K5", "MU", "9P", "K6", "VR", "N9", "M9", "F9", "0P", "0A", "JR", + "D3", "TK", "BP", "2F", "2M", "K7", "1N", "8A", "H7", "X3", "T4", "X4", "5O", "8C", + "3M", "6I", "2P", "PU", "7I", "X5", "AL", ], ), ( @@ -765,8 +803,8 @@ fn browser_families() -> HashMap> { vec![ "FF", "BI", "BF", "BH", "BN", "C0", "CU", "EI", "F1", "FB", "FE", "AX", "FM", "FR", "FY", "GZ", "I4", "IF", "IW", "LH", "LY", "MB", "MN", "MO", "MY", "OA", "OS", "PI", - "PX", "QA", "S5", "SX", "TF", "TO", "WF", "ZV", "FP", "AD", "WL", "2I", "P9", "KJ", - "WY", "VK", "W5", "7C", "N7", "W7", "8P", + "PX", "QA", "S5", "SX", "TF", "TO", "WF", "ZV", "FP", "AD", "2I", "P9", "KJ", "WY", + "VK", "W5", "7C", "N7", "W7", "8P", ], ), ( @@ -801,9 +839,9 @@ fn browser_families() -> HashMap> { pub fn mobile_only_browsers() -> HashSet { [ - "36", "AH", "AI", "BL", "C1", "C4", "CB", "CW", "DB", "DD", "DT", "EU", "EZ", "FK", "FM", + "36", "AH", "AI", "BL", "C1", "C4", "CB", "CW", "DB", "3M", "DT", "EU", "EZ", "FK", "FM", "FR", "FX", "GH", "GI", "GR", "HA", "HU", "IV", "JB", "KD", "M1", "MF", "MN", "MZ", "NX", - "OC", "OI", "OM", "OZ", "PU", "PI", "PE", "QU", "RE", "S0", "S7", "SA", "SB", "SG", "SK", + "OC", "OI", "OM", "OZ", "2P", "PI", "PE", "QU", "RE", "S0", "S7", "SA", "SB", "SG", "SK", "ST", "SU", "T1", "UH", "UM", "UT", "VE", "VV", "WI", "WP", "YN", "IO", "IS", "HQ", "RW", "HI", "PN", "BW", "YO", "PK", "MR", "AP", "AK", "UI", "SD", "VN", "4S", "RF", "LR", "SQ", "BV", "L1", "F0", "KS", "V0", "C8", "AZ", "MM", "BT", "N0", "P0", "F3", "DU", "D0", "P1", @@ -811,9 +849,10 @@ pub fn mobile_only_browsers() -> HashSet { "YG", "WR", "NA", "DM", "1M", "A7", "XN", "XT", "XB", "W1", "HT", "B7", "B9", "T0", "I8", "O6", "P7", "O8", "4B", "A8", "P8", "1W", "EV", "Z0", "I9", "V4", "H4", "M5", "0S", "0C", "ZR", "D6", "F6", "P3", "FT", "A9", "X2", "NI", "FG", "TH", "N3", "GD", "O9", "Q3", "F7", - "K2", "N4", "P5", "H5", "V3", "G2", "BG", "OL", "II", "TL", "M6", "Y3", "M7", "GN", "D3", + "K2", "N4", "P5", "H5", "V3", "G2", "BG", "OL", "II", "TL", "M6", "Y3", "M7", "GN", "JR", "IG", "HW", "4O", "OU", "5P", "KE", "5A", "TT", "6P", "G3", "7P", "VU", "F8", "L4", "DK", - "DP", "KL", "K4", "N6", "KU", "WK", "M8", "UP", "ZT", + "DP", "KL", "K4", "N6", "KU", "WK", "M8", "UP", "ZT", "9P", "N8", "VR", "N9", "M9", "F9", + "0P", "0A", "2F", "2M", "K7", "1N", "8A", "H7", "X3", "X4", "5O", "6I", "7I", "X5", ] .into_iter() .map(|f| f.to_owned()) diff --git a/src/known_oss.rs b/src/known_oss.rs index 984e132..5743ef5 100644 --- a/src/known_oss.rs +++ b/src/known_oss.rs @@ -81,12 +81,14 @@ fn available_operating_systems() -> HashMap { ("ARL", "Arch Linux"), ("AOS", "AOSC OS"), ("ASP", "ASPLinux"), + ("AZU", "Azure Linux"), ("BTR", "BackTrack"), ("SBA", "Bada"), ("BEO", "BeOS"), ("BYI", "Baidu Yi"), ("BLB", "BlackBerry OS"), ("QNX", "BlackBerry Tablet OS"), + ("PAN", "blackPanther OS"), ("BOS", "Bliss OS"), ("BMP", "Brew"), ("BSN", "BrightSignOS"), @@ -183,6 +185,7 @@ fn available_operating_systems() -> HashMap { ("PSP", "PlayStation Portable"), ("PS3", "PlayStation"), ("PVE", "Proxmox VE"), + ("PUF", "Puffin OS"), ("PUR", "PureOS"), ("QTP", "Qtopia"), ("PIO", "Raspberry Pi OS"), @@ -191,6 +194,7 @@ fn available_operating_systems() -> HashMap { ("RST", "Red Star"), ("RED", "RedOS"), ("REV", "Revenge OS"), + ("RIS", "risingOS"), ("ROS", "RISC OS"), ("ROC", "Rocky Linux"), ("ROK", "Roku OS"), @@ -226,6 +230,7 @@ fn available_operating_systems() -> HashMap { ("ULT", "ULTRIX"), ("UOS", "UOS"), ("VID", "VIDAA"), + ("VIZ", "ViziOS"), ("WAS", "watchOS"), ("WER", "Wear OS"), ("WTV", "WebTV"), @@ -258,7 +263,7 @@ fn os_families() -> HashMap> { "Android", vec![ "AND", "CYN", "FIR", "REM", "RZD", "MLD", "MCD", "YNS", "GRI", "HAR", "ADR", "CLR", - "BOS", "REV", "LEN", "SIR", "RRS", "WER", "PIC", "ARM", "HEL", "BYI", + "BOS", "REV", "LEN", "SIR", "RRS", "WER", "PIC", "ARM", "HEL", "BYI", "RIS", "PUF", ], ), ("AmigaOS", vec!["AMG", "MOR", "ARO"]), @@ -281,7 +286,8 @@ fn os_families() -> HashMap> { "PUR", "PLA", "FUC", "PAR", "FOR", "MON", "KAN", "ZEN", "LND", "LNS", "CHN", "AMZ", "TEN", "CST", "NOV", "ROU", "ZOR", "RED", "KAL", "ORA", "VID", "TIV", "BSN", "RAS", "UOS", "PIO", "FRI", "LIR", "WEB", "SER", "ASP", "AOS", "LOO", "EUL", "SCI", "ALP", - "CLO", "ROC", "OVZ", "PVE", "RST", "EZX", "GNS", "JOL", "TUR", "QTP", "WPO", + "CLO", "ROC", "OVZ", "PVE", "RST", "EZX", "GNS", "JOL", "TUR", "QTP", "WPO", "PAN", + "VIZ", "AZU", ], ), ("Mac", vec!["MAC"]), @@ -302,7 +308,10 @@ fn os_families() -> HashMap> { ), ("WebTV", vec!["WTV"]), ("Windows", vec!["WIN"]), - ("Windows Mobile", vec!["WPH", "WMO", "WCE", "WRT", "WIO", "KIN"]), + ( + "Windows Mobile", + vec!["WPH", "WMO", "WCE", "WRT", "WIO", "KIN"], + ), ("Other Smart TV", vec!["WHS"]), ] .into_iter() diff --git a/src/parsers/client/browsers.rs b/src/parsers/client/browsers.rs index fa00187..472f8ef 100644 --- a/src/parsers/client/browsers.rs +++ b/src/parsers/client/browsers.rs @@ -241,6 +241,8 @@ pub fn lookup(ua: &str, client_hints: Option<&ClientHint>) -> Result) -> Result = static_user_agent_match!(r#"Opera Tablet"#); + static PUFFIN_DESKTOP: Lazy = static_user_agent_match!(r#"Puffin/(?:\d+[.\d]+)[LMW]D"#); + static PUFFIN_SMARTPHONE: Lazy = + static_user_agent_match!(r#"Puffin/(?:\d+[.\d]+)[AIFLW]P"#); + static PUFFIN_TABLET: Lazy = static_user_agent_match!(r#"Puffin/(?:\d+[.\d]+)[AILW]T"#); + if device.device_type.is_none() && ANDROID_VR.is_match(&ua)? { device.device_type = Some(DeviceType::Wearable); } @@ -353,6 +358,18 @@ pub fn lookup( device.device_type = Some(DeviceType::FeaturePhone); } + if device.device_type.is_none() && PUFFIN_DESKTOP.is_match(&ua)? { + device.device_type = Some(DeviceType::Desktop); + } + + if device.device_type.is_none() && PUFFIN_SMARTPHONE.is_match(&ua)? { + device.device_type = Some(DeviceType::SmartPhone); + } + + if device.device_type.is_none() && PUFFIN_TABLET.is_match(&ua)? { + device.device_type = Some(DeviceType::Tablet); + } + if device.device_type.is_none() { if os.name == "Windows RT" { device.device_type = Some(DeviceType::Tablet); @@ -370,8 +387,9 @@ pub fn lookup( } static OPERA: Lazy = static_user_agent_match!(r#"Opera TV Store| OMI/"#); - static ANDR0ID: Lazy = - static_user_agent_match!(r#"Andr0id|(?:Android(?: UHD)?|Google) TV|\(lite\) TV|BRAVIA'"#); + static ANDR0ID: Lazy = static_user_agent_match!( + r#"Andr0id|(?:Android(?: UHD)?|Google) TV|\(lite\) TV|BRAVIA| TV$"# + ); static TIZEN: Lazy = static_user_agent_match!(r#"SmartTV|Tizen.+ TV .+$"#); static GENERIC_TV: Lazy = static_user_agent_match!(r#"\(TV;"#); @@ -657,7 +675,7 @@ impl DeviceEntry { // we can just fix the most common case here to side step the // issue 99.999% of the time. if model.model.contains("$10") { - model.model = model.model.replace("$1", "${1}"); + model.model = model.model.replace("$1", "${1}"); } captures.expand(&model.model, &mut m); diff --git a/src/parsers/oss.rs b/src/parsers/oss.rs index 703df5a..884d345 100644 --- a/src/parsers/oss.rs +++ b/src/parsers/oss.rs @@ -308,8 +308,9 @@ fn parse_platform(ua: &str, client_hints: Option<&ClientHint>) -> Result = - static_user_agent_match!("arm[ _;)ev]|.*arm$|.*arm64|aarch64|Apple ?TV|Watch ?OS|Watch1,[12]"); + static ARM_REG: Lazy = static_user_agent_match!( + "arm[ _;)ev]|.*arm$|.*arm64|aarch64|Apple ?TV|Watch ?OS|Watch1,[12]" + ); static LONGARCH64_REG: Lazy = static_user_agent_match!("loongarch64"); static MIPS_REG: Lazy = static_user_agent_match!("mips"); static SH4_REG: Lazy = static_user_agent_match!("sh4");