1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1997-2016. 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-module(httpd_conf).
22
23%% Application internal API
24-export([load_mime_types/1, store/1, store/2,
25	 remove/1, remove_all/1, get_config/3, get_config/4,
26	 lookup_socket_type/1,
27	 lookup/2, lookup/3, lookup/4,
28	 validate_properties/1, white_space_clean/1]).
29
30-define(VMODULE,"CONF").
31-include("httpd_internal.hrl").
32-include("httpd.hrl").
33-include_lib("inets/src/http_lib/http_internal.hrl").
34
35%% Removed functions
36
37-removed([{check_enum,2,"use lists:member/2 instead"},
38          {clean,1,"use sting:strip/1 instead or possibly the re module"},
39          {custom_clean,3,"use sting:strip/1 instead or possibly the re module"},
40          {is_directory,1,"use filelib:is_dir/1 instead"},
41          {is_file,1,"use filelib:is_file/1 instead"},
42          {make_integer,1,"use erlang:list_to_integer/1 instead"}]).
43
44%%%=========================================================================
45%%%  Application internal API
46%%%=========================================================================
47%% The configuration data is handled in three (2) phases:
48%% 1. Traverse the key-value tuple list store it into an ETS table.
49%%    Directives depending on other directives are taken care of here
50%%    (store/1).
51%% 3. Traverse the ETS table and do a complete clean-up (remove/1).
52
53validate_ipfamily(inet) ->
54    inet;
55validate_ipfamily(inet6) ->
56    inet6;
57%% Backwards compatibility wrapper,
58%% fallback to the default, IPV4,
59%% as it will most proably work.
60%% IPv6 standard moved away from
61%% beeing able to fallback to ipv4
62validate_ipfamily(inet6fb4) ->
63    inet;
64validate_ipfamily(IpFamilyStr) ->
65    throw({error, {bad_ipfamily, IpFamilyStr}}).
66
67%%
68%% load_mime_types/1 -> {ok, MimeTypes} | {error, Reason}
69%%
70load_mime_types(MimeTypesFile) ->
71    case file:open(MimeTypesFile, [read]) of
72	{ok, Stream} ->
73	    parse_mime_types(Stream, []);
74	{error, _} ->
75	    {error, ?NICE("Can't open " ++ MimeTypesFile)}
76    end.
77
78
79validate_properties(Properties) ->
80    %% First, check that all mandatory properties are present
81    case mandatory_properties(Properties) of
82	ok ->
83	    %% Second, check that property dependency are ok
84	    {ok, check_minimum_bytes_per_second(validate_properties2(Properties))};
85	Error ->
86	    throw(Error)
87    end.
88
89%% This function is used to validate inter-property dependencies.
90%% That is, if property A depends on property B.
91%% The only sunch preperty at this time is bind_address that depends
92%% on ipfamily.
93validate_properties2(Properties0) ->
94    Properties = fix_ipfamily(Properties0),
95    case proplists:get_value(bind_address, Properties) of
96	undefined ->
97	    case proplists:get_value(sock_type, Properties, ip_comm) of
98		ip_comm ->
99		   add_inet_defaults(Properties);
100		{ip_comm, _} ->
101		    add_inet_defaults(Properties);
102		_ ->
103		    [{bind_address, any} | Properties]
104	    end;
105	any ->
106	    Properties;
107	Address0 ->
108	    IpFamily = proplists:get_value(ipfamily, Properties, inet),
109	    case httpd_util:ip_address(Address0, IpFamily) of
110		{ok, Address} ->
111		    Properties1 = proplists:delete(bind_address, Properties),
112		    [{bind_address, Address} | Properties1];
113		{error, Reason} ->
114		    Error = {error,
115			     {failed_determine_ip_address,
116			      Address0, IpFamily, Reason}},
117		    throw(Error)
118	    end
119    end.
120
121fix_ipfamily(Properties) ->
122    case proplists:get_value(ipfamily, Properties) of
123	undefined ->
124	    Properties;
125	IpFamily ->
126	    NewProps = proplists:delete(ipfamily, Properties),
127	    [{ipfamily, validate_ipfamily(IpFamily)} | NewProps]
128    end.
129
130add_inet_defaults(Properties) ->
131    case proplists:get_value(ipfamily, Properties) of
132	undefined ->
133	    [{bind_address, any},
134	     {ipfamily, inet} | Properties];
135	_ ->
136	    [{bind_address, any} | Properties]
137    end.
138
139check_minimum_bytes_per_second(Properties) ->
140    case proplists:get_value(minimum_bytes_per_second, Properties, false) of
141	false ->
142	    Properties;
143	Nr ->
144	    case is_integer(Nr) of
145		false ->
146		    throw({error, {minimum_bytes_per_second, is_not_integer}});
147		_ ->
148		    Properties
149	    end
150    end.
151mandatory_properties(ConfigList) ->
152    a_must(ConfigList, [server_name, port, server_root, document_root]).
153
154a_must(_ConfigList, []) ->
155    ok;
156a_must(ConfigList, [Prop | Rest]) ->
157    case proplists:get_value(Prop, ConfigList) of
158	undefined ->
159	    {error, {missing_property, Prop}};
160	_ ->
161	    a_must(ConfigList, Rest)
162    end.
163
164
165validate_config_params([]) ->
166    ok;
167validate_config_params([{max_header_size, Value} | Rest])
168  when is_integer(Value) andalso (Value > 0) ->
169    validate_config_params(Rest);
170validate_config_params([{max_header_size, Value} | _]) ->
171    throw({max_header_size, Value});
172
173validate_config_params([{max_body_size, Value} | Rest])
174  when is_integer(Value) andalso (Value > 0) ->
175    validate_config_params(Rest);
176validate_config_params([{max_body_size, Value} | _]) ->
177    throw({max_body_size, Value});
178
179validate_config_params([{max_content_length, Value} | Rest])
180  when is_integer(Value) andalso (Value > 0) ->
181    validate_config_params(Rest);
182validate_config_params([{max_content_length, Value} | _]) ->
183    throw({max_content_length, Value});
184
185validate_config_params([{server_name, Value} | Rest])
186  when is_list(Value) ->
187    validate_config_params(Rest);
188validate_config_params([{server_name, Value} | _]) ->
189    throw({server_name, Value});
190
191validate_config_params([{server_tokens, Value} | Rest])
192  when is_atom(Value) ->
193    case lists:member(Value, plain_server_tokens()) of
194	true ->
195	    validate_config_params(Rest);
196	false ->
197	    throw({server_tokens, Value})
198    end;
199validate_config_params([{server_tokens, {private, Value}} | Rest])
200  when is_list(Value) ->
201    validate_config_params(Rest);
202validate_config_params([{server_tokens, Value} | _]) ->
203    throw({server_tokens, Value});
204
205validate_config_params([{socket_type, ip_comm} | Rest]) ->
206    validate_config_params(Rest);
207
208validate_config_params([{socket_type, {Value, Opts}} | Rest]) when Value == ip_comm;
209								   Value == ssl;
210								   Value == essl ->
211    %% Make sure not to set socket values used internaly
212    validate_config_params(Opts),
213    validate_config_params(Rest);
214
215validate_config_params([{socket_type, Value} | _]) ->
216    throw({socket_type, Value});
217
218validate_config_params([{port, Value} | Rest])
219  when is_integer(Value) andalso (Value >= 0) ->
220    validate_config_params(Rest);
221validate_config_params([{port, Value} | _]) ->
222    throw({port, Value});
223
224validate_config_params([{bind_address, Value} | Rest])  ->
225    case is_bind_address(Value) of
226	true ->
227	    validate_config_params(Rest);
228	false ->
229	    throw({bind_address, Value})
230    end;
231
232validate_config_params([{ipfamily, Value} | Rest])
233  when ((Value =:= inet)  orelse
234	(Value =:= inet6) orelse
235	(Value =:= inet6fb4)) ->
236    validate_config_params(Rest);
237validate_config_params([{ipfamily, Value} | _]) ->
238    throw({ipfamily, Value});
239
240validate_config_params([{keep_alive, Value} | Rest])
241  when (Value =:= true) orelse (Value =:= false) ->
242    validate_config_params(Rest);
243validate_config_params([{keep_alive, Value} | _]) ->
244    throw({keep_alive, Value});
245
246validate_config_params([{max_keep_alive_request, Value} | Rest])
247  when is_integer(Value) andalso (Value > 0) ->
248    validate_config_params(Rest);
249validate_config_params([{max_keep_alive_request, Value} | _]) ->
250    throw({max_keep_alive_request, Value});
251
252validate_config_params([{keep_alive_timeout, Value} | Rest])
253  when is_integer(Value) andalso (Value >= 0) ->
254    validate_config_params(Rest);
255validate_config_params([{keep_alive_timeout, Value} | _]) ->
256    throw({keep_alive_timeout, Value});
257
258validate_config_params([{modules, Value} | Rest]) ->
259    ok = httpd_util:modules_validate(Value),
260    validate_config_params(Rest);
261
262validate_config_params([{server_admin, Value} | Rest]) when is_list(Value) ->
263    validate_config_params(Rest);
264validate_config_params([{server_admin, Value} | _]) ->
265    throw({server_admin, Value});
266
267validate_config_params([{server_root, Value} | Rest]) ->
268    ok = httpd_util:dir_validate(server_root, Value),
269    validate_config_params(Rest);
270
271validate_config_params([{mime_types, Value} | Rest]) ->
272    ok = httpd_util:mime_types_validate(Value),
273    validate_config_params(Rest);
274
275validate_config_params([{max_clients, Value} | Rest])
276  when is_integer(Value) andalso (Value > 0) ->
277    validate_config_params(Rest);
278validate_config_params([{max_clients, Value} | _]) ->
279    throw({max_clients, Value});
280
281validate_config_params([{document_root, Value} | Rest]) ->
282    ok = httpd_util:dir_validate(document_root, Value),
283    validate_config_params(Rest);
284
285validate_config_params([{default_type, Value} | Rest]) when is_list(Value) ->
286    validate_config_params(Rest);
287validate_config_params([{default_type, Value} | _]) ->
288    throw({default_type, Value});
289
290validate_config_params([{logger, Value} | Rest]) when is_list(Value) ->
291    true = validate_logger(Value),
292    validate_config_params(Rest);
293validate_config_params([{logger, Value} | _]) ->
294    throw({logger, Value});
295
296validate_config_params([{ssl_certificate_file = Key, Value} | Rest]) ->
297    ok = httpd_util:file_validate(Key, Value),
298    validate_config_params(Rest);
299
300validate_config_params([{ssl_certificate_key_file = Key, Value} | Rest]) ->
301    ok = httpd_util:file_validate(Key, Value),
302    validate_config_params(Rest);
303
304validate_config_params([{ssl_verify_client, Value} | Rest])
305  when (Value =:= 0) orelse (Value =:= 1) orelse (Value =:= 2) ->
306    validate_config_params(Rest);
307
308validate_config_params([{ssl_verify_client_depth, Value} | Rest])
309  when is_integer(Value) andalso (Value >= 0) ->
310    validate_config_params(Rest);
311validate_config_params([{ssl_verify_client_depth, Value} | _]) ->
312    throw({ssl_verify_client_depth, Value});
313
314validate_config_params([{ssl_ciphers, Value} | Rest]) when is_list(Value) ->
315    validate_config_params(Rest);
316validate_config_params([{ssl_ciphers, Value} | _]) ->
317    throw({ssl_ciphers, Value});
318
319validate_config_params([{ssl_ca_certificate_file = Key, Value} | Rest]) ->
320    ok = httpd_util:file_validate(Key, Value),
321    validate_config_params(Rest);
322
323validate_config_params([{ssl_password_callback_module, Value} | Rest])
324  when is_atom(Value) ->
325    validate_config_params(Rest);
326validate_config_params([{ssl_password_callback_module, Value} | _]) ->
327    throw({ssl_password_callback_module, Value});
328
329validate_config_params([{ssl_password_callback_function, Value} | Rest])
330  when is_atom(Value) ->
331    validate_config_params(Rest);
332validate_config_params([{ssl_password_callback_function, Value} | _]) ->
333    throw({ssl_password_callback_function, Value});
334
335validate_config_params([{ssl_password_callback_arguments, Value} | Rest])
336  when is_list(Value) ->
337    validate_config_params(Rest);
338validate_config_params([{ssl_password_callback_arguments, Value} | _]) ->
339    throw({ssl_password_callback_arguments, Value});
340
341validate_config_params([{disable_chunked_transfer_encoding_send, Value} |
342			Rest])
343  when (Value =:= true) orelse (Value =:= false) ->
344    validate_config_params(Rest);
345validate_config_params([{disable_chunked_transfer_encoding_send, Value} |
346			_ ]) ->
347    throw({disable_chunked_transfer_encoding_send, Value});
348validate_config_params([{Name, _} = Opt | _]) when Name == packet;
349						   Name == mode;
350						   Name == active;
351						   Name == reuseaddr ->
352    throw({internaly_handled_opt_can_not_be_set, Opt});
353validate_config_params([_| Rest]) ->
354    validate_config_params(Rest).
355
356is_bind_address(any) ->
357    true;
358is_bind_address(Value) ->
359    case is_bind_address(Value, inet) of
360	false ->
361	    is_bind_address(Value, inet6);
362	True ->
363	    True
364    end.
365
366is_bind_address(Value, IpFamily) ->
367    case httpd_util:ip_address(Value, IpFamily) of
368	{ok, _} ->
369	    true;
370	_ ->
371	    false
372    end.
373
374store(ConfigList0) ->
375    try validate_config_params(ConfigList0) of
376	ok ->
377	    Modules =
378		proplists:get_value(modules, ConfigList0, ?DEFAULT_MODS),
379	    Port = proplists:get_value(port, ConfigList0),
380	    Addr = proplists:get_value(bind_address, ConfigList0, any),
381	    Profile = proplists:get_value(profile, ConfigList0, default),
382	    ConfigList = fix_mime_types(ConfigList0),
383	    Name = httpd_util:make_name("httpd_conf", Addr, Port, Profile),
384	    ConfigDB = ets:new(Name, [named_table, bag, protected]),
385	    store(ConfigDB, ConfigList,
386		  lists:append(Modules, [?MODULE]),
387		  ConfigList)
388    catch
389	throw:Error ->
390	    {error, {invalid_option, Error}}
391    end.
392
393fix_mime_types(ConfigList0) ->
394    case proplists:get_value(mime_types, ConfigList0) of
395	undefined ->
396	    ServerRoot = proplists:get_value(server_root, ConfigList0),
397		MimeTypesFile =
398		filename:join([ServerRoot,"conf", "mime.types"]),
399		case filelib:is_file(MimeTypesFile) of
400		    true ->
401			{ok, MimeTypesList} = load_mime_types(MimeTypesFile),
402			[{mime_types, MimeTypesList} | ConfigList0];
403		    false ->
404			[{mime_types,
405			  [{"html","text/html"},{"htm","text/html"}]}
406			 | ConfigList0]
407		end;
408    MimeTypes ->
409        case filelib:is_file(MimeTypes) of
410            true ->
411                {ok, MimeTypesList} = load_mime_types(MimeTypes),
412                ConfigList = proplists:delete(mime_types, ConfigList0),
413                [{mime_types, MimeTypesList} | ConfigList];
414            false ->
415                ConfigList0
416        end
417    end.
418
419store({mime_types,MimeTypesList},ConfigList) ->
420    Port = proplists:get_value(port, ConfigList),
421    Addr = proplists:get_value(bind_address, ConfigList),
422    Name = httpd_util:make_name("httpd_mime",Addr,Port),
423    {ok, MimeTypesDB} = store_mime_types(Name,MimeTypesList),
424    {ok, {mime_types,MimeTypesDB}};
425store({log_format, LogFormat}, _ConfigList)
426  when (LogFormat =:= common) orelse (LogFormat =:= combined) ->
427    {ok,{log_format, LogFormat}};
428store({log_format, LogFormat}, _ConfigList)
429  when (LogFormat =:= compact) orelse (LogFormat =:= pretty) ->
430    {ok, {log_format, LogFormat}};
431store({server_tokens, ServerTokens} = Entry, _ConfigList) ->
432    Server = server(ServerTokens),
433    {ok, [Entry, {server, Server}]};
434store({keep_alive_timeout, KeepAliveTimeout}, _ConfigList) ->
435    {ok, {keep_alive_timeout, KeepAliveTimeout}};
436store(ConfigListEntry, _ConfigList) ->
437    {ok, ConfigListEntry}.
438
439
440%% The SERVER_SOFTWARE macro has the following structure:
441%%       <product>/<version>
442%% Example: "inets/1.2.3"
443%% So, with this example (on a linux machine, with OTP R15B),
444%% this will result in:
445%%   prod:    "inets"
446%%   major:   "inets/1"
447%%   minor:   "inets/1.2"
448%%   minimal: "inets/1.2.3"
449%%   os:      "inets/1.2.3 (unix)
450%%   full:    "inets/1.2.3 (unix/linux) OTP/R15B"
451%% Note that the format of SERVER_SOFTWARE is that of 'minimal'.
452%% Also, there will always be atleast two digits in a version:
453%% Not just 1 but 1.0
454%%
455%% We have already checked that the value is valid,
456%% so there is no need to check enything here.
457%%
458server(prod = _ServerTokens) ->
459    [Prod|_Version] = string:tokens(?SERVER_SOFTWARE, [$/]),
460    Prod;
461server(major = _ServerTokens) ->
462    [Prod|Version] = string:tokens(?SERVER_SOFTWARE, [$/]),
463    [Major|_]      = string:tokens(Version, [$.]),
464    Prod ++ "/" ++ Major;
465server(minor = _ServerTokens) ->
466    [Prod|Version]  = string:tokens(?SERVER_SOFTWARE, [$/]),
467    [Major,Minor|_] = string:tokens(Version, [$.]),
468    Prod ++ "/" ++ Major ++ "." ++ Minor;
469server(minimal = _ServerTokens) ->
470    %% This is the default
471    ?SERVER_SOFTWARE;
472server(os = _ServerTokens) ->
473    OS = os_info(partial),
474    lists:flatten(io_lib:format("~s ~s", [?SERVER_SOFTWARE, OS]));
475server(full = _ServerTokens) ->
476    OTPRelease = otp_release(),
477    OS = os_info(full),
478    lists:flatten(
479      io_lib:format("~s ~s OTP/~s", [?SERVER_SOFTWARE, OS, OTPRelease]));
480server(none = _ServerTokens) ->
481    "";
482server({private, Server} = _ServerTokens) when is_list(Server) ->
483    %% The user provide its own
484    Server;
485server(_) ->
486    ?SERVER_SOFTWARE.
487
488os_info(Info) ->
489    case os:type() of
490	{OsFamily, _OsName} when Info =:= partial ->
491	    lists:flatten(io_lib:format("(~w)", [OsFamily]));
492	{OsFamily, OsName} ->
493	    lists:flatten(io_lib:format("(~w/~w)", [OsFamily, OsName]))
494    end.
495
496otp_release() ->
497    erlang:system_info(otp_release).
498
499
500%% Phase 3: Remove
501remove_all(ConfigDB) ->
502    Modules = httpd_util:lookup(ConfigDB,modules,[]),
503    remove_traverse(ConfigDB, lists:append(Modules,[?MODULE])).
504
505remove(ConfigDB) ->
506    ets:delete(ConfigDB),
507    ok.
508
509
510get_config(Address, Port, Profile) ->
511    Tab = httpd_util:make_name("httpd_conf", Address, Port, Profile),
512    Properties =  ets:tab2list(Tab),
513    MimeTab = proplists:get_value(mime_types, Properties),
514    NewProperties = proplists:delete(mime_types, Properties),
515    [{mime_types, ets:tab2list(MimeTab)} | NewProperties].
516
517get_config(Address, Port, Profile, Properties) ->
518    Tab = httpd_util:make_name("httpd_conf", Address, Port, Profile),
519    Config =
520	lists:map(fun(Prop) -> {Prop, httpd_util:lookup(Tab, Prop)} end,
521		  Properties),
522    [{Proporty, Value} || {Proporty, Value} <- Config, Value =/= undefined].
523
524
525lookup(Tab, Key) ->
526    httpd_util:lookup(Tab, Key).
527
528lookup(Tab, Key, Default) when is_atom(Key) ->
529    httpd_util:lookup(Tab, Key, Default);
530
531lookup(Address, Port, Key) when is_integer(Port) ->
532    Tab = table(Address, Port),
533    lookup(Tab, Key).
534
535lookup(Address, Port, Key, Default) when is_integer(Port) ->
536    Tab = table(Address, Port),
537    lookup(Tab, Key, Default).
538
539table(Address, Port) ->
540    httpd_util:make_name("httpd_conf", Address, Port).
541
542
543lookup_socket_type(ConfigDB) ->
544    case httpd_util:lookup(ConfigDB, socket_type, ip_comm) of
545	ip_comm ->
546	    ip_comm;
547	{ip_comm, _} = Type ->
548	    Type;
549	{Tag, Conf} ->
550	    {Tag, Conf};
551	SSL when (SSL =:= ssl) orelse (SSL =:= essl) ->
552	    SSLTag =
553		if
554		    (SSL =:= ssl) ->
555			?HTTP_DEFAULT_SSL_KIND;
556		    true ->
557			SSL
558		end,
559	    case ssl_certificate_file(ConfigDB) of
560		undefined ->
561		    Reason = "Directive SSLCertificateFile "
562			"not found in the config file",
563		    throw({error, Reason});
564		SSLCertificateFile ->
565		    {SSLTag, SSLCertificateFile ++ ssl_config(ConfigDB)}
566	    end
567    end.
568
569ssl_config(ConfigDB) ->
570    ssl_certificate_key_file(ConfigDB) ++
571	ssl_verify_client(ConfigDB) ++
572	ssl_ciphers(ConfigDB) ++
573	ssl_password(ConfigDB) ++
574	ssl_verify_depth(ConfigDB) ++
575	ssl_ca_certificate_file(ConfigDB) ++
576	ssl_log_level(ConfigDB).
577
578%%%========================================================================
579%%% Internal functions
580%%%========================================================================
581parse_mime_types(Stream,MimeTypesList) ->
582    Line=
583	case io:get_line(Stream,'') of
584	    eof ->
585		eof;
586	    String ->
587		re:replace(white_space_clean(String), "[\t\r\f ]"," ", [{return,list}, global])
588	end,
589    parse_mime_types(Stream, MimeTypesList, Line).
590parse_mime_types(Stream, MimeTypesList, eof) ->
591    file:close(Stream),
592    {ok, MimeTypesList};
593parse_mime_types(Stream, MimeTypesList, "") ->
594    parse_mime_types(Stream, MimeTypesList);
595parse_mime_types(Stream, MimeTypesList, [$#|_]) ->
596    parse_mime_types(Stream, MimeTypesList);
597parse_mime_types(Stream, MimeTypesList, Line) ->
598    case re:split(Line, " ", [{return, list}]) of
599	[NewMimeType|Suffixes] ->
600	    parse_mime_types(Stream,
601			     lists:append(suffixes(NewMimeType,Suffixes),
602					  MimeTypesList));
603	_ ->
604	    {error, ?NICE(Line)}
605    end.
606
607suffixes(_MimeType,[]) ->
608    [];
609suffixes(MimeType,[""|Rest]) ->
610    suffixes(MimeType, Rest);
611suffixes(MimeType,[Suffix|Rest]) ->
612    [{Suffix,MimeType}|suffixes(MimeType,Rest)].
613
614
615%% Phase 2: store
616store(ConfigDB, _ConfigList, _Modules, []) ->
617    {ok, ConfigDB};
618store(ConfigDB, ConfigList, Modules, [ConfigListEntry|Rest]) ->
619    case store_traverse(ConfigListEntry, ConfigList, Modules) of
620	{ok, ConfigDBEntry} when is_tuple(ConfigDBEntry) ->
621	    ets:insert(ConfigDB, ConfigDBEntry),
622	    store(ConfigDB, ConfigList, Modules, Rest);
623	{ok, ConfigDBEntry} when is_list(ConfigDBEntry) ->
624	    lists:foreach(fun(Entry) ->
625				  ets:insert(ConfigDB,Entry)
626			  end,ConfigDBEntry),
627	    store(ConfigDB, ConfigList, Modules, Rest);
628	{error, Reason} ->
629	    {error,Reason}
630    end.
631
632store_traverse(_ConfigListEntry, _ConfigList,[]) ->
633    {error, ?NICE("Unable to store configuration...")};
634store_traverse(ConfigListEntry, ConfigList, [Module|Rest]) ->
635    case catch apply(Module, store, [ConfigListEntry, ConfigList]) of
636	{'EXIT',{function_clause,_}} ->
637	    store_traverse(ConfigListEntry,ConfigList,Rest);
638	{'EXIT',{undef, _}} ->
639	    store_traverse(ConfigListEntry,ConfigList,Rest);
640	{'EXIT', Reason} ->
641	    error_logger:error_report({'EXIT',Reason}),
642	    store_traverse(ConfigListEntry,ConfigList,Rest);
643	Result ->
644	    Result
645    end.
646
647store_mime_types(Name,MimeTypesList) ->
648    %% Make sure that the ets table is not duplicated
649    %% when reloading configuration
650    catch ets:delete(Name),
651    MimeTypesDB = ets:new(Name, [named_table, set, protected]),
652    store_mime_types1(MimeTypesDB, MimeTypesList).
653store_mime_types1(MimeTypesDB,[]) ->
654    {ok, MimeTypesDB};
655store_mime_types1(MimeTypesDB,[Type|Rest]) ->
656    ets:insert(MimeTypesDB, Type),
657    store_mime_types1(MimeTypesDB, Rest).
658
659
660%% Phase 3: remove
661remove_traverse(_ConfigDB,[]) ->
662    ok;
663remove_traverse(ConfigDB,[Module|Rest]) ->
664    case (catch apply(Module,remove,[ConfigDB])) of
665	{'EXIT',{undef,_}} ->
666	    remove_traverse(ConfigDB,Rest);
667	{'EXIT',{function_clause,_}} ->
668	    remove_traverse(ConfigDB,Rest);
669	{'EXIT',Reason} ->
670	    error_logger:error_report({'EXIT',Reason}),
671	    remove_traverse(ConfigDB,Rest);
672	{error,Reason} ->
673	    error_logger:error_report(Reason),
674	    remove_traverse(ConfigDB,Rest);
675	_ ->
676	    remove_traverse(ConfigDB,Rest)
677    end.
678
679ssl_certificate_file(ConfigDB) ->
680    case httpd_util:lookup(ConfigDB,ssl_certificate_file) of
681	undefined ->
682	    undefined;
683	SSLCertificateFile ->
684	    [{certfile,SSLCertificateFile}]
685    end.
686
687ssl_certificate_key_file(ConfigDB) ->
688    case httpd_util:lookup(ConfigDB,ssl_certificate_key_file) of
689	undefined ->
690	    [];
691	SSLCertificateKeyFile ->
692	    [{keyfile,SSLCertificateKeyFile}]
693    end.
694
695ssl_log_level(ConfigDB) ->
696    case httpd_util:lookup(ConfigDB,ssl_log_alert) of
697	undefined ->
698	    [];
699	SSLLogLevel ->
700	    [{log_alert,SSLLogLevel}]
701    end.
702
703ssl_verify_client(ConfigDB) ->
704    case httpd_util:lookup(ConfigDB,ssl_verify_client) of
705	undefined ->
706	    [];
707	SSLVerifyClient ->
708	    [{verify,SSLVerifyClient}]
709    end.
710
711ssl_ciphers(ConfigDB) ->
712    case httpd_util:lookup(ConfigDB,ssl_ciphers) of
713	undefined ->
714	    [];
715	Ciphers ->
716	    [{ciphers, Ciphers}]
717    end.
718
719ssl_password(ConfigDB) ->
720    case httpd_util:lookup(ConfigDB,ssl_password_callback_module) of
721	undefined ->
722	    [];
723	Module ->
724	    case httpd_util:lookup(ConfigDB,
725				   ssl_password_callback_function) of
726		undefined ->
727		    [];
728		Function ->
729		    Args = case httpd_util:lookup(ConfigDB,
730					   ssl_password_callback_arguments) of
731			       undefined ->
732				   [];
733			       Arguments  ->
734				   [Arguments]
735			   end,
736
737		    case catch apply(Module, Function, Args) of
738			Password when is_list(Password) ->
739			    [{password, Password}];
740			Error ->
741			    error_report(ssl_password,Module,Function,Error),
742			    []
743		    end
744	    end
745    end.
746
747ssl_verify_depth(ConfigDB) ->
748    case httpd_util:lookup(ConfigDB, ssl_verify_client_depth) of
749	undefined ->
750	    [];
751	Depth ->
752	    [{depth, Depth}]
753    end.
754
755ssl_ca_certificate_file(ConfigDB) ->
756    case httpd_util:lookup(ConfigDB, ssl_ca_certificate_file) of
757	undefined ->
758	    [];
759	File ->
760	    [{cacertfile, File}]
761    end.
762
763plain_server_tokens() ->
764    [none, prod, major, minor, minimum, os, full].
765
766error_report(Where,M,F,Error) ->
767    error_logger:error_report([{?MODULE, Where},
768			       {apply, {M, F, []}}, Error]).
769white_space_clean(String) ->
770    re:replace(String, "^[ \t\n\r\f]*|[ \t\n\r\f]*\$","",
771	       [{return,list}, global]).
772
773validate_logger([{error, Domain}]) when is_atom(Domain) ->
774    true;
775validate_logger(List) ->
776    throw({logger, List}).
777
778
779
780
781
782