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
%%%% Software distributed under the License is distributed on an "AS IS"
7
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
8
%% the License for the specific language governing rights and limitations
11
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
12
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
13
%% AB. All Rights Reserved.''
18
-module(mod_htaccess).
20
-export([do/1, load/2]).
22
-include("httpd.hrl").
24
%% We will not make the change to use base64 in stdlib in inets just yet.
25
%% it will be included in the next major release of inets.
26
-compile({nowarn_deprecated_function, {http_base_64, encode, 1}}).
28
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
29
%% Public methods that interface the eswapi %%
30
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
32
%----------------------------------------------------------------------
33
% Public method called by the webbserver to insert the data about
34
% Names on accessfiles
35
%----------------------------------------------------------------------
36
load("AccessFileName" ++ FileNames, _Context)->
37
CleanFileNames=httpd_conf:clean(FileNames),
38
{ok,[],{access_files,string:tokens(CleanFileNames," ")}}.
41
%----------------------------------------------------------------------
42
% Public method that the webbserver calls to control the page
43
%----------------------------------------------------------------------
45
case httpd_util:key1search(Info#mod.data,status) of
46
{_Status_code, _PhraseArgs, _Reason}->
47
{proceed,Info#mod.data};
53
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
55
%% The functions that start the control if there is a accessfile %%
56
%% and if so controls if the dir is allowed or not %%
58
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
59
%----------------------------------------------------------------------
60
%Info = record mod as specified in httpd.hrl
61
%returns either {proceed,Info#mod.data}
62
%{proceed,[{status,403....}|Info#mod.data]}
63
%{proceed,[{status,401....}|Info#mod.data]}
64
%{proceed,[{status,500....}|Info#mod.data]}
65
%----------------------------------------------------------------------
67
Path = mod_alias:path(Info#mod.data,
69
Info#mod.request_uri),
70
case isErlScriptOrNotAccessibleFile(Path,Info) of
72
{proceed,Info#mod.data};
74
case getHtAccessData(Path,Info)of
76
%%There was no restrictions on the page continue
77
{proceed,Info#mod.data};
79
%%Something got wrong continue or quit??????????????????/
80
{proceed,Info#mod.data};
81
{accessData,AccessData}->
82
controlAllowedMethod(Info,AccessData)
87
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
89
%% These methods controls that the method the client used in the %%
90
%% request is one of the limited %%
92
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
93
%----------------------------------------------------------------------
94
%Control that if the accessmethod used is in the list of modes to challenge
96
%Info is the mod record as specified in httpd.hrl
97
%AccessData is an ets table whit the data in the .htaccessfiles
98
%----------------------------------------------------------------------
99
controlAllowedMethod(Info,AccessData)->
100
case allowedRequestMethod(Info,AccessData) of
102
%%The request didnt use one of the limited methods
103
ets:delete(AccessData),
104
{proceed,Info#mod.data};
106
authenticateUser(Info,AccessData)
109
%----------------------------------------------------------------------
110
%Check the specified access method in the .htaccessfile
111
%----------------------------------------------------------------------
112
allowedRequestMethod(Info,AccessData)->
113
case ets:lookup(AccessData,limit) of
117
isLimitedRequestMethod(Info,Methods)
121
%----------------------------------------------------------------------
122
%Check the specified accessmethods in the .htaccesfile against the users
125
%Info is the record from the do call
126
%Methods is a list of the methods specified in the .htaccessfile
127
%----------------------------------------------------------------------
128
isLimitedRequestMethod(Info,Methods)->
129
case lists:member(Info#mod.method,Methods) of
137
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
139
%% These methods controls that the user comes from an allowwed net %%
140
%% and if so wheather its a valid user or a challenge shall be %%
143
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
144
%----------------------------------------------------------------------
145
%The first thing to control is that the user is from a network
146
%that has access to the page
147
%----------------------------------------------------------------------
148
authenticateUser(Info,AccessData)->
149
case controlNet(Info,AccessData) of
151
%the network is ok control that it is an allowed user
152
authenticateUser2(Info,AccessData);
154
%The user isnt allowed to access the pages from that network
155
ets:delete(AccessData),
156
{proceed,[{status,{403,Info#mod.request_uri,
157
"Restricted area not allowed from your network"}}|Info#mod.data]}
161
%----------------------------------------------------------------------
162
%The network the user comes from is allowed to view the resources
163
%control whether the user needsto supply a password or not
164
%----------------------------------------------------------------------
165
authenticateUser2(Info,AccessData)->
166
case ets:lookup(AccessData,require) of
167
[{require,AllowedUsers}]->
168
case ets:lookup(AccessData,auth_name) of
169
[{auth_name,Realm}]->
170
authenticateUser2(Info,AccessData,Realm,AllowedUsers);
172
ets:delete(AccessData),
173
{break,[{status,{500,none,
174
?NICE("mod_htaccess:AuthName directive "
178
%%No special user is required the network is ok so let
180
ets:delete(AccessData),
181
{proceed,Info#mod.data}
185
%----------------------------------------------------------------------
186
%The user must send a userId and a password to get the resource
187
%Control if its already in the http-request
188
%if the file with users is bad send an 500 response
189
%----------------------------------------------------------------------
190
authenticateUser2(Info,AccessData,Realm,AllowedUsers)->
191
case authenticateUser(Info,AccessData,AllowedUsers) of
193
ets:delete(AccessData),
194
{user,Name, _Pwd} = getAuthenticatingDataFromHeader(Info),
195
{proceed, [{remote_user_name,Name}|Info#mod.data]};
197
ets:delete(AccessData),
198
ReasonPhrase = httpd_util:reason_phrase(401),
199
Message = httpd_util:message(401,none,Info#mod.config_db),
203
["WWW-Authenticate: Basic realm=\"",Realm,
204
"\"\r\n\r\n","<HTML>\n<HEAD>\n<TITLE>",
205
ReasonPhrase,"</TITLE>\n",
206
"</HEAD>\n<BODY>\n<H1>",ReasonPhrase,
207
"</H1>\n",Message,"\n</BODY>\n</HTML>\n"]}}|
210
ets:delete(AccessData),
211
{break,[{status,{500,none,
212
?NICE("mod_htaccess:Bad path to user "
217
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
219
%% Methods that validate the netwqork the user comes from %%
220
%% according to the allowed networks %%
222
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
223
%---------------------------------------------------------------------
224
%Controls the users networkaddress agains the specifed networks to
227
%returns either allow or deny
228
%----------------------------------------------------------------------
229
controlNet(Info,AccessData)->
230
UserNetwork=getUserNetworkAddress(Info),
231
case getAllowDenyOrder(AccessData) of
232
{_deny,[],_allow,[]}->
234
{deny,[],allow,AllowedNetworks}->
235
controlIfAllowed(AllowedNetworks,UserNetwork,allow,deny);
236
{allow,AllowedNetworks,deny,[]}->
237
controlIfAllowed(AllowedNetworks,UserNetwork,allow,deny);
239
{deny,DeniedNetworks,allow,[]}->
240
controlIfAllowed(DeniedNetworks,UserNetwork,allow,deny);
241
{allow,[],deny,DeniedNetworks}->
242
controlIfAllowed(DeniedNetworks,UserNetwork,allow,deny);
244
{deny,DeniedNetworks,allow,AllowedNetworks}->
245
controlDenyAllow(DeniedNetworks,AllowedNetworks,UserNetwork);
246
{allow,AllowedNetworks,deny,DeniedNetworks}->
247
controlAllowDeny(AllowedNetworks,DeniedNetworks,UserNetwork)
251
%----------------------------------------------------------------------
252
%Returns the users IP-Number
253
%----------------------------------------------------------------------
254
getUserNetworkAddress(Info)->
255
{_Socket,Address}=(Info#mod.init_data)#init_data.peername,
259
%----------------------------------------------------------------------
260
%Control the users Ip-number against the ip-numbers in the .htaccessfile
261
%----------------------------------------------------------------------
262
controlIfAllowed(AllowedNetworks,UserNetwork,IfAllowed,IfDenied)->
263
case AllowedNetworks of
269
memberNetwork(Networks,UserNetwork,IfDenied,IfAllowed);
271
memberNetwork(Networks,UserNetwork,IfAllowed,IfDenied);
277
%---------------------------------------------------------------------%
278
%The Denycontrol isn't neccessary to preform since the allow control %
279
%override the deny control %
280
%---------------------------------------------------------------------%
281
controlDenyAllow(_DeniedNetworks, AllowedNetworks, UserNetwork)->
282
case AllowedNetworks of
285
[{allow, Networks}]->
286
case memberNetwork(Networks, UserNetwork) of
295
%----------------------------------------------------------------------%
296
%Control that the user is in the allowed list if so control that the %
297
%network is in the denied list
298
%----------------------------------------------------------------------%
299
controlAllowDeny(AllowedNetworks,DeniedNetworks,UserNetwork)->
300
case controlIfAllowed(AllowedNetworks,UserNetwork,allow,deny) of
302
controlIfAllowed(DeniedNetworks,UserNetwork,deny,allow);
307
%----------------------------------------------------------------------
308
%Controls if the users Ipnumber is in the list of either denied or
310
%----------------------------------------------------------------------
311
memberNetwork(Networks,UserNetwork,IfTrue,IfFalse)->
312
case memberNetwork(Networks,UserNetwork) of
320
%----------------------------------------------------------------------
321
%regexp match the users ip-address against the networks in the list of
322
%ipadresses or subnet addresses.
323
memberNetwork(Networks,UserNetwork)->
324
case lists:filter(fun(Net)->
325
case regexp:match(UserNetwork,
326
formatRegexp(Net)) of
340
%----------------------------------------------------------------------
341
%Creates a regexp from an ip-number i.e "127.0.0-> "^127[.]0[.]0.*"
342
%"127.0.0.-> "^127[.]0[.]0[.].*"
343
%----------------------------------------------------------------------
345
[SubNet1|SubNets]=string:tokens(Net,"."),
346
NetRegexp=lists:foldl(fun(SubNet,Newnet)->
347
Newnet ++ "[.]" ++SubNet
348
end,"^"++SubNet1,SubNets),
349
case string:len(Net)-string:rchr(Net,$.) of
356
%----------------------------------------------------------------------
357
%If the user has specified if the allow or deny check shall be preformed
358
%first get that order if no order is specified take
359
%allow - deny since its harder that deny - allow
360
%----------------------------------------------------------------------
361
getAllowDenyOrder(AccessData)->
362
case ets:lookup(AccessData,order) of
363
[{order,{deny,allow}}]->
364
{deny,ets:lookup(AccessData,deny),
365
allow,ets:lookup(AccessData,allow)};
367
{allow,ets:lookup(AccessData,allow),
368
deny,ets:lookup(AccessData,deny)}
372
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
374
%% The methods that validates the user %%
376
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
378
%----------------------------------------------------------------------
379
%Control if there is anyu autheticating data in threquest header
380
%if so it controls it against the users in the list Allowed Users
381
%----------------------------------------------------------------------
382
authenticateUser(Info,AccessData,AllowedUsers)->
383
case getAuthenticatingDataFromHeader(Info) of
384
{user,User,PassWord}->
385
authenticateUser(Info,AccessData,AllowedUsers,
386
{user,User,PassWord});
394
%----------------------------------------------------------------------
395
%Returns the Autheticating data in the http-request
396
%----------------------------------------------------------------------
397
getAuthenticatingDataFromHeader(Info)->
398
PrsedHeader=Info#mod.parsed_header,
399
case httpd_util:key1search(PrsedHeader,"authorization" ) of
402
[$B,$a,$s,$i,$c,$\ |EncodedString] = Credentials ->
403
case (catch http_base_64:decode(EncodedString)) of
404
{'EXIT',{function_clause, _}} ->
405
{error, Credentials};
407
case httpd_util:split(UnCodedString,":",2) of
408
{ok,[User,PassWord]}->
409
{user,User,PassWord};
415
{error,BadCredentials}
418
%----------------------------------------------------------------------
419
%Returns a list of all members of the allowed groups
420
%----------------------------------------------------------------------
421
getGroupMembers(Groups,AllowedGroups)->
422
Allowed=lists:foldl(fun({group,Name,Members},AllowedMembers)->
423
case lists:member(Name,AllowedGroups) of
425
AllowedMembers++Members;
432
authenticateUser(Info,AccessData,{{users,[]},{groups,Groups}},User)->
433
authenticateUser(Info,AccessData,{groups,Groups},User);
434
authenticateUser(Info,AccessData,{{users,Users},{groups,[]}},User)->
435
authenticateUser(Info,AccessData,{users,Users},User);
437
authenticateUser(Info,AccessData,{{users,Users},{groups,Groups}},User)->
438
AllowUser=authenticateUser(Info,AccessData,{users,Users},User),
439
AllowGroup=authenticateUser(Info,AccessData,{groups,Groups},User),
440
case {AllowGroup,AllowUser} of
454
%----------------------------------------------------------------------
455
%Controls that the user is a member in one of the allowed group
456
%----------------------------------------------------------------------
457
authenticateUser(Info,AccessData,{groups,AllowedGroups},{user,User,PassWord})->
458
case getUsers(AccessData,group_file) of
459
{group_data,Groups}->
460
{ok, Members } = getGroupMembers(Groups,AllowedGroups),
461
authenticateUser(Info,AccessData,{users,Members},
462
{user,User,PassWord});
468
%----------------------------------------------------------------------
469
%Control that the user is one of the allowed users and that the passwd is ok
470
%----------------------------------------------------------------------
471
authenticateUser(_Info,AccessData,{users,AllowedUsers},{user,User,PassWord})->
472
case lists:member(User,AllowedUsers) of
474
%Get the usernames and passwords from the file
475
case getUsers(AccessData,user_file) of
479
%Users is a list of the users in
480
%the userfile [{user,User,Passwd}]
481
checkPassWord(Users,{user,User,PassWord})
488
%----------------------------------------------------------------------
489
%Control that the user User={user,"UserName","PassWd"} is
490
%member of the list of Users
491
%----------------------------------------------------------------------
492
checkPassWord(Users,User)->
493
case lists:member(User,Users) of
501
%----------------------------------------------------------------------
502
%Get the users in the specified file
503
%UserOrGroup is an atom that specify if its a group file or a user file
504
%i.e. group_file or user_file
505
%----------------------------------------------------------------------
506
getUsers({file,FileName},UserOrGroup)->
507
case file:open(FileName,[read]) of
508
{ok,AccessFileHandle} ->
509
getUsers({stream,AccessFileHandle},[],UserOrGroup);
511
{error,{Reason,FileName}}
515
%----------------------------------------------------------------------
516
%The method that starts the lokkong for user files
517
%----------------------------------------------------------------------
519
getUsers(AccessData,UserOrGroup)->
520
case ets:lookup(AccessData,UserOrGroup) of
521
[{UserOrGroup,File}]->
522
getUsers({file,File},UserOrGroup);
528
%----------------------------------------------------------------------
529
%Reads data from the filehandle File to the list FileData and when its
530
%reach the end it returns the list in a tuple {user_file|group_file,FileData}
531
%----------------------------------------------------------------------
532
getUsers({stream,File},FileData,UserOrGroup)->
533
case io:get_line(File,[]) of
534
eof when UserOrGroup==user_file->
535
{user_data,FileData};
536
eof when UserOrGroup ==group_file->
537
{group_data,FileData};
539
getUsers({stream,File},
540
formatUser(Line,FileData,UserOrGroup),UserOrGroup)
544
%----------------------------------------------------------------------
545
%If the line is a comment remove it
546
%----------------------------------------------------------------------
547
formatUser([$#|_UserDataComment],FileData,_UserOrgroup)->
551
%----------------------------------------------------------------------
552
%The user name in the file is Username:Passwd\n
553
%Remove the newline sign and split the user name in
554
%UserName and Password
555
%----------------------------------------------------------------------
556
formatUser(UserData,FileData,UserOrGroup)->
557
case string:tokens(UserData," \r\n")of
558
[User| _Whitespace] when UserOrGroup==user_file->
559
case string:tokens(User,":") of
561
[{user,Name,PassWord}|FileData];
565
GroupData when UserOrGroup==group_file ->
566
parseGroupData(GroupData,FileData);
572
%----------------------------------------------------------------------
573
%if everything is right GroupData is on the form
574
% ["groupName:", "Member1", "Member2", "Member2"
575
%----------------------------------------------------------------------
576
parseGroupData([GroupName|GroupData],FileData)->
577
[{group,formatGroupName(GroupName),GroupData}|FileData].
580
%----------------------------------------------------------------------
581
%the line in the file is GroupName: Member1 Member2 .....MemberN
582
%Remove the : from the group name
583
%----------------------------------------------------------------------
584
formatGroupName(GroupName)->
585
string:strip(GroupName,right,$:).
588
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
590
%% Functions that parses the accessfiles %%
592
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
593
%----------------------------------------------------------------------
594
%Control that the asset is a real file and not a request for an virtual
596
%----------------------------------------------------------------------
597
isErlScriptOrNotAccessibleFile(Path, _Info)->
598
case file:read_file_info(Path) of
606
%----------------------------------------------------------------------
607
%Path=PathToTheRequestedFile=String
609
%----------------------------------------------------------------------
610
getHtAccessData(Path,Info)->
611
HtAccessFileNames=getHtAccessFileNames(Info),
612
case getData(Path,Info,HtAccessFileNames) of
615
{accessData,AccessData}->
616
{accessData,AccessData};
622
%----------------------------------------------------------------------
623
%returns the names of the accessfiles
624
%----------------------------------------------------------------------
625
getHtAccessFileNames(Info)->
626
case httpd_util:lookup(Info#mod.config_db,access_files) of
632
%----------------------------------------------------------------------
633
%HtAccessFileNames=["accessfileName1",..."AccessFileName2"]
634
%----------------------------------------------------------------------
635
getData(Path,Info,HtAccessFileNames)->
636
case regexp:split(Path,"/") of
640
getData2(HtAccessFileNames,SplittedPath,Info)
644
%----------------------------------------------------------------------
645
%Add to together the data in the Splittedpath up to the path
646
%that is the alias or the document root
647
%Since we do not need to control after any accessfiles before here
648
%----------------------------------------------------------------------
649
getData2(HtAccessFileNames,SplittedPath,Info)->
650
case getRootPath(SplittedPath,Info) of
653
{ok,StartPath,RestOfSplittedPath} ->
654
getData2(HtAccessFileNames,StartPath,RestOfSplittedPath,Info)
658
%----------------------------------------------------------------------
659
%HtAccessFilenames is a list the names the accesssfiles can have
660
%Path is the shortest match agains all alias and documentroot
661
%rest of splitted path is a list of the parts of the path
662
%Info is the mod recod from the server
663
%----------------------------------------------------------------------
664
getData2(HtAccessFileNames, StartPath, RestOfSplittedPath, _Info)->
665
case getHtAccessFiles(HtAccessFileNames,StartPath,RestOfSplittedPath) of
667
%No accessfile qiut its a public directory
670
loadAccessFilesData(Files)
674
%----------------------------------------------------------------------
675
%Loads the data in the accessFiles specifiied by
676
% AccessFiles=["/hoem/public/html/accefile",
677
% "/home/public/html/priv/accessfile"]
678
%----------------------------------------------------------------------
679
loadAccessFilesData(AccessFiles)->
680
loadAccessFilesData(AccessFiles,ets:new(accessData,[])).
683
%----------------------------------------------------------------------
684
%Returns the found data
685
%----------------------------------------------------------------------
686
contextToValues(AccessData)->
687
case ets:lookup(AccessData,context) of
689
ets:delete(AccessData,context),
690
insertContext(AccessData,Values),
691
{accessData,AccessData};
693
{error,errorInAccessFile}
697
insertContext(_AccessData, [])->
700
insertContext(AccessData,[{allow,From}|Values])->
701
insertDenyAllowContext(AccessData,{allow,From}),
702
insertContext(AccessData,Values);
704
insertContext(AccessData,[{deny,From}|Values])->
705
insertDenyAllowContext(AccessData,{deny,From}),
706
insertContext(AccessData,Values);
708
insertContext(AccessData,[{require,{GrpOrUsr,Members}}|Values])->
709
case ets:lookup(AccessData,require) of
710
[]when GrpOrUsr==users->
711
ets:insert(AccessData,{require,{{users,Members},{groups,[]}}});
713
[{require,{{users,Users},{groups,Groups}}}]when GrpOrUsr==users ->
714
ets:insert(AccessData,{require,{{users,Users++Members},
716
[]when GrpOrUsr==groups->
717
ets:insert(AccessData,{require,{{users,[]},{groups,Members}}});
719
[{require,{{users,Users},{groups,Groups}}}]when GrpOrUsr==groups ->
720
ets:insert(AccessData,{require,{{users,Users},
721
{groups,Groups++Members}}})
723
insertContext(AccessData,Values);
727
%%limit and order directive need no transforming they areis just to insert
728
insertContext(AccessData,[Elem|Values])->
729
ets:insert(AccessData,Elem),
730
insertContext(AccessData,Values).
733
insertDenyAllowContext(AccessData,{AllowDeny,From})->
736
ets:insert(AccessData,{AllowDeny,all});
738
case ets:lookup(AccessData,AllowDeny) of
740
ets:insert(AccessData,{AllowDeny,From});
743
[{AllowDeny,Networks}]->
744
ets:insert(AccessData,{allow,Networks++From})
748
loadAccessFilesData([],AccessData)->
749
%preform context to limits
750
contextToValues(AccessData),
751
{accessData,AccessData};
753
%----------------------------------------------------------------------
754
%Takes each file in the list and load the data to the ets table
756
%----------------------------------------------------------------------
757
loadAccessFilesData([FileName|FileNames],AccessData)->
758
case loadAccessFileData({file,FileName},AccessData) of
760
loadAccessFilesData(FileNames,AccessData);
762
{accessData,AccessData};
764
ets:delete(AccessData),
765
{error,errorInAccessFile}
768
%----------------------------------------------------------------------
769
%opens the filehandle to the specified file
770
%----------------------------------------------------------------------
771
loadAccessFileData({file,FileName},AccessData)->
772
case file:open(FileName,[read]) of
773
{ok,AccessFileHandle}->
774
loadAccessFileData({stream,AccessFileHandle},AccessData,[]);
779
%----------------------------------------------------------------------
780
%%look att each line in the file and add them to the database
781
%%When end of file is reached control i overrride is allowed
783
%----------------------------------------------------------------------
784
loadAccessFileData({stream,File},AccessData,FileData)->
785
case io:get_line(File,[]) of
787
insertData(AccessData,FileData),
788
case ets:match_object(AccessData,{'_',error}) of
790
%Case we got no error control that we can override a
791
%at least some of the values
792
case ets:match_object(AccessData,
793
{allow_over_ride,none}) of
803
loadAccessFileData({stream,File},AccessData,
804
insertLine(string:strip(Line,left),FileData))
807
%----------------------------------------------------------------------
808
%AccessData is a ets table where the previous found data is inserted
809
%FileData is a list of the directives in the last parsed file
810
%before insertion a control is done that the directive is allowed to
812
%----------------------------------------------------------------------
813
insertData(AccessData,{{context,Values},FileData})->
814
insertData(AccessData,[{context,Values}|FileData]);
816
insertData(AccessData,FileData)->
817
case ets:lookup(AccessData,allow_over_ride) of
818
[{allow_over_ride,all}]->
819
lists:foreach(fun(Elem)->
820
ets:insert(AccessData,Elem)
823
lists:foreach(fun(Elem)->
824
ets:insert(AccessData,Elem)
826
[{allow_over_ride,Directives}]when list(Directives)->
827
lists:foreach(fun({Key,Value})->
828
case lists:member(Key,Directives) of
832
ets:insert(AccessData,{Key,Value})
835
[{allow_over_ride,_}]->
836
%Will never appear if the user
837
%aint doing very strang econfig files
840
%----------------------------------------------------------------------
841
%Take a line in the accessfile and transform it into a tuple that
842
%later can be inserted in to the ets:table
843
%----------------------------------------------------------------------
844
%%%Here is the alternatives that resides inside the limit context
846
insertLine("order"++ Order, {{context, Values}, FileData})->
847
{{context,[{order,getOrder(Order)}|Values]},FileData};
848
%%Let the user place a tab in the beginning
849
insertLine([$\t,$o,$r,$d,$e,$r|Order],{{context,Values},FileData})->
850
{{context,[{order,getOrder(Order)}|Values]},FileData};
852
insertLine("allow" ++ Allow, {{context, Values}, FileData})->
853
{{context,[{allow,getAllowDenyData(Allow)}|Values]},FileData};
854
insertLine([$\t,$a,$l,$l,$o,$w|Allow],{{context,Values},FileData})->
855
{{context,[{allow,getAllowDenyData(Allow)}|Values]},FileData};
857
insertLine("deny" ++ Deny, {{context,Values}, FileData})->
858
{{context,[{deny,getAllowDenyData(Deny)}|Values]},FileData};
859
insertLine([$\t, $d,$e,$n,$y|Deny],{{context,Values},FileData})->
860
{{context,[{deny,getAllowDenyData(Deny)}|Values]},FileData};
862
insertLine("require" ++ Require, {{context, Values}, FileData})->
863
{{context,[{require,getRequireData(Require)}|Values]},FileData};
864
insertLine([$\t,$r,$e,$q,$u,$i,$r,$e|Require],{{context,Values},FileData})->
865
{{context,[{require,getRequireData(Require)}|Values]},FileData};
867
insertLine("</Limit" ++ _EndLimit, {Context,FileData})->
868
[Context | FileData];
869
insertLine("<Limit" ++ Limit, FileData)->
870
{{context,[{limit,getLimits(Limit)}]}, FileData};
872
insertLine([$A,$u,$t,$h,$U,$s,$e,$r,$F,$i,$l,$e,$\ |AuthUserFile],FileData)->
873
[{user_file,string:strip(AuthUserFile,right,$\n)}|FileData];
875
insertLine([$A,$u,$t,$h,$G,$r,$o,$u,$p,$F,$i,$l,$e,$\ |AuthGroupFile],
877
[{group_file,string:strip(AuthGroupFile,right,$\n)}|FileData];
879
insertLine("AllowOverRide" ++ AllowOverRide, FileData)->
880
[{allow_over_ride,getAllowOverRideData(AllowOverRide)}
883
insertLine([$A,$u,$t,$h,$N,$a,$m,$e,$\ |AuthName],FileData)->
884
[{auth_name,string:strip(AuthName,right,$\n)}|FileData];
886
insertLine("AuthType" ++ AuthType,FileData)->
887
[{auth_type,getAuthorizationType(AuthType)}|FileData];
889
insertLine(_BadDirectiveOrComment,FileData)->
892
%----------------------------------------------------------------------
893
%transform the Data specified about override to a form that is ieasier
895
%Override data="all"|"md5"|"Directive1 .... DirectioveN"
896
%----------------------------------------------------------------------
898
getAllowOverRideData(OverRideData)->
899
case string:tokens(OverRideData," \r\n") of
905
getOverRideDirectives(Directives)
908
getOverRideDirectives(Directives)->
909
lists:map(fun(Directive)->
910
transformDirective(Directive)
912
transformDirective("AuthUserFile" ++ _)->
914
transformDirective("AuthGroupFile" ++ _) ->
916
transformDirective("AuthName" ++ _)->
918
transformDirective("AuthType" ++ _)->
920
transformDirective(_UnAllowedOverRideDirective) ->
922
%----------------------------------------------------------------------
923
%Replace the string that specify which method to use for authentication
924
%and replace it with the atom for easier mathing
925
%----------------------------------------------------------------------
926
getAuthorizationType(AuthType)->
927
[Arg | _Crap] = string:tokens(AuthType,"\n\r\ "),
936
%----------------------------------------------------------------------
937
%Returns a list of the specified methods to limit or the atom all
938
%----------------------------------------------------------------------
940
case regexp:split(Limits,">")of
941
{ok,[_NoEndOnLimit]}->
943
{ok, [Methods | _Crap]}->
944
case regexp:split(Methods," ")of
947
{ok,SplittedMethods}->
957
%----------------------------------------------------------------------
958
% Transform the order to prefrom deny allow control to a tuple of atoms
959
%----------------------------------------------------------------------
961
[First | _Rest]=lists:map(fun(Part)->
963
end,string:tokens(Order," \n\r")),
973
%----------------------------------------------------------------------
974
% The string AllowDeny is "from all" or "from Subnet1 Subnet2...SubnetN"
975
%----------------------------------------------------------------------
976
getAllowDenyData(AllowDeny)->
977
case string:tokens(AllowDeny," \n\r") of
978
[_From|AllowDenyData] when length(AllowDenyData)>=1->
979
case lists:nth(1,AllowDenyData) of
988
%----------------------------------------------------------------------
989
% Fix the string that describes who is allowed to se the page
990
%----------------------------------------------------------------------
991
getRequireData(Require)->
992
[UserOrGroup|UserData]=string:tokens(Require," \n\r"),
1003
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1005
%% Methods that collects the searchways to the accessfiles %%
1007
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1009
%----------------------------------------------------------------------
1010
% Get the whole path to the different accessfiles
1011
%----------------------------------------------------------------------
1012
getHtAccessFiles(HtAccessFileNames,Path,RestOfSplittedPath)->
1013
getHtAccessFiles(HtAccessFileNames,Path,RestOfSplittedPath,[]).
1015
getHtAccessFiles(HtAccessFileNames,Path,[[]],HtAccessFiles)->
1016
HtAccessFiles ++ accessFilesOfPath(HtAccessFileNames,Path++"/");
1018
getHtAccessFiles(_HtAccessFileNames, _Path, [], HtAccessFiles)->
1020
getHtAccessFiles(HtAccessFileNames,Path,[NextDir|RestOfSplittedPath],
1022
getHtAccessFiles(HtAccessFileNames,Path++"/"++NextDir,RestOfSplittedPath,
1024
accessFilesOfPath(HtAccessFileNames,Path++"/")).
1027
%----------------------------------------------------------------------
1028
%Control if therer are any accessfies in the path
1029
%----------------------------------------------------------------------
1030
accessFilesOfPath(HtAccessFileNames,Path)->
1031
lists:foldl(fun(HtAccessFileName,Files)->
1032
case file:read_file_info(Path++HtAccessFileName) of
1034
[Path++HtAccessFileName|Files];
1038
end,[],HtAccessFileNames).
1041
%----------------------------------------------------------------------
1042
%Sake the splitted path and joins it up to the documentroot or the alias
1044
%----------------------------------------------------------------------
1046
getRootPath(SplittedPath, Info)->
1047
DocRoot=httpd_util:lookup(Info#mod.config_db,document_root,"/"),
1049
[DocRoot|lists:map(fun({_Alias,RealPath})->
1052
httpd_util:multi_lookup(Info#mod.config_db,alias))],
1053
getRootPath(PresumtiveRootPath,SplittedPath,Info).
1056
getRootPath(PresumtiveRootPath,[[],Splittedpath],Info)->
1057
getRootPath(PresumtiveRootPath,["/",Splittedpath],Info);
1060
getRootPath(PresumtiveRootPath,[Part,NextPart|SplittedPath],Info)->
1061
case lists:member(Part,PresumtiveRootPath)of
1063
{ok,Part,[NextPart|SplittedPath]};
1065
getRootPath(PresumtiveRootPath,
1066
[Part++"/"++NextPart|SplittedPath],Info)
1069
getRootPath(PresumtiveRootPath, [Part], _Info)->
1070
case lists:member(Part,PresumtiveRootPath)of