~ubuntu-branches/debian/wheezy/couchdb/wheezy

« back to all changes in this revision

Viewing changes to src/couch_inets/httpd_conf.erl

  • Committer: Bazaar Package Importer
  • Author(s): Noah Slater
  • Date: 2008-02-06 17:03:38 UTC
  • Revision ID: james.westby@ubuntu.com-20080206170338-y411anylx3oplqid
Tags: upstream-0.7.3~svn684
ImportĀ upstreamĀ versionĀ 0.7.3~svn684

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
%% ``The contents of this file are subject to the Erlang Public License,
 
2
%% Version 1.1, (the "License"); you may not use this file except in
 
3
%% compliance with the License. You should have received a copy of the
 
4
%% Erlang Public License along with this software. If not, it can be
 
5
%% retrieved via the world wide web at http://www.erlang.org/.
 
6
%% 
 
7
%% Software distributed under the License is distributed on an "AS IS"
 
8
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 
9
%% the License for the specific language governing rights and limitations
 
10
%% under the License.
 
11
%% 
 
12
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
 
13
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
 
14
%% AB. All Rights Reserved.''
 
15
%% 
 
16
%%     $Id$
 
17
%%
 
18
-module(httpd_conf).
 
19
 
 
20
%% EWSAPI 
 
21
-export([is_directory/1, is_file/1, make_integer/1, clean/1, 
 
22
         custom_clean/3, check_enum/2]).
 
23
 
 
24
%% Application internal API
 
25
-export([load/1, load/2, load_mime_types/1, store/1, store/2,
 
26
        remove/1, remove_all/1, config/1]).
 
27
 
 
28
-define(VMODULE,"CONF").
 
29
-include("httpd.hrl").
 
30
 
 
31
%%%=========================================================================
 
32
%%%  EWSAPI
 
33
%%%=========================================================================
 
34
%%-------------------------------------------------------------------------
 
35
%%  is_directory(FilePath) -> Result
 
36
%%      FilePath = string()
 
37
%%      Result = {ok,Directory} | {error,Reason}
 
38
%%      Directory = string()
 
39
%%      Reason = string() | enoent | eaccess | enotdir | FileInfo
 
40
%%      FileInfo = File info record
 
41
%%
 
42
%% Description: Checks if FilePath is a directory in which case it is
 
43
%% returned. 
 
44
%%-------------------------------------------------------------------------
 
45
is_directory(Directory) ->
 
46
    case file:read_file_info(Directory) of
 
47
        {ok,FileInfo} ->
 
48
            #file_info{type = Type, access = Access} = FileInfo,
 
49
            is_directory(Type,Access,FileInfo,Directory);
 
50
        {error,Reason} ->
 
51
            {error,Reason}
 
52
    end.
 
53
is_directory(directory,read,_FileInfo,Directory) ->
 
54
    {ok,Directory};
 
55
is_directory(directory,read_write,_FileInfo,Directory) ->
 
56
    {ok,Directory};
 
57
is_directory(_Type,_Access,FileInfo,_Directory) ->
 
58
    {error,FileInfo}.
 
59
%%-------------------------------------------------------------------------
 
60
%% is_file(FilePath) -> Result
 
61
%%      FilePath = string()
 
62
%%      Result = {ok,File} | {error,Reason}
 
63
%%      File = string()
 
64
%%      Reason = string() | enoent | eaccess | enotdir | FileInfo
 
65
%%      FileInfo = File info record
 
66
%%
 
67
%% Description: Checks if FilePath is a regular file in which case it
 
68
%% is returned.
 
69
%%-------------------------------------------------------------------------
 
70
is_file(File) ->
 
71
    case file:read_file_info(File) of
 
72
        {ok,FileInfo} ->
 
73
            #file_info{type = Type, access = Access} = FileInfo,
 
74
            is_file(Type,Access,FileInfo,File);
 
75
        {error,Reason} ->
 
76
            {error,Reason}
 
77
    end.
 
78
is_file(regular,read,_FileInfo,File) ->
 
79
    {ok,File};
 
80
is_file(regular,read_write,_FileInfo,File) ->
 
81
    {ok,File};
 
82
is_file(_Type,_Access,FileInfo,_File) ->
 
83
    {error,FileInfo}.
 
84
%%-------------------------------------------------------------------------
 
85
%% make_integer(String) -> Result
 
86
%% String = string()
 
87
%% Result = {ok,integer()} | {error,nomatch}
 
88
%%
 
89
%% Description: make_integer/1 returns an integer representation of String. 
 
90
%%-------------------------------------------------------------------------
 
