%% ``The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved via the world wide web at http://www.erlang.org/.
%% 
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%% 
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
%% AB. All Rights Reserved.''
%% 
%%     $Id: mod_browser.erl,v 1.1 2008/12/17 09:53:35 mikpe Exp $
%%
%% ----------------------------------------------------------------------
%%
%% 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
%% tree axamples:
%%
%% Netscape Mozilla/4.75 [en] (X11; U; SunOS 5.8 sun4u)
%% 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).

%% 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,[{opera,"opera"},{msie,"msie"}]).


%% 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"]}]).

-define(LYNX,lynx).
-define(MOZILLA,mozilla).
-define(EMACS,emacs).
-define(STAROFFICE,soffice).
-define(MOSAIC,mosaic).
-define(NETSCAPE,netscape).
-define(UNKOWN,unknown).

-include("httpd.hrl").

-export([do/1, test/0, getBrowser/1]).


do(Info) ->
    case httpd_util:key1search(Info#mod.data,status) of
	{Status_code,PhraseArgs,Reason} ->
	    {proceed,Info#mod.data};
	undefined ->
	    {proceed,[{'user-agent',getBrowser1(Info)}|Info#mod.data]}
    end.

getBrowser1(Info) ->
    PHead=Info#mod.parsed_header,
    case httpd_util:key1search(PHead,"User-Agent") of
	undefined->
	   undefined;
	AgentString ->
	    getBrowser(AgentString)
    end.

getBrowser(AgentString) ->
    LAgentString = httpd_util:to_lower(AgentString),
    case 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([$e,$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 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 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 
%% not are the default mozillaborwser against the regexp 
%% for the different browsers. if no match it a mozilla 
%% browser i.e opera netscape or internet explorer

getMozilla(AgentString,[],Default) ->
    Default;
getMozilla(AgentString,[{Agent,AgentRegExp}|Rest],Default) ->
    case regexp:match(AgentString,AgentRegExp) of
	{match,_,_} ->
	    {Agent,getVersion(AgentString,AgentRegExp)};
	nomatch ->
	    getMozilla(AgentString,Rest,Default)
    end.

getVersion(AgentString,AgentRegExp) ->
    case 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
		[] ->
		    unknown;
		Vsn ->
		    case string:tokens(Vsn,".") of
			[Number]->
			    list_to_float(Number++".0");
			[Major,Minor|_MinorMinor]->
			    list_to_float(Major++"."++Minor)
		    end
	    end;
	nomatch ->
	    unknown
    end.


test()->
    io:format("~n--------------------------------------------------------~n"),
    Res1=getBrowser("Mozilla/4.75 [en] (X11; U; SunOS 5.8 sun4u)"),
    io:format("~p",[Res1]),
    io:format("~n--------------------------------------------------------~n"),
    io:format("~n--------------------------------------------------------~n"),
    Res2=getBrowser("Mozilla/4.0 (compatible; MSIE 5.0; SP1B; SunOS 5.8 sun4u; X11)"),
    io:format("~p",[Res2]),
    io:format("~n--------------------------------------------------------~n"),
    io:format("~n--------------------------------------------------------~n"),
    Res3=getBrowser("Lynx/2.8.3rel.1 libwww-FM/2.142"),
    io:format("~p",[Res3]),
    io:format("~n--------------------------------------------------------~n").

