1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2005-2021. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20%%
21
22-module(httpd_script_env).
23
24-export([create_env/3]).
25
26-include("httpd.hrl").
27-include("httpd_internal.hrl").
28
29%%%=========================================================================
30%%%  Internal application API
31%%%=========================================================================
32%%--------------------------------------------------------------------------
33%% create_env(ScriptType, ModData, ScriptElements) -> [{EnvVariable, Value}]
34%%
35%%	ScriptType = cgi | esi
36%%	ModData = #mod{}
37%%      ScriptElements = [{Element, Value}]
38%%      Element = path_info | query_string | entity_body
39%%      Value = term()
40%%      EnvVariable = string() - cgi | atom() - esi
41%%
42%% Description: Creates a list of cgi/esi environment variables and
43%% there values.
44%%--------------------------------------------------------------------------
45create_env(ScriptType, ModData, ScriptElements) ->
46    create_basic_elements(ScriptType, ModData)
47	++ create_http_header_elements(ScriptType, ModData#mod.parsed_header)
48	++ create_script_elements(ScriptType, ModData, ScriptElements)
49	++ create_mod_interaction_elements(ScriptType, ModData).
50
51%%%========================================================================
52%%% Internal functions
53%%%========================================================================
54
55which_server(#mod{config_db = ConfigDb}) ->
56    httpd_util:lookup(ConfigDb, server, ?SERVER_SOFTWARE).
57
58which_port(#mod{config_db = ConfigDb}) ->
59    httpd_util:lookup(ConfigDb, port, 80).
60
61which_peername(#mod{init_data = #init_data{peername = {_, RemoteAddr}}}) ->
62    RemoteAddr.
63
64which_peercert(#mod{socket_type = {Type, _}, socket = Socket}) when Type == essl;
65								    Type == ssl ->
66    case ssl:peercert(Socket) of
67	{ok, Cert} ->
68	    Cert;
69	{error, no_peercert} ->
70	    no_peercert;
71	_  ->
72	    undefined
73    end;
74which_peercert(_) -> %% Not an ssl connection
75    undefined.
76
77
78which_resolve(#mod{init_data = #init_data{resolve = Resolve}}) ->
79    Resolve.
80
81which_name(#mod{config_db = ConfigDB}) ->
82    httpd_util:lookup(ConfigDB, server_name).
83
84which_method(#mod{method = Method}) ->
85    Method.
86
87which_request_uri(#mod{request_uri = RUri}) ->
88    RUri.
89
90create_basic_elements(esi, ModData) ->
91    [{server_software,   which_server(ModData)},
92     {server_name,       which_name(ModData)},
93     {host_name,         which_resolve(ModData)},
94     {gateway_interface, ?GATEWAY_INTERFACE},
95     {server_protocol,   ?SERVER_PROTOCOL},
96     {server_port,       which_port(ModData)},
97     {request_method,    which_method(ModData)},
98     {remote_addr,       which_peername(ModData)},
99     {peer_cert,         which_peercert(ModData)},
100     {script_name,       which_request_uri(ModData)}];
101
102create_basic_elements(cgi, ModData) ->
103    [{"SERVER_SOFTWARE",   which_server(ModData)},
104     {"SERVER_NAME",       which_name(ModData)},
105     {"HOST_NAME",         which_resolve(ModData)},
106     {"GATEWAY_INTERFACE", ?GATEWAY_INTERFACE},
107     {"SERVER_PROTOCOL",   ?SERVER_PROTOCOL},
108     {"SERVER_PORT",       integer_to_list(which_port(ModData))},
109     {"REQUEST_METHOD",    which_method(ModData)},
110     {"REMOTE_ADDR",       which_peername(ModData)},
111     {"SCRIPT_NAME",       which_request_uri(ModData)}].
112
113create_http_header_elements(ScriptType, Headers) ->
114    create_http_header_elements(ScriptType, Headers, [], []).
115
116create_http_header_elements(esi, [], Acc, OtherAcc) ->
117    [{http_other, OtherAcc} | Acc];
118create_http_header_elements(_, [], Acc, _OtherAcc) ->
119    Acc;
120create_http_header_elements(ScriptType, [{Name, [Value | _] = Values } |
121					 Headers], Acc, OtherAcc)
122  when is_list(Value) ->
123    try http_env_element(ScriptType, Name, multi_value(Values)) of
124        Element ->
125            create_http_header_elements(ScriptType, Headers, [Element | Acc],
126                                        OtherAcc)
127    catch
128        _:_ ->
129            create_http_header_elements(ScriptType, Headers, Acc,
130                                        [{Name, Values} | OtherAcc])
131    end;
132create_http_header_elements(ScriptType, [{Name, Value} | Headers], Acc, OtherAcc)
133  when is_list(Value) ->
134    try http_env_element(ScriptType, Name, Value) of
135        Element ->
136            create_http_header_elements(ScriptType, Headers, [Element | Acc],
137                                       OtherAcc)
138    catch
139        _:_ ->
140            create_http_header_elements(ScriptType, Headers, Acc,
141                                       [{Name, Value} | OtherAcc])
142    end.
143
144http_env_element(cgi, VarName0, Value)  ->
145    VarName = re:replace(VarName0,"-","_", [{return,list}, global]),
146    {"HTTP_"++ http_util:to_upper(VarName), Value};
147http_env_element(esi, VarName0, Value)  ->
148    list_to_existing_atom(VarName0),
149    VarName = re:replace(VarName0,"-","_", [{return,list}, global]),
150    HeaderName = http_util:to_lower(VarName),
151    {list_to_atom("http_"++ HeaderName), Value}.
152
153multi_value([]) ->
154  [];
155multi_value([Value]) ->
156  Value;
157multi_value([Value | Rest]) ->
158  Value ++ ", " ++ multi_value(Rest).
159
160create_script_elements(ScriptType, ModData, ScriptElements) ->
161    lists:flatmap(fun({Element, Data}) ->
162			  create_script_elements(ScriptType,
163						 Element,
164						 Data, ModData)
165		  end, ScriptElements).
166
167create_script_elements(esi, query_string, QueryString, _) ->
168    [{query_string, QueryString}];
169create_script_elements(cgi, query_string, QueryString, _) ->
170    [{"QUERY_STRING", QueryString}];
171create_script_elements(esi, path_info, PathInfo, ModData) ->
172    Aliases = httpd_util:multi_lookup(ModData#mod.config_db, alias),
173    {_,PathTranslated,_} =
174	mod_alias:real_name(ModData#mod.config_db, PathInfo,
175			    Aliases),
176    [{path_info, PathInfo},
177     {path_translated, PathTranslated}];
178create_script_elements(cgi, path_info, PathInfo, ModData) ->
179    Aliases = httpd_util:multi_lookup(ModData#mod.config_db, alias),
180    {_,PathTranslated,_} =
181	mod_alias:real_name(ModData#mod.config_db, PathInfo,
182			    Aliases),
183    [{"PATH_INFO", PathInfo},
184     {"PATH_TRANSLATED", PathTranslated}];
185create_script_elements(esi, entity_body, Body, _) ->
186    [{content_length, integer_to_list(erlang:iolist_size(Body))}];
187create_script_elements(cgi, entity_body, Body, _) ->
188    [{"CONTENT_LENGTH", integer_to_list(erlang:iolist_size(Body))}];
189create_script_elements(_, _, _, _) ->
190    [].
191
192create_mod_interaction_elements(_, ModData)->
193    case proplists:get_value(remote_user, ModData#mod.data) of
194	undefined ->
195	    [];
196	RemoteUser ->
197	    [{remote_user, RemoteUser}]
198    end.
199