91
make_integer(String) ->
 
92
    case regexp:match(clean(String),"[0-9]+") of
 
93
        {match, _, _} ->
 
94
            {ok, list_to_integer(clean(String))};
 
95
        nomatch ->
 
96
            {error, nomatch}
 
97
    end.
 
98
%%-------------------------------------------------------------------------
 
99
%% clean(String) -> Stripped
 
100
%% String = Stripped = string()
 
101
%%
 
102
%% Description:clean/1 removes leading and/or trailing white spaces
 
103
%% from String.
 
104
%%-------------------------------------------------------------------------
 
105
clean(String) ->
 
106
    {ok,CleanedString,_} = 
 
107
        regexp:gsub(String, "^[ \t\n\r\f]*|[ \t\n\r\f]*\$",""),
 
108
    CleanedString.
 
109
%%-------------------------------------------------------------------------
 
110
%% custom_clean(String,Before,After) -> Stripped
 
111
%% Before = After = regexp()
 
112
%% String = Stripped = string()
 
113
%%
 
114
%% Description: custom_clean/3 removes leading and/or trailing white
 
115
%% spaces and custom characters from String. 
 
116
%%-------------------------------------------------------------------------
 
117
custom_clean(String,MoreBefore,MoreAfter) ->
 
118
    {ok,CleanedString,_} = regexp:gsub(String,"^[ \t\n\r\f"++MoreBefore++
 
119
                                       "]*|[ \t\n\r\f"++MoreAfter++"]*\$",""),
 
120
    CleanedString.
 
121
%%-------------------------------------------------------------------------
 
122
%% check_enum(EnumString,ValidEnumStrings) -> Result
 
123
%%      EnumString = string()
 
124
%%      ValidEnumStrings = [string()]
 
125
%%      Result = {ok,atom()} | {error,not_valid}
 
126
%%
 
127
%% Description: check_enum/2 checks if EnumString is a valid
 
128
%% enumeration of ValidEnumStrings in which case it is returned as an
 
129
%% atom.
 
130
%%-------------------------------------------------------------------------
 
131
check_enum(_Enum,[]) ->
 
132
    {error, not_valid};
 
133
check_enum(Enum,[Enum|_Rest]) ->
 
134
    {ok, list_to_atom(Enum)};
 
135
check_enum(Enum, [_NotValid|Rest]) ->
 
136
    check_enum(Enum, Rest).
 
137
 
 
138
%%%=========================================================================
 
139
%%%  Application internal API
 
140
%%%=========================================================================
 
141
%% The configuration data is handled in three (3) phases:
 
142
%% 1. Parse the config file and put all directives into a key-vale
 
143
%%    tuple list (load/1). 
 
144
%% 2. Traverse the key-value tuple list store it into an ETS table.
 
145
%%    Directives depending on other directives are taken care of here
 
146
%%    (store/1).
 
147
%% 3. Traverse the ETS table and do a complete clean-up (remove/1).
 
148
 
 
149
%% Phase 1: Load
 
150
load(ConfigFile) ->
 
151
    case read_config_file(ConfigFile) of
 
152
        {ok, Config} ->
 
153
            case bootstrap(Config) of
 
154
                {error, Reason} ->
 
155
                    {error, Reason};
 
156
                {ok, Modules} ->
 
157
                    load_config(Config, lists:append(Modules, [?MODULE]))
 
158
            end;
 
159
        {error, Reason} ->
 
160
            {error, ?NICE("Error while reading config file: "++Reason)}
 
161
    end.
 
162
 
 
163
load(eof, []) ->
 
164
    eof;
 
165
load("MaxHeaderSize " ++ MaxHeaderSize, []) ->
 
166
    case make_integer(MaxHeaderSize) of
 
167
        {ok, Integer} ->
 
168
            {ok, [], {max_header_size,Integer}};
 
169
        {error, _} ->
 
170
            {error, ?NICE(clean(MaxHeaderSize)++
 
171
                          " is an invalid number of MaxHeaderSize")}
 
172
    end;
 
173
load("MaxHeaderAction " ++ Action, []) ->
 
174
    {ok, [], {max_header_action,list_to_atom(clean(Action))}};
 
175
load("MaxBodySize " ++ MaxBodySize, []) ->
 
176
    case make_integer(MaxBodySize) of
 
177
        {ok, Integer} ->
 
178
            {ok, [], {max_body_size,Integer}};
 
179
        {error, _} ->
 
180
            {error, ?NICE(clean(MaxBodySize)++
 
181
                          " is an invalid number of MaxBodySize")}
 
182
    end;
 
