useragent-0.16.11/0000755000004100000410000000000014761246733013722 5ustar www-datawww-datauseragent-0.16.11/lib/0000755000004100000410000000000014761246733014470 5ustar www-datawww-datauseragent-0.16.11/lib/useragent.rb0000644000004100000410000000002514761246733017007 0ustar www-datawww-datarequire 'user_agent' useragent-0.16.11/lib/user_agent/0000755000004100000410000000000014761246733016624 5ustar www-datawww-datauseragent-0.16.11/lib/user_agent/comparable.rb0000644000004100000410000000077614761246733021270 0ustar www-datawww-dataclass UserAgent # A custom Comparable module that will always return false # if the <=> returns false module Comparable def <(other) (c = self <=> other) ? c == -1 : false end def <=(other) (c = self <=> other) ? c == -1 || c == 0 : false end def ==(other) (c = self <=> other) ? c == 0 : false end def >(other) (c = self <=> other) ? c == 1 : false end def >=(other) (c = self <=> other) ? c == 1 || c == 0 : false end end end useragent-0.16.11/lib/user_agent/browsers/0000755000004100000410000000000014761246733020472 5ustar www-datawww-datauseragent-0.16.11/lib/user_agent/browsers/libavformat.rb0000644000004100000410000000155414761246733023332 0ustar www-datawww-dataclass UserAgent module Browsers # The user agent utilized by ffmpeg or other projects utilizing libavformat class Libavformat < Base def self.extend?(agent) agent.detect do |useragent| useragent.product == "Lavf" || (useragent.product == "NSPlayer" && agent.version == "4.1.0.3856") end end # @return ["libavformat"] To make it easy to pick it out, all of the UAs that Lavf uses have this browser. def browser "libavformat" end # @return [nil, Version] If the product is NSPlayer, we don't have a version def version super unless detect_product("NSPlayer") end # @return [nil] Lavf doesn't return us anything here def os nil end # @return [nil] Lavf doesn't return us anything here def platform nil end end end enduseragent-0.16.11/lib/user_agent/browsers/podcast_addict.rb0000644000004100000410000000637614761246733024000 0ustar www-datawww-dataclass UserAgent module Browsers # Podcast Addict - Dalvik/1.6.0 (Linux; U; Android 4.4.2; LG-D631 Build/KOT49I.D63110b) # Podcast Addict - Dalvik/2.1.0 (Linux; U; Android 5.1; XT1093 Build/LPE23.32-21.3) # Podcast Addict - Mozilla/5.0 (Linux; U; Android 4.2.2; en-us; ALCATEL ONE TOUCH Fierce Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.2 Mobile Safari/534.30 # Podcast Addict - Mozilla/5.0 (Linux; U; Android 4.2.2; en-ca; ALCATEL ONE TOUCH 6040A Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.2 Mobile Safari/534.30 # Podcast Addict - Dalvik/2.1.0 (Linux; U; Android M Build/MPZ79M) class PodcastAddict < Base def self.extend?(agent) agent.length >= 3 && agent[0].product == 'Podcast' && agent[1].product == 'Addict' && agent[2].product == '-' end def browser 'Podcast Addict' end # If we can figure out the device, return it. # # @return [nil, String] the device model def device return nil unless length >= 4 return nil unless self[3].comment.last.include?(' Build/') self[3].comment.last.split(' Build/').first end # If we can figure out the device build, return it. # # @return [nil, String] the device build def device_build return nil unless length >= 4 return nil unless self[3].comment.last.include?(' Build/') self[3].comment.last.split(' Build/').last end # Returns the localization, if known. We currently only know this for certain devices. # # @return [nil, String] the localization def localization return nil unless length >= 4 return nil unless self[3].comment.last.include?('ALCATEL ') return nil unless self[3].comment.length >= 5 self[3].comment[3] end # This is a mobile app, always return true. # # @return [true] def mobile? true end # Gets the operating system (some variant of Android, if we're certain that is the case) # # @return [nil, String] the operating system def os return nil unless length >= 4 # comment[0] = 'Linux' # comment[1] = 'U' # comment[2] = 'Android x.y.z' except when there are only 3 tokens, then we don't know the version if (self[3].product == 'Dalvik' || self[3].product == 'Mozilla') && self[3].comment.length > 3 self[3].comment[2] elsif (self[3].product == 'Dalvik' || self[3].product == 'Mozilla') && self[3].comment.length == 3 'Android' else nil end end # Gets the platform (Android, if we're certain) # # @return [nil, "Android"] the platform def platform if os.include?('Android') 'Android' else nil end end # Get the security level reported # # @return [:weak, :strong, :none] the security level def security return nil unless length >= 4 return nil unless self[3].product == 'Dalvik' || self[3].product == 'Mozilla' Security[self[3].comment[1]] end # We aren't provided with the version :( # # @return [nil] def version nil end end end enduseragent-0.16.11/lib/user_agent/browsers/vivaldi.rb0000644000004100000410000000264614761246733022465 0ustar www-datawww-dataclass UserAgent module Browsers class Vivaldi < Base def self.extend?(agent) agent.detect { |useragent| useragent.product == 'Vivaldi' } end def browser 'Vivaldi' end def build webkit.version end def version last.version end def application self.reject { |agent| agent.comment.nil? || agent.comment.empty? }.first end def platform return unless application if application.comment[0] =~ /Windows/ 'Windows' elsif application.comment.any? { |c| c =~ /CrOS/ } 'ChromeOS' elsif application.comment.any? { |c| c =~ /Android/ } 'Android' else application.comment[0] end end def webkit detect_product("AppleWebKit") end def os return unless application if application.comment[0] =~ /Windows NT/ OperatingSystems.normalize_os(application.comment[0]) elsif application.comment[2].nil? OperatingSystems.normalize_os(application.comment[1]) elsif application.comment[1] =~ /Android/ OperatingSystems.normalize_os(application.comment[1]) else OperatingSystems.normalize_os(application.comment[2]) end end def localization return unless application application.comment[3] end end end end useragent-0.16.11/lib/user_agent/browsers/playstation.rb0000644000004100000410000000507414761246733023374 0ustar www-datawww-dataclass UserAgent module Browsers # Mozilla/5.0 (PLAYSTATION 3 4.75) AppleWebKit/531.22.8 (KHTML, like Gecko) # Mozilla/5.0 (PLAYSTATION 3 4.76) AppleWebKit/531.22.8 (KHTML, like Gecko) # Mozilla/5.0 (PLAYSTATION 3; 1.00) # Mozilla/5.0 (PlayStation Vita 3.52) AppleWebKit/537.73 (KHTML, like Gecko) Silk/3.2 # Mozilla/5.0 (PlayStation 4 2.57) AppleWebKit/537.73 (KHTML, like Gecko) class PlayStation < Base def self.extend?(agent) !agent.application.nil? && !agent.application.comment.nil? && agent.application.comment.any? && ( agent.application.comment.first.include?('PLAYSTATION 3') || agent.application.comment.first.include?('PlayStation Vita') || agent.application.comment.first.include?('PlayStation 4') ) end # Returns the name of the browser in use. # # @return [nil, String] the name of the browser def browser if application.comment.first.include?('PLAYSTATION 3') 'PS3 Internet Browser' elsif last.product == 'Silk' 'Silk' elsif application.comment.first.include?('PlayStation 4') 'PS4 Internet Browser' else nil end end # PS Vita is mobile, others are not. # # @return [true, false] is this a mobile browser? def mobile? platform == 'PlayStation Vita' end # Returns the operating system in use. # # @return [String] the operating system in use def os application.comment.join(' ') end # Returns the platform in use. # # @return [nil, "PlayStation 3", "PlayStation 4", "PlayStation Vita"] the platform in use def platform if os.include?('PLAYSTATION 3') 'PlayStation 3' elsif os.include?('PlayStation 4') 'PlayStation 4' elsif os.include?('PlayStation Vita') 'PlayStation Vita' else nil end end # Returns the browser version in use. If Silk, returns the version of Silk. # Otherwise, returns the PS3/PS4 firmware version. # # @return [nil, Version] the version def version if browser == 'Silk' last.version elsif platform == 'PlayStation 3' Version.new(os.split('PLAYSTATION 3 ').last) elsif platform == 'PlayStation 4' Version.new(os.split('PlayStation 4 ').last) elsif platform == 'PlayStation Vita' Version.new(os.split('PlayStation Vita ').last) else nil end end end end end useragent-0.16.11/lib/user_agent/browsers/gecko.rb0000644000004100000410000000257714761246733022122 0ustar www-datawww-dataclass UserAgent module Browsers class Gecko < Base def self.extend?(agent) agent.application && agent.application.product == "Mozilla" end GeckoBrowsers = %w( PaleMoon Firefox Camino Iceweasel Seamonkey ).freeze def browser GeckoBrowsers.detect { |browser| respond_to?(browser) } || super end def version v = send(browser).version v.nil? ? super : v end def platform if comment = application.comment if comment[0] == 'compatible' || comment[0] == 'Mobile' nil elsif /^Windows / =~ comment[0] 'Windows' else comment[0] end end end def security Security[application.comment[1]] || :strong end def os if comment = application.comment i = if comment[1] == 'U' 2 elsif /^Windows / =~ comment[0] || /^Android/ =~ comment[0] 0 elsif comment[0] == 'Mobile' nil else 1 end return nil if i.nil? OperatingSystems.normalize_os(comment[i]) end end def localization if comment = application.comment comment[3] end end end end end useragent-0.16.11/lib/user_agent/browsers/base.rb0000644000004100000410000000616414761246733021740 0ustar www-datawww-dataclass UserAgent module Browsers class Base < Array include Comparable def <=>(other) if respond_to?(:browser) && other.respond_to?(:browser) && browser == other.browser version <=> Version.new(other.version) else false end end def eql?(other) self == other end def to_s to_str end def to_str join(" ") end def application first end def browser application && application.product end def version application && application.version end def platform nil end def os nil end def respond_to?(symbol, include_all = false) detect_product(symbol) ? true : super end def method_missing(method, *args, &block) detect_product(method) || super end def mobile? if detect_product('Mobile') || detect_comment('Mobile') true elsif os =~ /Android/ true elsif application && application.detect_comment { |c| c =~ /^IEMobile/ } true else false end end def bot? # If UA has no application type, its probably generated by a # shitty bot. if application.nil? true # Match common case when bots refer to themselves as bots in # the application comment. There are no standards for how bots # should call themselves so its not an exhaustive method. # # If you want to expand the scope, override the method and # provide your own regexp. Any patches to future extend this # list will be rejected. elsif detect_comment_match(/bot/i) true # Google PageSpeed Insights adds "Chrome-Lighthouse" to the user agent # https://stackoverflow.com/questions/16403295/what-is-the-name-of-the-google-pagespeed-user-agent elsif detect_product("Chrome-Lighthouse") true elsif product = application.product product.include?('bot') else false end end def to_h return unless application hash = { :browser => browser, :platform => platform, :os => os, :mobile => mobile?, :bot => bot?, } if version hash[:version] = version.to_a else hash[:version] = nil end if comment = application.comment hash[:comment] = comment.dup else hash[:comment] = nil end hash end private def detect_product(product) detect { |useragent| useragent.product.to_s.downcase == product.to_s.downcase } end def detect_comment(comment) detect { |useragent| useragent.detect_comment { |c| c == comment } } end def detect_comment_match(regexp) comment_match = nil detect { |useragent| useragent.detect_comment { |c| comment_match = c.match(regexp) } } comment_match end end end end useragent-0.16.11/lib/user_agent/browsers/wechat_browser.rb0000644000004100000410000000224014761246733024033 0ustar www-datawww-dataclass UserAgent module Browsers class WechatBrowser < Base def self.extend?(agent) agent.detect { |useragent| useragent.product =~ /MicroMessenger/i } end def browser 'Wechat Browser' end def version micro_messenger = detect_product("MicroMessenger") Version.new(micro_messenger.version) end def platform return unless application && application.comment if application.comment[0] =~ /iPhone/ 'iPhone' elsif application.comment.any? { |c| c =~ /Android/ } 'Android' else application.comment[0] end end def os return unless application && application.comment if application.comment[0] =~ /Windows NT/ OperatingSystems.normalize_os(application.comment[0]) elsif application.comment[2].nil? OperatingSystems.normalize_os(application.comment[1]) elsif application.comment[1] =~ /Android/ OperatingSystems.normalize_os(application.comment[1]) else OperatingSystems.normalize_os(application.comment[2]) end end end end end useragent-0.16.11/lib/user_agent/browsers/chrome.rb0000644000004100000410000000342714761246733022302 0ustar www-datawww-dataclass UserAgent module Browsers class Chrome < Base def self.extend?(agent) agent.detect { |useragent| %w(Chrome CriOS).include?(useragent.product) } end ChromeBrowsers = %w( Iron ).freeze def browser ChromeBrowsers.detect { |browser| respond_to?(browser) } || 'Chrome' end def build webkit.version end # Prior to Safari 3, the user agent did not include a version number def version str = if detect_product("CriOs") crios.version else chrome.version end Version.new(str) end def application self.reject { |agent| agent.comment.nil? || agent.comment.empty? }.first end def platform return unless application if application.comment[0] =~ /Windows/ 'Windows' elsif application.comment.any? { |c| c =~ /CrOS/ } 'ChromeOS' elsif application.comment.any? { |c| c =~ /Android/ } 'Android' else application.comment[0] end end def webkit detect { |useragent| useragent.product == "AppleWebKit" } end def os return unless application if application.comment[0] =~ /Windows NT/ OperatingSystems.normalize_os(application.comment[0]) elsif application.comment[2].nil? OperatingSystems.normalize_os(application.comment[1]) elsif application.comment[1] =~ /Android/ OperatingSystems.normalize_os(application.comment[1]) else OperatingSystems.normalize_os(application.comment[2]) end end def localization return unless application application.comment[3] end end end end useragent-0.16.11/lib/user_agent/browsers/itunes.rb0000644000004100000410000000417514761246733022335 0ustar www-datawww-dataclass UserAgent module Browsers # The user agent for iTunes # # Some user agents: # iTunes/10.6.1 (Macintosh; Intel Mac OS X 10.7.3) AppleWebKit/534.53.11 # iTunes/12.0.1 (Macintosh; OS X 10.10) AppleWebKit/0600.1.25 # iTunes/11.1.5 (Windows; Microsoft Windows 7 x64 Business Edition Service Pack 1 (Build 7601)) AppleWebKit/537.60.15 # iTunes/12.0.1 (Windows; Microsoft Windows 8 x64 Home Premium Edition (Build 9200)) AppleWebKit/7600.1017.0.24 # iTunes/12.0.1 (Macintosh; OS X 10.10.1) AppleWebKit/0600.1.25 class ITunes < Webkit def self.extend?(agent) agent.detect { |useragent| useragent.product == "iTunes" } end # @return ["iTunes"] Always return iTunes as the browser def browser "iTunes" end # @return [Version] The version of iTunes in use def version self.iTunes.version end # @return [nil] nil - not included in the user agent def security nil end # @return [nil, Version] The WebKit version in use if we have it def build super if webkit end # Parses the operating system in use. # # @return [String] The operating system def os full_os = self.full_os if application && application.comment[0] =~ /Windows/ if full_os =~ /Windows 8\.1/ "Windows 8.1" elsif full_os =~ /Windows 8/ "Windows 8" elsif full_os =~ /Windows 7/ "Windows 7" elsif full_os =~ /Windows Vista/ "Windows Vista" elsif full_os =~ /Windows XP/ "Windows XP" else "Windows" end else super end end # Parses the operating system in use. # # @return [String] The operating system def full_os if application && application.comment && application.comment.length > 1 full_os = application.comment[1] full_os = "#{full_os})" if full_os =~ /\(Build [0-9][0-9][0-9][0-9]\z/ # The regex chops the ) off :( full_os end end end end end useragent-0.16.11/lib/user_agent/browsers/webkit.rb0000644000004100000410000000664414761246733022316 0ustar www-datawww-dataclass UserAgent module Browsers class Webkit < Base WEBKIT_PRODUCT_REGEXP = /\AAppleWebKit\z/i WEBKIT_VERSION_REGEXP = /\A(?AppleWebKit)\/(?[\d\.]+)/i def self.extend?(agent) agent.detect { |useragent| useragent.product =~ WEBKIT_PRODUCT_REGEXP || useragent.detect_comment { |c| c =~ WEBKIT_VERSION_REGEXP } } end def browser if os =~ /Android/ 'Android' elsif platform == 'BlackBerry' platform else 'Safari' end end def build webkit.version end BuildVersions = { "85.7" => "1.0", "85.8.5" => "1.0.3", "85.8.2" => "1.0.3", "124" => "1.2", "125.2" => "1.2.2", "125.4" => "1.2.3", "125.5.5" => "1.2.4", "125.5.6" => "1.2.4", "125.5.7" => "1.2.4", "312.1.1" => "1.3", "312.1" => "1.3", "312.5" => "1.3.1", "312.5.1" => "1.3.1", "312.5.2" => "1.3.1", "312.8" => "1.3.2", "312.8.1" => "1.3.2", "412" => "2.0", "412.6" => "2.0", "412.6.2" => "2.0", "412.7" => "2.0.1", "416.11" => "2.0.2", "416.12" => "2.0.2", "417.9" => "2.0.3", "418" => "2.0.3", "418.8" => "2.0.4", "418.9" => "2.0.4", "418.9.1" => "2.0.4", "419" => "2.0.4", "425.13" => "2.2", "534.52.7" => "5.1.2" }.freeze # Prior to Safari 3, the user agent did not include a version number def version str = if product = detect_product('Version') product.version elsif os =~ /iOS ([\d\.]+)/ && browser == "Safari" $1.tr('_', '.') else BuildVersions[build.to_s] end Version.new(str) end def application self.reject { |agent| agent.comment.nil? || agent.comment.empty? }.first end def platform return unless application if application.comment[0] =~ /Windows/ 'Windows' elsif application.comment[0] == 'BB10' 'BlackBerry' elsif application.comment.any? { |c| c =~ /Android/ } 'Android' else application.comment[0] end end def webkit if product_match = detect { |useragent| useragent.product =~ WEBKIT_PRODUCT_REGEXP } product_match elsif comment_match = detect_comment_match(WEBKIT_VERSION_REGEXP) UserAgent.new(comment_match[:webkit], comment_match[:version]) end end def security Security[application.comment[1]] end def os return unless application if application.comment[0] =~ /Windows NT/ OperatingSystems.normalize_os(application.comment[0]) elsif application.comment[2].nil? OperatingSystems.normalize_os(application.comment[1]) elsif application.comment[1] =~ /Android/ OperatingSystems.normalize_os(application.comment[1]) elsif (os_string = application.comment.detect { |c| c =~ OperatingSystems::IOS_VERSION_REGEX }) OperatingSystems.normalize_os(os_string) else OperatingSystems.normalize_os(application.comment[2]) end end def localization return unless application application.comment[3] end end end end useragent-0.16.11/lib/user_agent/browsers/internet_explorer.rb0000644000004100000410000000336514761246733024576 0ustar www-datawww-dataclass UserAgent module Browsers class InternetExplorer < Base TRIDENT_ENGINES = { "Trident/8.0" => "11.0", "Trident/7.0" => "11.0", "Trident/6.0" => "10.0", "Trident/5.0" => "9.0", "Trident/4.0" => "8.0", }.freeze def self.extend?(agent) agent.application && agent.application.comment && (agent.application.comment[1] =~ /MSIE/ || agent.application.comment.join('; ') =~ /Trident.+rv:/) end def browser "Internet Explorer" end def version str = application.comment.join('; ')[/(MSIE\s|rv:)([\d\.]+)/, 2] Version.new(str) end def trident_version if trident = application.comment.detect { |c| c['Trident/'] } trident_version = TRIDENT_ENGINES.fetch(trident, trident) Version.new(trident_version) end end def real_version [trident_version, version].sort.last end def compatibility_view? trident_version && version < real_version end # Before version 4.0, Chrome Frame declared itself (unversioned) in a comment; # as of 4.0 it can declare itself versioned in a comment # or as a separate product with a version def chromeframe cf = application.comment.include?("chromeframe") || detect_product("chromeframe") return cf if cf cf_comment = application.comment.detect { |c| c['chromeframe/'] } cf_comment ? UserAgent.new(*cf_comment.split('/', 2)) : nil end def platform "Windows" end def os OperatingSystems.normalize_os(application.comment.join('; ').match(/Windows NT [\d\.]+|Windows Phone (OS )?[\d\.]+/).to_s) end end end end useragent-0.16.11/lib/user_agent/browsers/windows_media_player.rb0000644000004100000410000001365114761246733025232 0ustar www-datawww-dataclass UserAgent module Browsers # The user agent used by Windows Media Player or applications which utilize the # Windows Media SDK. # # @note Both VLC and libavformat impersonate Windows Media Player when they think they # are using MMS (Microsoft Media Services/Windows Media Server). class WindowsMediaPlayer < Base def self.extend?(agent) agent.detect do |useragent| %w(NSPlayer Windows-Media-Player WMFSDK).include?(useragent.product) && agent.version != "4.1.0.3856" && # 4.1.0.3856 is libavformat agent.version != "7.10.0.3059" && # used by VLC for mmsh support agent.version != "7.0.0.1956" # used by VLC for mmstu support end end # The Windows Media Format SDK version # # @return [Version, nil] The WMFSDK version def wmfsdk_version (respond_to?("WMFSDK") && self.send("WMFSDK").version) || nil end # Check if the client supports the WMFSDK version passed in. # # @param [String] version # The WMFSDK version to check for. For example, "9.0", "11.0", "12.0" # @return [true, false] Is this media player compatible with the passed WMFSDK version? def has_wmfsdk?(version) if wmfsdk_version && wmfsdk_version.to_s =~ /\A#{version}/ return true else return false end end # @return ["Windows Media Player"] All of the user agents we parse are Windows Media Player def browser "Windows Media Player" end # @return ["Windows"] All of the user agents we parse are on Windows def platform "Windows" end # @return [true, false] Is this Windows Media Player 6.4 (NSPlayer 4.1) or Media Player 6.0 (NSPlayer 3.2)? def classic? version.to_a[0] <= 4 end # Check if our parsed OS is a mobile OS # # @return [true, false] Is this a mobile Windows Media Player? def mobile? ["Windows Phone 8", "Windows Phone 8.1"].include?(os) end # Parses the Windows Media Player version to figure out the host OS version # # User agents I have personally found: # # Windows 95 with Windows Media Player 6.4:: # NSPlayer/4.1.0.3857 # # Windows 98 SE with Windows Media Player 6.01:: # NSPlayer/3.2.0.3564 # # Womdpws 98 SE with Windows Media Player 6.4:: # NSPlayer/4.1.0.3857 # NSPlayer/4.1.0.3925 # # Windows 98 SE with Windows Media Player 7.1:: # NSPlayer/7.1.0.3055 # # Windows 98 SE with Windows Media Player 9.0:: # Windows-Media-Player/9.00.00.2980 # NSPlayer/9.0.0.2980 WMFSDK/9.0 # # Windows 2000 with Windows Media Player 6.4:: # NSPlayer/4.1.0.3938 # # Windows 2000 with Windows Media Player 7.1 (downgraded from WMP9):: # NSPlayer/9.0.0.3268 # NSPlayer/9.0.0.3268 WMFSDK/9.0 # NSPlayer/9.0.0.3270 WMFSDK/9.0 # NSPlayer/9.0.0.2980 # # Windows 2000 with Windows Media Player 9.0:: # NSPlayer/9.0.0.3270 WMFSDK/9.0 # Windows-Media-Player/9.00.00.3367 # # Windows XP with Windows Media Player 6.4:: # NSPlayer/4.1.0.3936 # # Windows XP with Windows Media Player 9:: # NSPlayer/9.0.0.4503 # NSPlayer/9.0.0.4503 WMFSDK/9.0 # Windows-Media-Player/9.00.00.4503 # # Windows XP with Windows Media Player 10:: # NSPlayer/10.0.0.3802 # NSPlayer/10.0.0.3802 WMFSDK/10.0 # Windows-Media-Player/10.00.00.3802 # # Windows XP with Windows Media Player 11:: # NSPlayer/11.0.5721.5262 # NSPlayer/11.0.5721.5262 WMFSDK/11.0 # Windows-Media-Player/11.0.5721.5262 # # Windows Vista with Windows Media Player 11:: # NSPlayer/11.00.6002.18392 WMFSDK/11.00.6002.18392 # NSPlayer/11.0.6002.18005 # NSPlayer/11.0.6002.18049 WMFSDK/11.0 # Windows-Media-Player/11.0.6002.18311 # # Windows 8.1 with Windows Media Player 12:: # NSPlayer/12.00.9600.17031 WMFSDK/12.00.9600.17031 # # Windows 10 with Windows Media Player 12:: # Windows-Media-Player/12.0.9841.0 # NSPlayer/12.00.9841.0000 WMFSDK/12.00.9841.0000 # # Windows Phone 8.1 (Podcasts app):: # NSPlayer/12.00.9651.0000 WMFSDK/12.00.9651.0000 def os # WMP 6.4 if classic? case version.to_a[3] when 3564, 3925 then "Windows 98" when 3857 then "Windows 9x" when 3936 then "Windows XP" when 3938 then "Windows 2000" else "Windows" end # WMP 7/7.1 elsif version.to_a[0] == 7 case version.to_a[3] when 3055 then "Windows 98" else "Windows" end # WMP 8 was also known as "Windows Media Player for Windows XP" elsif version.to_a[0] == 8 "Windows XP" # WMP 9/10 elsif version.to_a[0] == 9 || version.to_a[0] == 10 case version.to_a[3] when 2980 then "Windows 98/2000" when 3268, 3367, 3270 then "Windows 2000" when 3802, 4503 then "Windows XP" else "Windows" end # WMP 11/12 elsif version.to_a[0] == 11 || version.to_a[0] == 12 case version.to_a[2] when 9841, 9858, 9860, 9879 then "Windows 10" when 9651 then "Windows Phone 8.1" when 9600 then "Windows 8.1" when 9200 then "Windows 8" when 7600, 7601 then "Windows 7" when 6000, 6001, 6002 then "Windows Vista" when 5721 then "Windows XP" else "Windows" end else "Windows" end end end end enduseragent-0.16.11/lib/user_agent/browsers/apple_core_media.rb0000644000004100000410000000242314761246733024270 0ustar www-datawww-dataclass UserAgent module Browsers # CoreMedia is a framework on iOS and is used by various iOS apps to playback media. class AppleCoreMedia < Base def self.extend?(agent) agent.detect { |useragent| useragent.product == 'AppleCoreMedia' } end def browser "AppleCoreMedia" end def application self.reject { |agent| agent.comment.nil? || agent.comment.empty? }.first end def platform return unless application if application.comment[0] =~ /Windows/ 'Windows' else application.comment[0] end end def security Security[application.comment[1]] end def os return unless application if application.comment[0] =~ /Windows NT/ OperatingSystems.normalize_os(application.comment[0]) elsif application.comment[2].nil? OperatingSystems.normalize_os(application.comment[1]) elsif application.comment[1] =~ /Android/ OperatingSystems.normalize_os(application.comment[1]) else OperatingSystems.normalize_os(application.comment[2]) end end def localization return unless application application.comment[3] end end end end useragent-0.16.11/lib/user_agent/browsers/opera.rb0000644000004100000410000000345214761246733022131 0ustar www-datawww-dataclass UserAgent module Browsers class Opera < Base def self.extend?(agent) (agent.first && agent.first.product == 'Opera') || (agent.application && agent.application.product == 'Opera') || (agent.last && agent.last.product == 'OPR') end def browser 'Opera' end def version if mini? Version.new(application.comment.detect{|c| c =~ /Opera Mini/}[/Opera Mini\/([\d\.]+)/, 1]) rescue Version.new elsif product = detect_product('Version') Version.new(product.version) elsif product = detect_product('OPR') Version.new(product.version) else super end end def platform return unless application.comment if application.comment[0] =~ /Windows/ "Windows" else application.comment[0] end end def security if application.comment.nil? :strong elsif macintosh? Security[application.comment[2]] elsif mini? Security[application.comment[-2]] else Security[application.comment[1]] end end def mobile? mini? end def os return unless application.comment if application.comment[0] =~ /Windows/ OperatingSystems.normalize_os(application.comment[0]) else application.comment[1] end end def localization return unless application.comment if macintosh? application.comment[3] else application.comment[2] end end private def mini? /Opera Mini/ === application end def macintosh? platform == 'Macintosh' end end end end useragent-0.16.11/lib/user_agent/browsers/edge.rb0000644000004100000410000000103514761246733021722 0ustar www-datawww-dataclass UserAgent module Browsers class Edge < Base OS_REGEXP = /Windows NT [\d\.]+|Windows Phone (OS )?[\d\.]+/ def self.extend?(agent) agent.last && agent.last.product == "Edge" end def browser "Edge" end def version last.version end def platform "Windows" end def os OperatingSystems.normalize_os(os_comment) end private def os_comment detect_comment_match(OS_REGEXP).to_s end end end end useragent-0.16.11/lib/user_agent/operating_systems.rb0000644000004100000410000000310514761246733022727 0ustar www-datawww-dataclass UserAgent module OperatingSystems IOS_VERSION_REGEX = /CPU (?:iPhone |iPod )?OS ([\d_]+) like Mac OS X/ Windows = { "Windows NT 10.0" => "Windows 10", "Windows NT 6.3" => "Windows 8.1", "Windows NT 6.2" => "Windows 8", "Windows NT 6.1" => "Windows 7", "Windows NT 6.0" => "Windows Vista", "Windows NT 5.2" => "Windows XP x64 Edition", "Windows NT 5.1" => "Windows XP", "Windows NT 5.01" => "Windows 2000, Service Pack 1 (SP1)", "Windows NT 5.0" => "Windows 2000", "Windows NT 4.0" => "Windows NT 4.0", "Windows 98" => "Windows 98", "Windows 95" => "Windows 95", "Windows CE" => "Windows CE" }.freeze def self.normalize_os(os) Windows[os] || normalize_mac_os_x(os) || normalize_ios(os) || normalize_chrome_os(os) || os end private def self.normalize_chrome_os(os) if os =~ /CrOS\s([^\s]+)\s(\d+(\.\d+)*)/ if $2.nil? "ChromeOS" else version = $2 "ChromeOS #{version}" end end end def self.normalize_ios(os) if os =~ IOS_VERSION_REGEX if $1.nil? "iOS" else version = $1.tr('_', '.') "iOS #{version}" end end end def self.normalize_mac_os_x(os) if os =~ /(?:Intel|PPC) Mac OS X\s*([0-9_\.]+)?/ if $1.nil? "OS X" else version = $1.tr('_', '.') "OS X #{version}" end end end end end useragent-0.16.11/lib/user_agent/version.rb0000644000004100000410000000362314761246733020642 0ustar www-datawww-dataclass UserAgent class Version include ::Comparable def self.new(obj = nil) case obj when Version obj when String super when NilClass super("") else raise ArgumentError, "invalid value for Version: #{obj.inspect}" end end def initialize(str) @str = str if @str =~ /^\s*$/ @nil = true @sequences = [] @comparable = false elsif str =~ /^\d+$/ || str =~ /^\d+\./ @nil = false @sequences = str.scan(/\d+|[A-Za-z][0-9A-Za-z-]*$/).map { |s| s =~ /^\d+$/ ? s.to_i : s } @comparable = true else @nil = false @sequences = [str] @comparable = false end end def nil? @nil end def to_a @sequences.dup end def to_str @str.dup end def eql?(other) other.is_a?(self.class) && to_s == other.to_s end def ==(other) case other when Version eql?(other) when String eql?(self.class.new(other)) when NilClass nil? else false end end def <=>(other) case other when Version if @comparable ([0]*6).zip(to_a, other.to_a).each do |dump, a, b| a ||= 0 b ||= 0 if a.is_a?(String) && b.is_a?(Integer) return -1 elsif a.is_a?(Integer) && b.is_a?(String) return 1 elsif a == b next else return a <=> b end end 0 elsif to_s == other.to_s return 0 else return -1 end when String, NilClass self <=> self.class.new(other) else nil end end def to_s to_str end def inspect "#<#{self.class} #{to_s}>" end end end useragent-0.16.11/lib/user_agent/browsers.rb0000644000004100000410000000227014761246733021020 0ustar www-datawww-datarequire 'user_agent/browsers/base' require 'user_agent/browsers/chrome' require 'user_agent/browsers/edge' require 'user_agent/browsers/gecko' require 'user_agent/browsers/internet_explorer' require 'user_agent/browsers/opera' require 'user_agent/browsers/webkit' require 'user_agent/browsers/wechat_browser' require 'user_agent/browsers/windows_media_player' require 'user_agent/browsers/itunes' require 'user_agent/browsers/apple_core_media' require 'user_agent/browsers/libavformat' require 'user_agent/browsers/playstation' require 'user_agent/browsers/podcast_addict' require 'user_agent/browsers/vivaldi' class UserAgent module Browsers Security = { "N" => :none, "U" => :strong, "I" => :weak }.freeze ALL = [ Edge, InternetExplorer, Opera, WechatBrowser, Vivaldi, Chrome, ITunes, PlayStation, PodcastAddict, Webkit, Gecko, WindowsMediaPlayer, AppleCoreMedia, Libavformat, ].freeze def self.all ALL end def self.extend(array) all.each do |extension| return extension.new(array) if extension.extend?(array) end array end end end useragent-0.16.11/lib/user_agent.rb0000644000004100000410000000373514761246733017161 0ustar www-datawww-datarequire 'user_agent/comparable' require 'user_agent/browsers' require 'user_agent/operating_systems' require 'user_agent/version' class UserAgent # http://www.texsoft.it/index.php?m=sw.php.useragent MATCHER = %r{ ^['"]* # Possible opening quote(s) ([^/\s]+) # Product /?([^\s,]*) # Version (\s\(([^\)]*)\)|,gzip\(gfe\))? # Comment }x.freeze DEFAULT_USER_AGENT = "Mozilla/4.0 (compatible)" def self.parse(string) if string.nil? || string.strip == "" string = DEFAULT_USER_AGENT end agents = Browsers::Base.new while m = string.to_s.match(MATCHER) agents << new(m[1], m[2], m[4]) string = string[m[0].length..-1].strip end Browsers.extend(agents) end attr_reader :product, :version, :comment def initialize(product, version = nil, comment = nil) if product @product = product else raise ArgumentError, "expected a value for product" end if version && !version.empty? @version = Version.new(version) else @version = Version.new end if comment.respond_to?(:split) @comment = comment.split("; ") else @comment = comment end end include Comparable def detect_comment(&block) comment && comment.detect(&block) end # Any comparison between two user agents with different products will # always return false. def <=>(other) if @product == other.product @version <=> other.version else false end end def eql?(other) @product == other.product && @version == other.version && @comment == other.comment end def to_s to_str end def to_str if @product && !@version.nil? && @comment "#{@product}/#{@version} (#{@comment.join("; ")})" elsif @product && !@version.nil? "#{@product}/#{@version}" elsif @product && @comment "#{@product} (#{@comment.join("; ")})" else @product end end end useragent-0.16.11/useragent.gemspec0000644000004100000410000000421514761246733017266 0ustar www-datawww-data######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: useragent 0.16.11 ruby lib Gem::Specification.new do |s| s.name = "useragent".freeze s.version = "0.16.11" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze] s.authors = ["Joshua Peek".freeze, "Garry Shutler".freeze] s.date = "2024-12-04" s.description = "HTTP User Agent parser".freeze s.email = "garry@robustsoftware.co.uk".freeze s.files = ["LICENSE".freeze, "README.md".freeze, "lib/user_agent.rb".freeze, "lib/user_agent/browsers.rb".freeze, "lib/user_agent/browsers/apple_core_media.rb".freeze, "lib/user_agent/browsers/base.rb".freeze, "lib/user_agent/browsers/chrome.rb".freeze, "lib/user_agent/browsers/edge.rb".freeze, "lib/user_agent/browsers/gecko.rb".freeze, "lib/user_agent/browsers/internet_explorer.rb".freeze, "lib/user_agent/browsers/itunes.rb".freeze, "lib/user_agent/browsers/libavformat.rb".freeze, "lib/user_agent/browsers/opera.rb".freeze, "lib/user_agent/browsers/playstation.rb".freeze, "lib/user_agent/browsers/podcast_addict.rb".freeze, "lib/user_agent/browsers/vivaldi.rb".freeze, "lib/user_agent/browsers/webkit.rb".freeze, "lib/user_agent/browsers/wechat_browser.rb".freeze, "lib/user_agent/browsers/windows_media_player.rb".freeze, "lib/user_agent/comparable.rb".freeze, "lib/user_agent/operating_systems.rb".freeze, "lib/user_agent/version.rb".freeze, "lib/useragent.rb".freeze] s.homepage = "https://github.com/gshutler/useragent".freeze s.licenses = ["MIT".freeze] s.rubygems_version = "3.3.15".freeze s.summary = "HTTP User Agent parser".freeze if s.respond_to? :specification_version then s.specification_version = 4 end if s.respond_to? :add_runtime_dependency then s.add_development_dependency(%q.freeze, ["~> 13.0"]) s.add_development_dependency(%q.freeze, ["~> 3.0"]) else s.add_dependency(%q.freeze, ["~> 13.0"]) s.add_dependency(%q.freeze, ["~> 3.0"]) end end useragent-0.16.11/LICENSE0000644000004100000410000000204114761246733014724 0ustar www-datawww-dataCopyright (c) 2015 Garry Shutler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. useragent-0.16.11/README.md0000644000004100000410000000210314761246733015175 0ustar www-datawww-data# UserAgent [![Build Status](https://github.com/gshutler/useragent/actions/workflows/test.yml/badge.svg)](https://github.com/gshutler/useragent/actions/workflows/test.yml) [![Gem Version](https://badge.fury.io/rb/useragent.svg)](http://badge.fury.io/rb/useragent) UserAgent is a Ruby library that parses and compares HTTP User Agents. ## Installation gem install useragent ### Examples #### Reporting ```ruby string = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.56 Safari/536.5' user_agent = UserAgent.parse(string) user_agent.browser # => 'Chrome' user_agent.version # => '19.0.1084.56' user_agent.platform # => 'Macintosh' ``` #### Comparison ```ruby Browser = Struct.new(:browser, :version) SupportedBrowsers = [ Browser.new("Safari", "3.1.1"), Browser.new("Firefox", "2.0.0.14"), Browser.new("Internet Explorer", "7.0") ] user_agent = UserAgent.parse(request.user_agent) SupportedBrowsers.detect { |browser| user_agent >= browser } ``` Copyright (c) 2015-2019 Garry Shutler, released under the MIT license