%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2001-2009. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% %% %% ---------------------------------------------------------------------- %% %% Browsers sends a string to the webbserver %% to identify themsevles. They are a bit nasty %% since the only thing that the specification really %% is strict about is that they shall be short %% some axamples: %% %% Netscape Mozilla/4.75 [en] (X11; U; SunOS 5.8 sun4u) %% Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.0.1) Gecko/20020823 Netscape/7.0 %% Mozilla Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.1) Gecko/20020827 %% Safari Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/85 (KHTML, like Gecko) Safari/85 %% IE5 Mozilla/4.0 (compatible; MSIE 5.0; SP1B; SunOS 5.8 sun4u; X11) %% Lynx Lynx/2.8.3rel.1 libwww-FM/2.142 %% %% ---------------------------------------------------------------------- -module(mod_browser). -export([do/1, test/0, getBrowser/1]). %% Remember that the order of the mozilla browsers are %% important since some browsers include others to behave %% as they were something else -define(MOZILLA_BROWSERS,[{netscape, "netscape"}, {opera, "opera"}, {msie, "msie"}, {safari, "safari"}, {mozilla, "rv:"}]). % fallback, must be last %% If your operatingsystem is not recognized add it to this list. -define(OPERATIVE_SYSTEMS,[{win3x, ["win16", "windows 3", "windows 16-bit"]}, {win95, ["win95", "windows 95"]}, {win98, ["win98", "windows 98"]}, {winnt, ["winnt", "windows nt"]}, {win2k, ["nt 5"]}, {sunos4, ["sunos 4"]}, {sunos5, ["sunos 5"]}, {sun, ["sunos"]}, {aix, ["aix"]}, {linux, ["linux"]}, {sco, ["sco", "unix_sv"]}, {freebsd,["freebsd"]}, {bsd, ["bsd"]}, {macosx, ["mac os x"]}]). -define(LYNX, lynx). -define(MOZILLA, mozilla). -define(EMACS, emacs). -define(STAROFFICE, soffice). -define(MOSAIC, mosaic). -define(NETSCAPE, netscape). -define(SAFARU, safari). -define(UNKOWN, unknown). -include("httpd.hrl"). -define(VMODULE,"BROWSER"). do(Info) -> case proplists:get_value(status, Info#mod.data) of {_StatusCode, _PhraseArgs, _Reason} -> {proceed,Info#mod.data}; undefined -> Browser = getBrowser1(Info), {proceed,[{'user-agent', Browser}|Info#mod.data]} end. getBrowser1(Info) -> PHead = Info#mod.parsed_header, case proplists:get_value("user-agent", PHead) of undefined -> undefined; AgentString -> getBrowser(AgentString) end. getBrowser(AgentString) -> LAgentString = http_util:to_lower(AgentString), case re:run(LAgentString,"^[^ ]*", [{capture, first}]) of {match,[{Start,Length}]} -> Browser = lists:sublist(LAgentString,Start+1,Length), case browserType(Browser) of {mozilla,Vsn} -> {getMozilla(LAgentString, ?MOZILLA_BROWSERS,{?NETSCAPE,Vsn}), operativeSystem(LAgentString)}; AnyBrowser -> {AnyBrowser,operativeSystem(LAgentString)} end; nomatch -> browserType(LAgentString) end. browserType([$l,$y,$n,$x|Version]) -> {?LYNX,browserVersion(Version)}; browserType([$m,$o,$z,$i,$l,$l,$a|Version]) -> {?MOZILLA,browserVersion(Version)}; browserType([$e,$m,$a,$c,$s|Version]) -> {?EMACS,browserVersion(Version)}; browserType([$s,$t,$a,$r,$o,$f,$f,$i,$c,$e|Version]) -> {?STAROFFICE,browserVersion(Version)}; browserType([$m,$o,$s,$a,$i,$c|Version]) -> {?MOSAIC,browserVersion(Version)}; browserType(_Unknown) -> unknown. browserVersion([$/|VsnString]) -> case catch list_to_float(VsnString) of Number when is_float(Number) -> Number; _Whatever -> case string:span(VsnString,"1234567890.") of 0 -> unknown; VLength -> Vsn = string:substr(VsnString,1,VLength), case string:tokens(Vsn,".") of [Number] -> list_to_float(Number++".0"); [Major,Minor|_MinorMinor] -> list_to_float(Major++"."++Minor) end end end; browserVersion(VsnString) -> browserVersion([$/|VsnString]). operativeSystem(OpString) -> operativeSystem(OpString, ?OPERATIVE_SYSTEMS). operativeSystem(_OpString,[]) -> unknown; operativeSystem(OpString,[{RetVal,RegExps}|Rest]) -> case controlOperativeSystem(OpString,RegExps) of true -> RetVal; _ -> operativeSystem(OpString,Rest) end. controlOperativeSystem(_OpString,[]) -> false; controlOperativeSystem(OpString,[Regexp|Regexps]) -> case re:run(OpString,Regexp, [{capture, none}]) of match -> true; nomatch -> controlOperativeSystem(OpString,Regexps) end. %% OK this is ugly but thats the only way since %% all browsers dont conform to the name/vsn standard %% First we check if it is one of the browsers that %% are not the default mozillaborwser against the regexp %% for the different browsers. if no match, it is a mozilla %% browser i.e opera, netscape, ie or safari getMozilla(_AgentString,[],Default) -> Default; getMozilla(AgentString,[{Agent,AgentRegExp}|Rest],Default) -> case re:run(AgentString,AgentRegExp, [{capture, none}]) of match -> {Agent,getMozVersion(AgentString,AgentRegExp)}; nomatch -> getMozilla(AgentString,Rest,Default) end. getMozVersion(AgentString, AgentRegExp) -> case re:run(AgentString,AgentRegExp++"[0-9\.\ \/]*", [{capture, first}]) of {match, [{Start,Length}]} when length(AgentRegExp) < Length -> %% Ok we got the number split it out RealStart = Start+1+length(AgentRegExp), RealLength = Length-length(AgentRegExp), VsnString = string:substr(AgentString,RealStart,RealLength), %% case string:strip(VsnString,both,$\ ) of case strip(VsnString) of [] -> unknown; [Y1,Y2,Y3,Y4,M1,M2,D1,D2] = DateVsn when Y1 =< $9, Y1 >= $0, Y2 =< $9, Y2 >= $0, Y3 =< $9, Y3 >= $0, Y4 =< $9, Y4 >= $0, M1 =< $9, M1 >= $0, M2 =< $9, M2 >= $0, D1 =< $9, D1 >= $0, D2 =< $9, D2 >= $0 -> list_to_integer(DateVsn); Vsn -> case string:tokens(Vsn,".") of [Number]-> list_to_float(Number++".0"); [Major,Minor|Rev] -> V = lists:flatten([Major,".",Minor,Rev]), list_to_float(V) end end; nomatch -> unknown end. strip(VsnString) -> strip2(strip1(VsnString)). strip1(VsnString) -> string:strip(VsnString,both,$\ ). strip2(VsnString) -> string:strip(VsnString,both,$/ ). test()-> test("Mozilla/4.75 [en] (X11; U; SunOS 5.8 sun4u)"), test("Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.0.1) Gecko/20020823 Netscape/7.0"), test("Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.1) Gecko/20020827"), test("Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.4) Gecko/20020827"), test("Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/85 (KHTML, like Gecko) Safari/85"), test("Mozilla/4.0 (compatible; MSIE 5.0; SP1B; SunOS 5.8 sun4u; X11)"), test("Lynx/2.8.3rel.1 libwww-FM/2.142"), ok. test(Str) -> Browser = getBrowser(Str), io:format("~n--------------------------------------------------------~n"), io:format("~p",[Browser]), io:format("~n--------------------------------------------------------~n").