183
load("MaxBodyAction " ++ Action, []) ->
 
184
    {ok, [], {max_body_action,list_to_atom(clean(Action))}};
 
185
load("ServerName " ++ ServerName, []) ->
 
186
    {ok,[],{server_name,clean(ServerName)}};
 
187
load("SocketType " ++ SocketType, []) ->
 
188
    case check_enum(clean(SocketType),["ssl","ip_comm"]) of
 
189
        {ok, ValidSocketType} ->
 
190
            {ok, [], {com_type,ValidSocketType}};
 
191
        {error,_} ->
 
192
            {error, ?NICE(clean(SocketType) ++ " is an invalid SocketType")}
 
193
    end;
 
194
load("Port " ++ Port, []) ->
 
195
    case make_integer(Port) of
 
196
        {ok, Integer} ->
 
197
            {ok, [], {port,Integer}};
 
198
        {error, _} ->
 
199
            {error, ?NICE(clean(Port)++" is an invalid Port")}
 
200
    end;
 
201
load("BindAddress " ++ Address, []) ->
 
202
    %% If an ipv6 address is provided in URL-syntax strip the
 
203
    %% url specific part e.i. "[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]"
 
204
    %% -> "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210"
 
205
    NewAddress = string:strip(string:strip(clean(Address), 
 
206
                                           left, $[), 
 
207
                              right, $]),
 
208
    case NewAddress of
 
209
        "*" ->
 
210
            {ok, [], {bind_address,any}};
 
211
        CAddress ->
 
212
            case (catch inet:getaddr(CAddress,inet6)) of
 
