%%
%% %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 inets_regexp:first_match(LAgentString,"^[^ ]*") of
{match,Start,Length} ->
Browser = lists:sublist(LAgentString,Start,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 inets_regexp:match(OpString,Regexp) 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 inets_regexp:match(AgentString,AgentRegExp) of
{match,_,_} ->
{Agent,getMozVersion(AgentString,AgentRegExp)};
nomatch ->
getMozilla(AgentString,Rest,Default)
end.
getMozVersion(AgentString, AgentRegExp) ->
case inets_regexp:match(AgentString,AgentRegExp++"[0-9\.\ \/]*") of
{match,Start,Length} when length(AgentRegExp) < Length ->
%% Ok we got the number split it out
RealStart = Start+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").