%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1997-2016. 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% %% %% -module(mod_alias). -export([do/1, real_name/3, real_script_name/3, default_index/2, load/2, store/2, path/3]). -include("httpd.hrl"). -include("httpd_internal.hrl"). -include("inets_internal.hrl"). -define(VMODULE,"ALIAS"). %% do do(#mod{data = Data} = Info) -> ?hdrt("do", []), case proplists:get_value(status, Data) of %% A status code has been generated! {_StatusCode, _PhraseArgs, _Reason} -> {proceed, Data}; %% No status code has been generated! undefined -> case proplists:get_value(response, Data) of %% No response has been generated! undefined -> do_alias(Info); %% A response has been generated or sent! _Response -> {proceed, Data} end end. do_alias(#mod{config_db = ConfigDB, request_uri = ReqURI, socket_type = SocketType, data = Data}) -> {ShortPath, Path, AfterPath} = real_name(ConfigDB, ReqURI, which_alias(ConfigDB)), ?hdrt("real name", [{request_uri, ReqURI}, {short_path, ShortPath}, {path, Path}, {after_path, AfterPath}]), %% Relocate if a trailing slash is missing else proceed! LastChar = lists:last(ShortPath), case file:read_file_info(ShortPath) of {ok, FileInfo} when ((FileInfo#file_info.type =:= directory) andalso (LastChar =/= $/)) -> ?hdrt("directory and last-char is a /", []), ServerName = which_server_name(ConfigDB), Port = port_string(which_port(ConfigDB)), Protocol = get_protocol(SocketType), URL = Protocol ++ ServerName ++ Port ++ ReqURI ++ "/", ReasonPhrase = httpd_util:reason_phrase(301), Message = httpd_util:message(301, URL, ConfigDB), {proceed, [{response, {301, ["Location: ", URL, "\r\n" "Content-Type: text/html\r\n", "\r\n", "\n\n",ReasonPhrase, "\n\n" "\n

",ReasonPhrase, "

\n", Message, "\n\n\n"]}}| [{real_name, {Path, AfterPath}} | Data]]}; _NoFile -> {proceed, [{real_name, {Path, AfterPath}} | Data]} end. port_string(80) -> ""; port_string(Port) -> ":" ++ integer_to_list(Port). get_protocol(ip_comm) -> "http://"; get_protocol(_) -> %% Should clean up to have only one ssl type essl vs ssl is not relevant any more "https://". %% real_name real_name(ConfigDB, RequestURI, []) -> DocumentRoot = which_document_root(ConfigDB), RealName = DocumentRoot ++ RequestURI, {ShortPath, _AfterPath} = httpd_util:split_path(RealName), {Path, AfterPath} = httpd_util:split_path(default_index(ConfigDB, RealName)), {ShortPath, Path, AfterPath}; real_name(ConfigDB, RequestURI, [{MP,Replacement}| _] = Aliases) when element(1, MP) =:= re_pattern -> case longest_match(Aliases, RequestURI) of {match, {MP, Replacement}} -> NewURI = re:replace(RequestURI, MP, Replacement, [{return,list}]), {ShortPath,_} = httpd_util:split_path(NewURI), {Path,AfterPath} = httpd_util:split_path(default_index(ConfigDB, NewURI)), {ShortPath, Path, AfterPath}; nomatch -> real_name(ConfigDB, RequestURI, []) end; real_name(ConfigDB, RequestURI, [{_,_}|_] = Aliases) -> case longest_match(Aliases, RequestURI) of {match, {FakeName, RealName}} -> ActualName = re:replace(RequestURI, "^" ++ FakeName, RealName, [{return,list}]), {ShortPath, _AfterPath} = httpd_util:split_path(ActualName), {Path, AfterPath} = httpd_util:split_path(default_index(ConfigDB, ActualName)), {ShortPath, Path, AfterPath}; nomatch -> real_name(ConfigDB, RequestURI, []) end. longest_match(Aliases, RequestURI) -> longest_match(Aliases, RequestURI, _LongestNo = 0, _LongestAlias = undefined). longest_match([{FakeName, RealName} | Rest], RequestURI, LongestNo, LongestAlias) -> case re:run(RequestURI, "^" ++ FakeName, [{capture, first}]) of {match, [{_, Length}]} -> if Length > LongestNo -> longest_match(Rest, RequestURI, Length, {FakeName, RealName}); true -> longest_match(Rest, RequestURI, LongestNo, LongestAlias) end; nomatch -> longest_match(Rest, RequestURI, LongestNo, LongestAlias) end; longest_match([], _RequestURI, 0, _LongestAlias) -> nomatch; longest_match([], _RequestURI, _LongestNo, LongestAlias) -> {match, LongestAlias}. %% real_script_name real_script_name(_ConfigDB, _RequestURI, []) -> not_a_script; real_script_name(ConfigDB, RequestURI, [{MP,Replacement} | Rest]) when element(1, MP) =:= re_pattern -> case re:run(RequestURI, MP, [{capture, none}]) of match -> ActualName = re:replace(RequestURI, MP, Replacement, [{return,list}]), httpd_util:split_script_path(default_index(ConfigDB, ActualName)); nomatch -> real_script_name(ConfigDB, RequestURI, Rest) end; real_script_name(ConfigDB, RequestURI, [{FakeName,RealName} | Rest]) -> case re:run(RequestURI, "^" ++ FakeName, [{capture, none}]) of match -> ActualName = re:replace(RequestURI, "^" ++ FakeName, RealName, [{return,list}]), httpd_util:split_script_path(default_index(ConfigDB, ActualName)); nomatch -> real_script_name(ConfigDB, RequestURI, Rest) end. %% default_index default_index(ConfigDB, Path) -> case file:read_file_info(Path) of {ok, FileInfo} when FileInfo#file_info.type =:= directory -> DirectoryIndex = which_directory_index(ConfigDB), append_index(Path, DirectoryIndex); _ -> Path end. append_index(RealName, []) -> RealName; append_index(RealName, [Index | Rest]) -> case file:read_file_info(filename:join(RealName, Index)) of {error, _Reason} -> append_index(RealName, Rest); _ -> filename:join(RealName, Index) end. %% path path(Data, ConfigDB, RequestURI) -> case proplists:get_value(real_name, Data) of undefined -> DocumentRoot = which_document_root(ConfigDB), {Path, _AfterPath} = httpd_util:split_path(DocumentRoot ++ RequestURI), Path; {Path, _AfterPath} -> Path end. %% %% Configuration %% %% load load("DirectoryIndex " ++ DirectoryIndex, []) -> DirectoryIndexes = re:split(DirectoryIndex," ", [{return, list}]), {ok,[], {directory_index, DirectoryIndexes}}; load("Alias " ++ Alias, []) -> case re:split(Alias," ", [{return, list}]) of [FakeName, RealName] -> {ok,[],{alias,{FakeName,RealName}}}; _ -> {error,?NICE(string:strip(Alias)++" is an invalid Alias")} end; load("ReWrite " ++ Rule, Acc) -> load_re_write(Rule, Acc, "ReWrite", re_write); load("ScriptAlias " ++ ScriptAlias, []) -> case re:split(ScriptAlias, " ", [{return, list}]) of [FakeName, RealName] -> %% Make sure the path always has a trailing slash.. RealName1 = filename:join(filename:split(RealName)), {ok, [], {script_alias, {FakeName, RealName1++"/"}}}; _ -> {error, ?NICE(string:strip(ScriptAlias)++ " is an invalid ScriptAlias")} end; load("ScriptReWrite " ++ Rule, Acc) -> load_re_write(Rule, Acc, "ScriptReWrite", script_re_write). load_re_write(Rule0, Acc, Type, Tag) -> case lists:dropwhile( fun ($\s) -> true; ($\t) -> true; (_) -> false end, Rule0) of "" -> {error, ?NICE(string:strip(Rule0)++" is an invalid "++Type)}; Rule -> case string:chr(Rule, $\s) of 0 -> {ok, Acc, {Tag, {Rule, ""}}}; N -> {Re, [_|Replacement]} = lists:split(N-1, Rule), {ok, Acc, {Tag, {Re, Replacement}}} end end. store({directory_index, Value} = Conf, _) when is_list(Value) -> case is_directory_index_list(Value) of true -> {ok, Conf}; false -> {error, {wrong_type, {directory_index, Value}}} end; store({directory_index, Value}, _) -> {error, {wrong_type, {directory_index, Value}}}; store({alias, {Fake, Real}} = Conf, _) when is_list(Fake), is_list(Real) -> {ok, Conf}; store({alias, Value}, _) -> {error, {wrong_type, {alias, Value}}}; store({re_write, {Re, Replacement}} = Conf, _) when is_list(Re), is_list(Replacement) -> case re:compile(Re) of {ok, MP} -> {ok, {alias, {MP, Replacement}}}; {error,_} -> {error, {re_compile, Conf}} end; store({re_write, _} = Conf, _) -> {error, {wrong_type, Conf}}; store({script_alias, {Fake, Real}} = Conf, _) when is_list(Fake), is_list(Real) -> {ok, Conf}; store({script_alias, Value}, _) -> {error, {wrong_type, {script_alias, Value}}}; store({script_re_write, {Re, Replacement}} = Conf, _) when is_list(Re), is_list(Replacement) -> case re:compile(Re) of {ok, MP} -> {ok, {script_alias, {MP, Replacement}}}; {error,_} -> {error, {re_compile, Conf}} end; store({script_re_write, _} = Conf, _) -> {error, {wrong_type, Conf}}. is_directory_index_list([]) -> true; is_directory_index_list([Head | Tail]) when is_list(Head) -> is_directory_index_list(Tail); is_directory_index_list(_) -> false. %% --------------------------------------------------------------------- which_alias(ConfigDB) -> httpd_util:multi_lookup(ConfigDB, alias). which_server_name(ConfigDB) -> httpd_util:lookup(ConfigDB, server_name). which_port(ConfigDB) -> httpd_util:lookup(ConfigDB, port, 80). which_document_root(ConfigDB) -> httpd_util:lookup(ConfigDB, document_root, ""). which_directory_index(ConfigDB) -> httpd_util:lookup(ConfigDB, directory_index, []).