213
                {ok, {0, 0, 0, 0, 0, 16#ffff, _, _}} ->
 
214
                    case inet:getaddr(CAddress, inet) of
 
215
                        {ok, IPAddr} ->
 
216
                            {ok, [], {bind_address,IPAddr}};
 
217
                        {error, _} ->
 
218
                            {error, ?NICE(CAddress++" is an invalid address")}
 
219
                    end;
 
220
                {ok, IPAddr} ->
 
221
                    {ok, [], {bind_address, IPAddr}};
 
222
                _ ->
 
223
                    case inet:getaddr(CAddress, inet) of
 
224
                        {ok, IPAddr} ->
 
225
                            {ok, [], {bind_address,IPAddr}};
 
226
                        {error, _} ->
 
227
                            {error, ?NICE(CAddress++" is an invalid address")}
 
228
                    end
 
229
            end
 
230
    end;
 
231
load("KeepAlive " ++ OnorOff, []) ->
 
232
    case list_to_atom(clean(OnorOff)) of
 
233
        off ->
 
234
            {ok, [], {persistent_conn, false}};
 
235
        _ ->
 
236
            {ok, [], {persistent_conn, true}}
 
237
    end;
 
238
load("MaxKeepAliveRequests " ++  MaxRequests, []) ->
 
239
    case make_integer(MaxRequests) of
 
240
        {ok, Integer} ->
 
241
            {ok, [], {max_keep_alive_request, Integer}};
 
242
        {error, _} ->
 
243
            {error, ?NICE(clean(MaxRequests) ++
 
244
                          " is an invalid MaxKeepAliveRequests")}
 
245
    end;
 
246
%% This clause is keept for backwards compability 
 
247
load("MaxKeepAliveRequest " ++  MaxRequests, []) ->
 
248
    case make_integer(MaxRequests) of
 
249
        {ok, Integer} ->
 
250
            {ok, [], {max_keep_alive_request, Integer}};
 
251
        {error, _} ->
 
252
            {error, ?NICE(clean(MaxRequests) ++
 
253
                          " is an invalid MaxKeepAliveRequest")}
 
254
    end;
 
255
load("KeepAliveTimeout " ++ Timeout, []) ->
 
256
    case make_integer(Timeout) of
 
257
        {ok, Integer} ->
 
258
            {ok, [], {keep_alive_timeout, Integer*1000}};
 
259
        {error, _} ->
 
260
            {error, ?NICE(clean(Timeout)++" is an invalid KeepAliveTimeout")}
 
261
    end;
 
262
load("Modules " ++ Modules, []) ->
 
263
    {ok, ModuleList} = regexp:split(Modules," "),
 
264
    {ok, [], {modules,[list_to_atom(X) || X <- ModuleList]}};
 
265
load("ServerAdmin " ++ ServerAdmin, []) ->
 
266
    {ok, [], {server_admin,clean(ServerAdmin)}};
 
267
load("ServerRoot " ++ ServerRoot, []) ->
 
268
    case is_directory(clean(ServerRoot)) of
 
269
        {ok, Directory} ->
 
270
            MimeTypesFile = 
 
271
                filename:join([clean(ServerRoot),"conf", "mime.types"]),
 
272
            case load_mime_types(MimeTypesFile) of
 
273
                {ok, MimeTypesList} ->
 
274
                    {ok, [], [{server_root,string:strip(Directory,right,$/)},
 
275
                              {mime_types,MimeTypesList}]};
 
276
                {error, Reason} ->
 
277
                    {error, Reason}
 
278
            end;
 
279
        {error, _} ->
 
280
            {error, ?NICE(clean(ServerRoot)++" is an invalid ServerRoot")}
 
281
    end;
 
282
load("MaxClients " ++ MaxClients, []) ->
 
283
    case make_integer(MaxClients) of
 
284
        {ok, Integer} ->
 
285
            {ok, [], {max_clients,Integer}};
 
286
        {error, _} ->
 
287
            {error, ?NICE(clean(MaxClients) ++
 
288
                          " is an invalid number of MaxClients")}
 
289
    end;
 
290
load("DocumentRoot " ++ DocumentRoot,[]) ->
 
291
    case is_directory(clean(DocumentRoot)) of
 
292
        {ok, Directory} ->
 
293
            {ok, [], {document_root,string:strip(Directory,right,$/)}};
 
294
        {error, _} ->
 
295
            {error, ?NICE(clean(DocumentRoot)++"is an invalid DocumentRoot")}
 
296
    end;
 
297
load("DefaultType " ++ DefaultType, []) ->
 
298
    {ok, [], {default_type,clean(DefaultType)}};
 
299
load("SSLCertificateFile " ++ SSLCertificateFile, []) ->
 
300
    case is_file(clean(SSLCertificateFile)) of
 
301
        {ok, File} ->
 
302
            {ok, [], {ssl_certificate_file,File}};
 
303
    {error, _} ->
 
304
            {error, ?NICE(clean(SSLCertificateFile)++
 
305
                          " is an invalid SSLCertificateFile")}
 
306
    end;
 
307
load("SSLCertificateKeyFile " ++ SSLCertificateKeyFile, []) ->
 
308
    case is_file(clean(SSLCertificateKeyFile)) of
 
309
        {ok, File} ->
 
310
            {ok, [], {ssl_certificate_key_file,File}};
 
311
        {error, _} ->
 
312
            {error, ?NICE(clean(SSLCertificateKeyFile)++
 
313
                          " is an invalid SSLCertificateKeyFile")}
 
314
    end;
 
315
load("SSLVerifyClient " ++ SSLVerifyClient, []) ->
 
316
    case make_integer(clean(SSLVerifyClient)) of
 
317
        {ok, Integer} when Integer >=0,Integer =< 2 ->
 
318
            {ok, [], {ssl_verify_client,Integer}};
 
319
        {ok, _Integer} ->
 
320
            {error,?NICE(clean(SSLVerifyClient) ++
 
321
                         " is an invalid SSLVerifyClient")};
 
322
        {error, nomatch} ->
 
323
            {error,?NICE(clean(SSLVerifyClient) ++ 
 
324
                         " is an invalid SSLVerifyClient")}
 
325
    end;
 
326
load("SSLVerifyDepth " ++ SSLVerifyDepth, []) ->
 
327
    case make_integer(clean(SSLVerifyDepth)) of
 
328
        {ok, Integer} when Integer > 0 ->
 
329
            {ok, [], {ssl_verify_client_depth,Integer}};
 
330
        {ok, _Integer} ->
 
331
            {error,?NICE(clean(SSLVerifyDepth) ++
 
332
                         " is an invalid SSLVerifyDepth")};
 
333
        {error, nomatch} ->
 
334
            {error,?NICE(clean(SSLVerifyDepth) ++
 
335
                         " is an invalid SSLVerifyDepth")}
 
336
    end;
 
337
load("SSLCiphers " ++ SSLCiphers, []) ->
 
338
    {ok, [], {ssl_ciphers, clean(SSLCiphers)}};
 
339
load("SSLCACertificateFile " ++ SSLCACertificateFile, []) ->
 
340
    case is_file(clean(SSLCACertificateFile)) of
 
341
        {ok, File} ->
 
342
            {ok, [], {ssl_ca_certificate_file,File}};
 
343
        {error, _} ->
 
344
            {error, ?NICE(clean(SSLCACertificateFile)++
 
345
                          " is an invalid SSLCACertificateFile")}
 
346
    end;
 
347
load("SSLPasswordCallbackModule " ++ SSLPasswordCallbackModule, []) ->
 
348
    {ok, [], {ssl_password_callback_module,
 
349
              list_to_atom(clean(SSLPasswordCallbackModule))}};
 
350
load("SSLPasswordCallbackFunction " ++ SSLPasswordCallbackFunction, []) ->
 
351
    {ok, [], {ssl_password_callback_function,
 
352
              list_to_atom(clean(SSLPasswordCallbackFunction))}};
 
353
load("DisableChunkedTransferEncodingSend " ++ TrueOrFalse, []) ->
 
354
    case list_to_atom(clean(TrueOrFalse)) of
 
355
        true ->
 
356
            {ok, [], {disable_chunked_transfer_encoding_send, true}};
 
357
        _ ->
 
358
            {ok, [], {disable_chunked_transfer_encoding_send, false}}
 
359
    end.
 
360
 
 
361
%%
 
362
%% load_mime_types/1 -> {ok, MimeTypes} | {error, Reason}
 
363
%%
 
364
load_mime_types(MimeTypesFile) ->
 
365
    case file:open(MimeTypesFile, read) of
 
366
        {ok, Stream} ->
 
367
            parse_mime_types(Stream, []);
 
368
        {error, _} ->
 
369
            {error, ?NICE("Can't open " ++ MimeTypesFile)}
 
370
    end.
 
371
 
 
372
%% Phase 2: Store
 
373
store(ConfigList) ->
 
374
    Modules = httpd_util:key1search(ConfigList, modules, []),
 
375
    Port = httpd_util:key1search(ConfigList, port),
 
376
    Addr = httpd_util:key1search(ConfigList,bind_address),
 
377
    Name = httpd_util:make_name("httpd_conf",Addr,Port),
 
378
    ConfigDB = ets:new(Name, [named_table, bag, protected]),
 
379
    store(ConfigDB, ConfigList, lists:append(Modules,[?MODULE]),ConfigList).
 
380
 
 
381
store({mime_types,MimeTypesList},ConfigList) ->
 
382
    Port = httpd_util:key1search(ConfigList, port),
 
383
    Addr = httpd_util:key1search(ConfigList, bind_address),
 
384
    Name = httpd_util:make_name("httpd_mime",Addr,Port),
 
385
    {ok, MimeTypesDB} = store_mime_types(Name,MimeTypesList),
 
386
    {ok, {mime_types,MimeTypesDB}};
 
387
store(ConfigListEntry, _ConfigList) ->
 
388
    {ok, ConfigListEntry}.
 
389
 
 
390
%% Phase 3: Remove
 
391
remove_all(ConfigDB) ->
 
392
    Modules = httpd_util:lookup(ConfigDB,modules,[]),
 
393
    remove_traverse(ConfigDB, lists:append(Modules,[?MODULE])).
 
394
 
 
395
remove(ConfigDB) ->
 
396
    ets:delete(ConfigDB),
 
397
    ok.
 
398
 
 
399
config(ConfigDB) ->
 
400
    case httpd_util:lookup(ConfigDB,com_type,ip_comm) of
 
401
        ssl ->
 
402
            case ssl_certificate_file(ConfigDB) of
 
403
                undefined ->
 
404
                    {error,
 
405
                     "Directive SSLCertificateFile "
 
406
                     "not found in the config file"};
 
407
                SSLCertificateFile ->
 
408
                    {ssl,
 
409
                     SSLCertificateFile++
 
410
                     ssl_certificate_key_file(ConfigDB)++
 
411
                     ssl_verify_client(ConfigDB)++
 
412
                     ssl_ciphers(ConfigDB)++
 
413
                     ssl_password(ConfigDB)++
 
414
                     ssl_verify_depth(ConfigDB)++
 
415
                     ssl_ca_certificate_file(ConfigDB)}
 
416
            end;
 
417
        ip_comm ->
 
418
            ip_comm
 
419
    end.
 
420
 
 
421
%%%========================================================================
 
422
%%% Internal functions
 
423
%%%========================================================================
 
424
%%% Phase 1 Load:
 
425
bootstrap([]) ->
 
426
    {error, ?NICE("Modules must be specified in the config file")};
 
427
bootstrap([Line|Config]) ->
 
428
    case Line of
 
429
        "Modules " ++ Modules ->
 
430
            {ok, ModuleList} = regexp:split(Modules," "),
 
431
            TheMods = [list_to_atom(X) || X <- ModuleList],
 
432
            case verify_modules(TheMods) of
 
433
                ok ->
 
434
                    {ok, TheMods};
 
435
                {error, Reason} ->
 
436
                    {error, Reason}
 
437
            end;
 
438
        _ ->
 
439
            bootstrap(Config)
 
440
    end.
 
441
 
 
442
load_config(Config, Modules) ->
 
443
    %% Create default contexts for all modules
 
444
    Contexts = lists:duplicate(length(Modules), []),
 
445
    load_config(Config, Modules, Contexts, []).
 
446
load_config([], _Modules, _Contexts, ConfigList) ->
 
447
    case a_must(ConfigList, [server_name,port,server_root,document_root]) of
 
448
        ok ->
 
449
            {ok, ConfigList};
 
450
        {missing, Directive} ->
 
451
            {error, ?NICE(atom_to_list(Directive)++
 
452
                          " must be specified in the config file")}
 
453
    end;
 
454
load_config([Line|Config], Modules, Contexts, ConfigList) ->
 
455
    case load_traverse(Line, Contexts, Modules, [], ConfigList, no) of
 
456
        {ok, NewContexts, NewConfigList} ->
 
457
            load_config(Config, Modules, NewContexts, NewConfigList);
 
458
        {error, Reason} -> 
 
459
            {error, Reason}
 
460
    end.
 
461
 
 
462
 
 
463
%% This loads the config file into each module specified by Modules
 
464
%% Each module has its own context that is passed to and (optionally)
 
465
%% returned by the modules load function. The module can also return
 
466
%% a ConfigEntry, which will be added to the global configuration
 
467
%% list.
 
468
%% All configuration directives are guaranteed to be passed to all
 
469
%% modules. Each module only implements the function clauses of
 
470
%% the load function for the configuration directives it supports,
 
471
%% it's ok if an apply returns {'EXIT', {function_clause, ..}}.
 
472
load_traverse(Line, [], [], _NewContexts, _ConfigList, no) ->
 
473
    {error, ?NICE("Configuration directive not recognized: "++Line)};
 
474
load_traverse(_Line, [], [], NewContexts, ConfigList, yes) ->
 
475
    {ok, lists:reverse(NewContexts), ConfigList};
 
476
load_traverse(Line, [Context|Contexts], [Module|Modules], NewContexts,
 
477
              ConfigList, State) ->
 
478
    case catch apply(Module, load, [Line, Context]) of
 
479
        {'EXIT', {function_clause, _}} ->
 
480
            load_traverse(Line, Contexts, Modules, 
 
481
                          [Context|NewContexts], ConfigList, State);
 
482
        {'EXIT',{undef, _}} ->
 
483
            load_traverse(Line, Contexts, Modules,
 
484
                          [Context|NewContexts], ConfigList,yes);
 
485
        {'EXIT', Reason} ->
 
486
            error_logger:error_report({'EXIT', Reason}),
 
487
            load_traverse(Line, Contexts, Modules, 
 
488
                          [Context|NewContexts], ConfigList, State);
 
489
        {ok, NewContext} ->
 
490
            load_traverse(Line, Contexts, Modules, 
 
491
                          [NewContext|NewContexts], ConfigList,yes);
 
492
        {ok, NewContext, ConfigEntry} when tuple(ConfigEntry) ->
 
493
            load_traverse(Line, Contexts, Modules, [NewContext|NewContexts],
 
494
                          [ConfigEntry|ConfigList], yes);
 
495
        {ok, NewContext, ConfigEntry} when list(ConfigEntry) ->
 
496
            load_traverse(Line, Contexts, Modules, [NewContext|NewContexts],
 
497
                          lists:append(ConfigEntry, ConfigList), yes);
 
498
        {error, Reason} ->
 
499
            {error, Reason}
 
500
    end.
 
501
        
 
502
%% Verifies that all specified modules are available.
 
503
verify_modules([]) ->
 
504
    ok;
 
505
verify_modules([Mod|Rest]) ->
 
506
    case code:which(Mod) of
 
507
        non_existing ->
 
508
            {error, ?NICE(atom_to_list(Mod)++" does not exist")};
 
509
        _Path ->
 
510
            verify_modules(Rest)
 
511
    end.
 
512
 
 
513
%% Reads the entire configuration file and returns list of strings or
 
514
%% and error.
 
515
read_config_file(FileName) ->
 
516
    case file:open(FileName, read) of
 
517
        {ok, Stream} ->
 
518
            read_config_file(Stream, []);
 
519
        {error, _Reason} ->
 
520
            {error, ?NICE("Cannot open "++FileName)}
 
521
    end.
 
522
read_config_file(Stream, SoFar) ->
 
523
    case io:get_line(Stream, []) of
 
524
        eof ->
 
525
            file:close(Stream),
 
526
            {ok, lists:reverse(SoFar)};
 
527
        {error, Reason} ->
 
528
            file:close(Stream),
 
529
            {error, Reason};
 
530
        [$#|_Rest] ->
 
531
            %% Ignore commented lines for efficiency later ..
 
532
            read_config_file(Stream, SoFar);
 
533
        Line ->
 
534
            {ok, NewLine, _}=regexp:sub(clean(Line),"[\t\r\f ]"," "),
 
535
            case NewLine of
 
536
                [] ->
 
537
                    %% Also ignore empty lines ..
 
538
                    read_config_file(Stream, SoFar);
 
539
                _Other ->
 
540
                    read_config_file(Stream, [NewLine|SoFar])
 
541
            end
 
542
    end.
 
543
 
 
544
parse_mime_types(Stream,MimeTypesList) ->
 
545
    Line=
 
546
        case io:get_line(Stream,'') of
 
547
            eof ->
 
548
                eof;
 
549
            String ->
 
550
                clean(String)
 
551
        end,
 
552
    parse_mime_types(Stream, MimeTypesList, Line).
 
553
parse_mime_types(Stream, MimeTypesList, eof) ->
 
554
    file:close(Stream),
 
555
    {ok, MimeTypesList};
 
556
parse_mime_types(Stream, MimeTypesList, "") ->
 
557
    parse_mime_types(Stream, MimeTypesList);
 
558
parse_mime_types(Stream, MimeTypesList, [$#|_]) ->
 
559
    parse_mime_types(Stream, MimeTypesList);
 
560
parse_mime_types(Stream, MimeTypesList, Line) ->
 
561
    case regexp:split(Line, " ") of
 
562
        {ok, [NewMimeType|Suffixes]} ->
 
563
            parse_mime_types(Stream,
 
564
                             lists:append(suffixes(NewMimeType,Suffixes),
 
565
                                          MimeTypesList));
 
566
        {ok, _} ->
 
567
            {error, ?NICE(Line)}
 
568
    end.
 
569
 
 
570
suffixes(_MimeType,[]) ->
 
571
    [];
 
572
suffixes(MimeType,[Suffix|Rest]) ->
 
573
    [{Suffix,MimeType}|suffixes(MimeType,Rest)].
 
574
 
 
575
a_must(_ConfigList,[]) ->
 
576
    ok;
 
577
a_must(ConfigList,[Directive|Rest]) ->
 
578
    case httpd_util:key1search(ConfigList,Directive) of
 
579
        undefined ->
 
580
            {missing,Directive};
 
581
        _ ->
 
582
            a_must(ConfigList,Rest)
 
583
    end.
 
584
 
 
585
%% Pahse 2: store
 
586
store(ConfigDB, _ConfigList, _Modules,[]) ->
 
587
    {ok, ConfigDB};
 
588
store(ConfigDB, ConfigList, Modules, [ConfigListEntry|Rest]) ->
 
589
    case store_traverse(ConfigListEntry,ConfigList,Modules) of
 
590
        {ok, ConfigDBEntry} when tuple(ConfigDBEntry) ->
 
591
            ets:insert(ConfigDB,ConfigDBEntry),
 
592
            store(ConfigDB,ConfigList,Modules,Rest);
 
593
        {ok, ConfigDBEntry} when list(ConfigDBEntry) ->
 
594
            lists:foreach(fun(Entry) ->
 
595
                                  ets:insert(ConfigDB,Entry)
 
596
                          end,ConfigDBEntry),
 
597
            store(ConfigDB,ConfigList,Modules,Rest);
 
598
        {error, Reason} ->
 
599
            {error,Reason}
 
600
    end.
 
601
 
 
602
store_traverse(_ConfigListEntry, _ConfigList,[]) ->
 
603
    {error,?NICE("Unable to store configuration...")};
 
604
store_traverse(ConfigListEntry, ConfigList, [Module|Rest]) ->
 
605
    case catch apply(Module,store,[ConfigListEntry, ConfigList]) of
 
606
        {'EXIT',{function_clause,_}} ->
 
607
            store_traverse(ConfigListEntry,ConfigList,Rest);
 
608
        {'EXIT',{undef, _}} ->
 
609
            store_traverse(ConfigListEntry,ConfigList,Rest);
 
610
        {'EXIT', Reason} ->
 
611
            error_logger:error_report({'EXIT',Reason}),
 
612
            store_traverse(ConfigListEntry,ConfigList,Rest);
 
613
        Result ->
 
614
            Result
 
615
    end.
 
616
 
 
617
store_mime_types(Name,MimeTypesList) ->
 
618
    %% Make sure that the ets table is not duplicated
 
619
    %% when reloading configuration
 
620
    catch ets:delete(Name),
 
621
    MimeTypesDB = ets:new(Name, [named_table, set, protected]),
 
622
    store_mime_types1(MimeTypesDB, MimeTypesList).
 
623
store_mime_types1(MimeTypesDB,[]) ->
 
624
    {ok, MimeTypesDB};
 
625
store_mime_types1(MimeTypesDB,[Type|Rest]) ->
 
626
    ets:insert(MimeTypesDB, Type),
 
627
    store_mime_types1(MimeTypesDB, Rest).
 
628
 
 
629
 
 
630
%% Phase 3: remove
 
631
remove_traverse(_ConfigDB,[]) ->
 
632
    ok;
 
633
remove_traverse(ConfigDB,[Module|Rest]) ->
 
634
    case (catch apply(Module,remove,[ConfigDB])) of
 
635
        {'EXIT',{undef,_}} ->
 
636
            remove_traverse(ConfigDB,Rest);
 
637
        {'EXIT',{function_clause,_}} ->
 
638
            remove_traverse(ConfigDB,Rest);
 
639
        {'EXIT',Reason} ->
 
640
            error_logger:error_report({'EXIT',Reason}),
 
641
            remove_traverse(ConfigDB,Rest);
 
642
        {error,Reason} ->
 
643
            error_logger:error_report(Reason),
 
644
            remove_traverse(ConfigDB,Rest);
 
645
        _ ->
 
646
            remove_traverse(ConfigDB,Rest)
 
647
    end.
 
648
 
 
649
ssl_certificate_file(ConfigDB) ->
 
650
    case httpd_util:lookup(ConfigDB,ssl_certificate_file) of
 
651
        undefined ->
 
652
            undefined;
 
653
        SSLCertificateFile ->
 
654
            [{certfile,SSLCertificateFile}]
 
655
    end.
 
656
 
 
657
ssl_certificate_key_file(ConfigDB) ->
 
658
    case httpd_util:lookup(ConfigDB,ssl_certificate_key_file) of
 
659
        undefined ->
 
660
            [];
 
661
        SSLCertificateKeyFile ->
 
662
            [{keyfile,SSLCertificateKeyFile}]
 
663
    end.
 
664
 
 
665
ssl_verify_client(ConfigDB) ->
 
666
    case httpd_util:lookup(ConfigDB,ssl_verify_client) of
 
667
        undefined ->
 
668
            [];
 
669
        SSLVerifyClient ->
 
670
            [{verify,SSLVerifyClient}]
 
671
    end.
 
672
 
 
673
ssl_ciphers(ConfigDB) ->
 
674
    case httpd_util:lookup(ConfigDB,ssl_ciphers) of
 
675
        undefined ->
 
676
            [];
 
677
        Ciphers ->
 
678
            [{ciphers, Ciphers}]
 
679
    end.
 
680
 
 
681
ssl_password(ConfigDB) ->
 
682
    case httpd_util:lookup(ConfigDB,ssl_password_callback_module) of
 
683
        undefined ->
 
684
            [];
 
685
        Module ->
 
686
            case httpd_util:lookup(ConfigDB, 
 
687
                                   ssl_password_callback_function) of
 
688
                undefined ->
 
689
                    [];
 
690
                Function ->
 
691
                    case catch apply(Module, Function, []) of
 
692
                        Password when list(Password) ->
 
693
                            [{password, Password}];
 
694
                        Error ->
 
695
                            error_report(ssl_password,Module,Function,Error),
 
696
                            []
 
697
                    end
 
698
            end
 
699
    end.
 
700
 
 
701
ssl_verify_depth(ConfigDB) ->
 
702
    case httpd_util:lookup(ConfigDB, ssl_verify_client_depth) of
 
703
        undefined ->
 
704
            [];
 
705
        Depth ->
 
706
            [{depth, Depth}]
 
707
    end.
 
708
 
 
709
ssl_ca_certificate_file(ConfigDB) ->
 
710
    case httpd_util:lookup(ConfigDB, ssl_ca_certificate_file) of
 
711
        undefined ->
 
712
            [];
 
713
        File ->
 
714
            [{cacertfile, File}]
 
715
    end.
 
716
 
 
717
error_report(Where,M,F,Error) ->
 
718
    error_logger:error_report([{?MODULE, Where}, 
 
719
                               {apply, {M, F, []}}, Error]).
 
720