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

« back to all changes in this revision

Viewing changes to src/couch_inets/mod_htaccess.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
%%%% 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
 
9
%% under the License.
 
10
%% 
 
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.''
 
14
%% 
 
15
%%     $Id$
 
16
%%
 
17
 
 
18
-module(mod_htaccess).
 
19
 
 
20
-export([do/1, load/2]).
 
21
 
 
22
-include("httpd.hrl").
 
23
 
 
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}}).
 
27
 
 
28
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
29
%% Public methods that interface the eswapi                         %%
 
30
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  
 
31
 
 
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," ")}}.
 
39
 
 
40
 
 
41
%----------------------------------------------------------------------
 
42
% Public method that the webbserver calls to control the page 
 
43
%----------------------------------------------------------------------
 
44
do(Info)->
 
45
    case httpd_util:key1search(Info#mod.data,status) of
 
46
        {_Status_code, _PhraseArgs, _Reason}->
 
47
            {proceed,Info#mod.data};
 
48
        undefined ->
 
49
            control_path(Info)
 
50
    end.
 
51
 
 
52
 
 
53
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
54
%%                                                                  %%
 
55
%% The functions that start the control if there is a accessfile    %%
 
56
%% and if so controls if the dir is allowed or not                  %%
 
57
%%                                                                  %%
 
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
%----------------------------------------------------------------------
 
66
control_path(Info) ->
 
67
    Path = mod_alias:path(Info#mod.data,
 
68
                          Info#mod.config_db,
 
69
                          Info#mod.request_uri),
 
70
    case isErlScriptOrNotAccessibleFile(Path,Info) of
 
71
        true->
 
72
            {proceed,Info#mod.data};
 
73
        false->
 
74
            case getHtAccessData(Path,Info)of
 
75
                {ok,public}->
 
76
                    %%There was no restrictions on the page continue
 
77
                    {proceed,Info#mod.data};
 
78
                {error, _Reason} ->
 
79
                    %%Something got wrong continue or quit??????????????????/
 
80
                   {proceed,Info#mod.data};
 
81
                {accessData,AccessData}->
 
82
                    controlAllowedMethod(Info,AccessData)
 
83
            end
 
84
    end.
 
85
 
 
86
 
 
87
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
88
%%                                                                  %%
 
89
%% These methods controls that the method the client used in the    %%
 
90
%% request is one of the limited                                    %%
 
91
%%                                                                  %%
 
92
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
93
%----------------------------------------------------------------------
 
94
%Control that if the accessmethod used is in the list of modes to challenge
 
95
%
 
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
 
101
        allow->
 
102
            %%The request didnt use one of the limited methods
 
103
            ets:delete(AccessData),
 
104
            {proceed,Info#mod.data};
 
105
        challenge->
 
106
            authenticateUser(Info,AccessData)
 
107
    end.
 
108
 
 
109
%----------------------------------------------------------------------
 
110
%Check the specified access method in the .htaccessfile
 
111
%----------------------------------------------------------------------
 
112
allowedRequestMethod(Info,AccessData)->
 
113
    case ets:lookup(AccessData,limit) of
 
114
        [{limit,all}]->
 
115
            challenge;
 
116
        [{limit,Methods}]->
 
117
            isLimitedRequestMethod(Info,Methods)
 
118
    end.
 
119
 
 
120
 
 
121
%----------------------------------------------------------------------
 
122
%Check the specified accessmethods in the .htaccesfile against the users 
 
123
%accessmethod
 
124
%
 
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
 
130
        true->
 
131
            challenge;
 
132
        false ->
 
133
            allow
 
134
    end.
 
135
 
 
136
 
 
137
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
138
%%                                                                  %%
 
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      %%
 
141
%% generated                                                        %%
 
142
%%                                                                  %%
 
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
 
150
        allow->
 
151
            %the network is ok control that it is an allowed user
 
152
            authenticateUser2(Info,AccessData);
 
153
        deny->
 
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]}
 
158
    end.
 
159
 
 
160
 
 
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);
 
171
                _NoAuthName->
 
172
                    ets:delete(AccessData),
 
173
                    {break,[{status,{500,none,
 
174
                                     ?NICE("mod_htaccess:AuthName directive " 
 
175
                                           "not specified")}}]}
 
176
            end;
 
177
        [] ->
 
178
            %%No special user is required the network is ok so let
 
179
            %%the user in
 
180
            ets:delete(AccessData),
 
181
            {proceed,Info#mod.data}
 
182
    end.
 
183
 
 
184
 
 
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
 
192
        allow ->
 
193
            ets:delete(AccessData),
 
194
            {user,Name, _Pwd} = getAuthenticatingDataFromHeader(Info),
 
195
            {proceed, [{remote_user_name,Name}|Info#mod.data]};
 
196
        challenge->  
 
197
            ets:delete(AccessData),
 
198
            ReasonPhrase = httpd_util:reason_phrase(401),
 
199
            Message = httpd_util:message(401,none,Info#mod.config_db),
 
200
            {proceed,
 
201
             [{response,
 
202
               {401,
 
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"]}}|
 
208
              Info#mod.data]};
 
209
        deny->
 
210
            ets:delete(AccessData),
 
211
            {break,[{status,{500,none,
 
212
                             ?NICE("mod_htaccess:Bad path to user " 
 
213
                                   "or group file")}}]}
 
214
    end.
 
215
 
 
216
                                                                      
 
217
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
218
%%                                                                  %%
 
219
%% Methods that validate the netwqork the user comes from           %%
 
220
%% according to the allowed networks                                %%
 
221
%%                                                                  %%
 
222
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
223
%--------------------------------------------------------------------- 
 
224
%Controls the users networkaddress agains the specifed networks to 
 
225
%allow or deny
 
226
%
 
227
%returns either allow or deny
 
228
%----------------------------------------------------------------------
 
229
controlNet(Info,AccessData)->
 
230
    UserNetwork=getUserNetworkAddress(Info),
 
231
    case getAllowDenyOrder(AccessData) of
 
232
        {_deny,[],_allow,[]}->
 
233
            allow;
 
234
        {deny,[],allow,AllowedNetworks}->
 
235
            controlIfAllowed(AllowedNetworks,UserNetwork,allow,deny);
 
236
        {allow,AllowedNetworks,deny,[]}->
 
237
            controlIfAllowed(AllowedNetworks,UserNetwork,allow,deny);
 
238
        
 
239
        {deny,DeniedNetworks,allow,[]}->
 
240
            controlIfAllowed(DeniedNetworks,UserNetwork,allow,deny);
 
241
        {allow,[],deny,DeniedNetworks}->
 
242
            controlIfAllowed(DeniedNetworks,UserNetwork,allow,deny);
 
243
        
 
244
        {deny,DeniedNetworks,allow,AllowedNetworks}->               
 
245
            controlDenyAllow(DeniedNetworks,AllowedNetworks,UserNetwork); 
 
246
        {allow,AllowedNetworks,deny,DeniedNetworks}->            
 
247
            controlAllowDeny(AllowedNetworks,DeniedNetworks,UserNetwork)
 
248
    end.
 
249
 
 
250
 
 
251
%----------------------------------------------------------------------
 
252
%Returns the users IP-Number
 
253
%----------------------------------------------------------------------
 
254
getUserNetworkAddress(Info)->
 
255
    {_Socket,Address}=(Info#mod.init_data)#init_data.peername,
 
256
    Address.
 
257
 
 
258
 
 
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
 
264
        [{allow,all}]->
 
265
           IfAllowed;
 
266
        [{deny,all}]->
 
267
            IfDenied;
 
268
        [{deny,Networks}]->
 
269
            memberNetwork(Networks,UserNetwork,IfDenied,IfAllowed);
 
270
        [{allow,Networks}]->
 
271
            memberNetwork(Networks,UserNetwork,IfAllowed,IfDenied);
 
272
        _Error->
 
273
            IfDenied
 
274
    end.
 
275
 
 
276
 
 
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
 
283
        [{allow, all}]->
 
284
            allow;
 
285
        [{allow, Networks}]->
 
286
          case memberNetwork(Networks, UserNetwork) of
 
287
              true->
 
288
                  allow;
 
289
              false->
 
290
                  deny
 
291
          end
 
292
    end.
 
293
 
 
294
 
 
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
 
301
        allow->
 
302
            controlIfAllowed(DeniedNetworks,UserNetwork,deny,allow);
 
303
        deny ->
 
304
            deny
 
305
    end.
 
306
            
 
307
%----------------------------------------------------------------------
 
308
%Controls if the users Ipnumber is in the list of either denied or
 
309
%allowed networks
 
310
%---------------------------------------------------------------------- 
 
311
memberNetwork(Networks,UserNetwork,IfTrue,IfFalse)->
 
312
    case memberNetwork(Networks,UserNetwork) of
 
313
        true->
 
314
            IfTrue;
 
315
        false->
 
316
            IfFalse
 
317
    end.
 
318
 
 
319
 
 
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
 
327
                                  {match,1,_}->
 
328
                                      true;
 
329
                                  _NotSubNet ->
 
330
                                      false
 
331
                              end
 
332
                      end,Networks) of
 
333
        []->
 
334
            false;
 
335
        _MemberNetWork ->
 
336
            true
 
337
    end.
 
338
 
 
339
 
 
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
%----------------------------------------------------------------------
 
344
formatRegexp(Net)->         
 
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
 
350
        0->
 
351
            NetRegexp++"[.].*";
 
352
        _->
 
353
            NetRegexp++".*"
 
354
    end.
 
355
 
 
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)};
 
366
        _DefaultOrder->
 
367
            {allow,ets:lookup(AccessData,allow),
 
368
             deny,ets:lookup(AccessData,deny)}
 
369
    end.
 
370
                                                                      
 
371
 
 
372
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
373
%%                                                                  %%
 
374
%% The methods that validates the user                              %%
 
375
%%                                                                  %%
 
376
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
377
 
 
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});
 
387
        {error,nouser}->
 
388
            challenge; 
 
389
        {error, _BadData}->
 
390
            challenge 
 
391
    end.
 
392
 
 
393
 
 
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
 
400
        undefined->
 
401
            {error,nouser};
 
402
        [$B,$a,$s,$i,$c,$\ |EncodedString] = Credentials ->
 
403
            case (catch http_base_64:decode(EncodedString)) of
 
404
                {'EXIT',{function_clause, _}} ->
 
405
                    {error, Credentials};
 
406
                UnCodedString ->
 
407
                    case httpd_util:split(UnCodedString,":",2) of
 
408
                        {ok,[User,PassWord]}->
 
409
                            {user,User,PassWord};
 
410
                        {error,Error}->
 
411
                            {error,Error}
 
412
                    end
 
413
            end;
 
414
        BadCredentials ->
 
415
            {error,BadCredentials}
 
416
    end.
 
417
 
 
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
 
424
                                    true->
 
425
                                        AllowedMembers++Members;
 
426
                                    false ->
 
427
                                        AllowedMembers
 
428
                                end
 
429
               end,[],Groups),
 
430
    {ok,Allowed}.
 
431
    
 
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);
 
436
 
 
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
 
441
        {_,allow}->
 
442
            allow;
 
443
        {allow,_}->
 
444
            allow;
 
445
        {challenge,_}->
 
446
            challenge;
 
447
        {_,challenge}->
 
448
            challenge;
 
449
        {_deny,_deny}->
 
450
            deny
 
451
    end;
 
452
    
 
453
 
 
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});
 
463
        {error, _BadData}->
 
464
            deny
 
465
    end;
 
466
 
 
467
 
 
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
 
473
       true->
 
474
            %Get the usernames and passwords from the file
 
475
            case getUsers(AccessData,user_file) of
 
476
                {error, _BadData}->
 
477
                    deny;
 
478
                {user_data,Users}-> 
 
479
                    %Users is a list of the users in
 
480
                    %the userfile [{user,User,Passwd}]
 
481
                    checkPassWord(Users,{user,User,PassWord})
 
482
            end;
 
483
        false ->
 
484
            challenge
 
485
    end.
 
486
 
 
487
 
 
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
 
494
        true->
 
495
            allow;
 
496
        false->
 
497
            challenge
 
498
    end.
 
499
 
 
500
 
 
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);
 
510
        {error,Reason} ->
 
511
            {error,{Reason,FileName}}
 
512
    end;
 
513
 
 
514
 
 
515
%----------------------------------------------------------------------
 
516
%The method that starts the lokkong for user files
 
517
%----------------------------------------------------------------------
 
518
 
 
519
getUsers(AccessData,UserOrGroup)->
 
520
    case ets:lookup(AccessData,UserOrGroup) of
 
521
        [{UserOrGroup,File}]->
 
522
            getUsers({file,File},UserOrGroup);
 
523
        _ ->
 
524
            {error,noUsers}
 
525
    end.
 
526
    
 
527
 
 
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};
 
538
        Line ->
 
539
            getUsers({stream,File},
 
540
                     formatUser(Line,FileData,UserOrGroup),UserOrGroup)
 
541
    end.
 
542
 
 
543
                                                                      
 
544
%----------------------------------------------------------------------
 
545
%If the line is a comment remove it
 
546
%----------------------------------------------------------------------
 
547
formatUser([$#|_UserDataComment],FileData,_UserOrgroup)->
 
548
    FileData;
 
549
 
 
550
 
 
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
 
560
                [Name,PassWord]->
 
561
                    [{user,Name,PassWord}|FileData];
 
562
                _Error->
 
563
                    FileData
 
564
            end;
 
565
        GroupData when UserOrGroup==group_file ->
 
566
            parseGroupData(GroupData,FileData);
 
567
        _Error ->
 
568
            FileData
 
569
    end.
 
570
 
 
571
 
 
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].
 
578
 
 
579
 
 
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,$:).
 
586
 
 
587
 
 
588
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
589
%%                                                                  %%
 
590
%%  Functions that parses the accessfiles                           %%
 
591
%%                                                                  %%
 
592
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
593
%----------------------------------------------------------------------
 
594
%Control that the asset is a real file and not a request for an virtual
 
595
%asset
 
596
%----------------------------------------------------------------------
 
597
isErlScriptOrNotAccessibleFile(Path, _Info)->
 
598
    case file:read_file_info(Path) of
 
599
        {ok,_fileInfo}->
 
600
            false;
 
601
        {error,_Reason} ->
 
602
            true
 
603
    end.
 
604
 
 
605
 
 
606
%----------------------------------------------------------------------
 
607
%Path=PathToTheRequestedFile=String
 
608
%Innfo=record#mod
 
609
%----------------------------------------------------------------------
 
610
getHtAccessData(Path,Info)->
 
611
    HtAccessFileNames=getHtAccessFileNames(Info),
 
612
    case getData(Path,Info,HtAccessFileNames) of
 
613
        {ok,public}->
 
614
            {ok,public};        
 
615
        {accessData,AccessData}->
 
616
            {accessData,AccessData};
 
617
        {error,Reason} ->
 
618
            {error,Reason}
 
619
    end.
 
620
 
 
621
 
 
622
%----------------------------------------------------------------------
 
623
%returns the names of the accessfiles
 
624
%----------------------------------------------------------------------
 
625
getHtAccessFileNames(Info)->
 
626
    case httpd_util:lookup(Info#mod.config_db,access_files) of
 
627
        undefined->
 
628
            [".htaccess"];
 
629
        Files->
 
630
            Files
 
631
    end.
 
632
%----------------------------------------------------------------------
 
633
%HtAccessFileNames=["accessfileName1",..."AccessFileName2"]
 
634
%----------------------------------------------------------------------
 
635
getData(Path,Info,HtAccessFileNames)->      
 
636
    case regexp:split(Path,"/") of
 
637
        {error,Error}->
 
638
            {error,Error};
 
639
        {ok,SplittedPath}->
 
640
            getData2(HtAccessFileNames,SplittedPath,Info)
 
641
        end.
 
642
 
 
643
 
 
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
 
651
        {error,Path}->
 
652
            {error,Path};
 
653
        {ok,StartPath,RestOfSplittedPath} ->
 
654
            getData2(HtAccessFileNames,StartPath,RestOfSplittedPath,Info)
 
655
    end.
 
656
 
 
657
 
 
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
 
666
        []->
 
667
            %No accessfile qiut its a public directory
 
668
            {ok,public};
 
669
        Files ->
 
670
            loadAccessFilesData(Files)
 
671
    end.
 
672
 
 
673
 
 
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,[])).
 
681
 
 
682
 
 
683
%----------------------------------------------------------------------
 
684
%Returns the found data
 
685
%----------------------------------------------------------------------
 
686
contextToValues(AccessData)->
 
687
    case ets:lookup(AccessData,context) of
 
688
        [{context,Values}]->
 
689
            ets:delete(AccessData,context),
 
690
            insertContext(AccessData,Values),
 
691
            {accessData,AccessData};
 
692
        _Error->
 
693
            {error,errorInAccessFile}
 
694
    end.
 
695
 
 
696
 
 
697
insertContext(_AccessData, [])->
 
698
    ok;
 
699
 
 
700
insertContext(AccessData,[{allow,From}|Values])->
 
701
    insertDenyAllowContext(AccessData,{allow,From}),
 
702
    insertContext(AccessData,Values);
 
703
   
 
704
insertContext(AccessData,[{deny,From}|Values])->
 
705
    insertDenyAllowContext(AccessData,{deny,From}),
 
706
    insertContext(AccessData,Values);
 
707
 
 
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,[]}}});
 
712
 
 
713
        [{require,{{users,Users},{groups,Groups}}}]when GrpOrUsr==users ->
 
714
            ets:insert(AccessData,{require,{{users,Users++Members},
 
715
                                           {groups,Groups}}});
 
716
        []when GrpOrUsr==groups->
 
717
            ets:insert(AccessData,{require,{{users,[]},{groups,Members}}});
 
718
 
 
719
        [{require,{{users,Users},{groups,Groups}}}]when GrpOrUsr==groups ->
 
720
            ets:insert(AccessData,{require,{{users,Users},
 
721
                                           {groups,Groups++Members}}})    
 
722
    end,
 
723
    insertContext(AccessData,Values);
 
724
 
 
725
        
 
726
 
 
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).
 
731
    
 
732
 
 
733
insertDenyAllowContext(AccessData,{AllowDeny,From})->
 
734
    case From of
 
735
        all ->
 
736
            ets:insert(AccessData,{AllowDeny,all});
 
737
        _AllowedSubnets ->
 
738
            case ets:lookup(AccessData,AllowDeny) of
 
739
                []->
 
740
                    ets:insert(AccessData,{AllowDeny,From});
 
741
                [{AllowDeny,all}]->
 
742
                    ok;
 
743
                [{AllowDeny,Networks}]->
 
744
                    ets:insert(AccessData,{allow,Networks++From})
 
745
            end
 
746
    end.
 
747
 
 
748
loadAccessFilesData([],AccessData)->
 
749
    %preform context to limits
 
750
    contextToValues(AccessData),
 
751
    {accessData,AccessData};
 
752
 
 
753
%----------------------------------------------------------------------
 
754
%Takes each file in the list and load the data to the ets table 
 
755
%AccessData
 
756
%----------------------------------------------------------------------
 
757
loadAccessFilesData([FileName|FileNames],AccessData)->
 
758
    case loadAccessFileData({file,FileName},AccessData) of
 
759
        overRide->
 
760
            loadAccessFilesData(FileNames,AccessData);
 
761
        noOverRide ->
 
762
            {accessData,AccessData};
 
763
        error->
 
764
            ets:delete(AccessData),
 
765
            {error,errorInAccessFile}
 
766
    end.
 
767
 
 
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,[]);
 
775
        {error, _Reason} ->
 
776
            overRide
 
777
    end.
 
778
 
 
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
 
782
%% if so return 
 
783
%----------------------------------------------------------------------
 
784
loadAccessFileData({stream,File},AccessData,FileData)->
 
785
    case io:get_line(File,[]) of
 
786
        eof->
 
787
            insertData(AccessData,FileData),
 
788
            case ets:match_object(AccessData,{'_',error}) of
 
789
                []->
 
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
 
794
                        []->
 
795
                            overRide;
 
796
                        _NoOverride->
 
797
                            noOverRide
 
798
                    end;
 
799
                _ ->
 
800
                    error
 
801
            end;
 
802
        Line ->
 
803
            loadAccessFileData({stream,File},AccessData,
 
804
                               insertLine(string:strip(Line,left),FileData))
 
805
    end.
 
806
 
 
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
 
811
%override
 
812
%----------------------------------------------------------------------
 
813
insertData(AccessData,{{context,Values},FileData})->
 
814
    insertData(AccessData,[{context,Values}|FileData]);
 
815
 
 
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)
 
821
                          end,FileData);
 
822
        []->
 
823
            lists:foreach(fun(Elem)->
 
824
                                  ets:insert(AccessData,Elem)
 
825
                          end,FileData);
 
826
        [{allow_over_ride,Directives}]when list(Directives)->
 
827
            lists:foreach(fun({Key,Value})->
 
828
                                  case lists:member(Key,Directives) of
 
829
                                      true->
 
830
                                          ok;
 
831
                                      false ->
 
832
                                          ets:insert(AccessData,{Key,Value})
 
833
                                  end
 
834
                          end,FileData);
 
835
        [{allow_over_ride,_}]->
 
836
            %Will never appear if the user 
 
837
            %aint doing very strang econfig files
 
838
            ok
 
839
    end.
 
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
 
845
 
 
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};
 
851
 
 
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};
 
856
 
 
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};
 
861
 
 
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};
 
866
 
 
867
insertLine("</Limit" ++ _EndLimit, {Context,FileData})->
 
868
    [Context | FileData];
 
869
insertLine("<Limit" ++ Limit, FileData)->
 
870
    {{context,[{limit,getLimits(Limit)}]}, FileData};
 
871
 
 
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];
 
874
 
 
875
insertLine([$A,$u,$t,$h,$G,$r,$o,$u,$p,$F,$i,$l,$e,$\ |AuthGroupFile],
 
876
           FileData)->
 
877
    [{group_file,string:strip(AuthGroupFile,right,$\n)}|FileData];
 
878
 
 
879
insertLine("AllowOverRide" ++ AllowOverRide, FileData)->
 
880
    [{allow_over_ride,getAllowOverRideData(AllowOverRide)}
 
881
     | FileData];
 
882
 
 
883
insertLine([$A,$u,$t,$h,$N,$a,$m,$e,$\ |AuthName],FileData)->
 
884
    [{auth_name,string:strip(AuthName,right,$\n)}|FileData];
 
885
 
 
886
insertLine("AuthType" ++ AuthType,FileData)->
 
887
    [{auth_type,getAuthorizationType(AuthType)}|FileData];
 
888
 
 
889
insertLine(_BadDirectiveOrComment,FileData)->
 
890
    FileData.
 
891
 
 
892
%----------------------------------------------------------------------
 
893
%transform the Data specified about override to a form that is ieasier 
 
894
%handled later
 
895
%Override data="all"|"md5"|"Directive1 .... DirectioveN"
 
896
%----------------------------------------------------------------------
 
897
 
 
898
getAllowOverRideData(OverRideData)->
 
899
   case string:tokens(OverRideData," \r\n") of
 
900
       ["all" ++ _] ->
 
901
           all;
 
902
       ["none" ++ _]->
 
903
           none;
 
904
       Directives ->
 
905
           getOverRideDirectives(Directives)
 
906
   end.
 
907
 
 
908
getOverRideDirectives(Directives)->
 
909
    lists:map(fun(Directive)->
 
910
                      transformDirective(Directive)
 
911
              end,Directives).
 
912
transformDirective("AuthUserFile" ++  _)->
 
913
    user_file;
 
914
transformDirective("AuthGroupFile" ++ _) ->
 
915
    group_file;
 
916
transformDirective("AuthName" ++ _)->
 
917
    auth_name;
 
918
transformDirective("AuthType" ++ _)-> 
 
919
    auth_type;
 
920
transformDirective(_UnAllowedOverRideDirective) ->
 
921
    unallowed.
 
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\ "),
 
928
    case Arg of
 
929
        "Basic"->
 
930
            basic;
 
931
        "MD5" ->
 
932
            md5;
 
933
        _What ->
 
934
            error
 
935
    end.
 
936
%----------------------------------------------------------------------
 
937
%Returns a list of the specified methods to limit or the atom all
 
938
%----------------------------------------------------------------------
 
939
getLimits(Limits)->
 
940
    case regexp:split(Limits,">")of
 
941
        {ok,[_NoEndOnLimit]}->
 
942
            error;
 
943
        {ok, [Methods | _Crap]}->
 
944
            case regexp:split(Methods," ")of
 
945
                {ok,[]}->
 
946
                    all;
 
947
                {ok,SplittedMethods}->
 
948
                    SplittedMethods;
 
949
                {error, _Error}->
 
950
                    error
 
951
            end;
 
952
        {error,_Error}->
 
953
            error
 
954
    end.
 
955
 
 
956
 
 
957
%----------------------------------------------------------------------
 
958
% Transform the order to prefrom deny allow control to a tuple of atoms
 
959
%----------------------------------------------------------------------
 
960
getOrder(Order)->
 
961
    [First | _Rest]=lists:map(fun(Part)->
 
962
                      list_to_atom(Part)
 
963
              end,string:tokens(Order," \n\r")),
 
964
    case First of
 
965
        deny->
 
966
            {deny,allow};
 
967
        allow->
 
968
            {allow,deny};
 
969
        _Error->
 
970
            error
 
971
    end.
 
972
 
 
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
 
980
                "all" ->
 
981
                    all;
 
982
                _Hosts->
 
983
                    AllowDenyData
 
984
            end;
 
985
        _ ->
 
986
            error
 
987
    end.
 
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"),
 
993
    case UserOrGroup of
 
994
        "user"->
 
995
            {users,UserData};
 
996
        "group" ->
 
997
            {groups,UserData};
 
998
        _Whatever ->
 
999
            error
 
1000
    end.
 
1001
 
 
1002
 
 
1003
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1004
%%                                                                  %%
 
1005
%% Methods that collects the searchways to the accessfiles          %%
 
1006
%%                                                                  %%
 
1007
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1008
 
 
1009
%----------------------------------------------------------------------
 
1010
% Get the whole path to the different accessfiles
 
1011
%---------------------------------------------------------------------- 
 
1012
getHtAccessFiles(HtAccessFileNames,Path,RestOfSplittedPath)->
 
1013
    getHtAccessFiles(HtAccessFileNames,Path,RestOfSplittedPath,[]).
 
1014
 
 
1015
getHtAccessFiles(HtAccessFileNames,Path,[[]],HtAccessFiles)->
 
1016
    HtAccessFiles ++ accessFilesOfPath(HtAccessFileNames,Path++"/");   
 
1017
    
 
1018
getHtAccessFiles(_HtAccessFileNames, _Path, [], HtAccessFiles)->
 
1019
    HtAccessFiles;
 
1020
getHtAccessFiles(HtAccessFileNames,Path,[NextDir|RestOfSplittedPath],
 
1021
                 AccessFiles)->   
 
1022
    getHtAccessFiles(HtAccessFileNames,Path++"/"++NextDir,RestOfSplittedPath,
 
1023
                     AccessFiles ++ 
 
1024
                     accessFilesOfPath(HtAccessFileNames,Path++"/")).   
 
1025
    
 
1026
 
 
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
 
1033
                            {ok, _}->
 
1034
                                [Path++HtAccessFileName|Files];
 
1035
                            {error,_Error} ->
 
1036
                                Files
 
1037
                        end
 
1038
                end,[],HtAccessFileNames).
 
1039
 
 
1040
 
 
1041
%----------------------------------------------------------------------
 
1042
%Sake the splitted path and joins it up to the documentroot or the alias
 
1043
%that match first
 
1044
%----------------------------------------------------------------------
 
1045
 
 
1046
getRootPath(SplittedPath, Info)->
 
1047
    DocRoot=httpd_util:lookup(Info#mod.config_db,document_root,"/"),
 
1048
    PresumtiveRootPath=
 
1049
        [DocRoot|lists:map(fun({_Alias,RealPath})->
 
1050
                                   RealPath
 
1051
                           end,
 
1052
                 httpd_util:multi_lookup(Info#mod.config_db,alias))],
 
1053
    getRootPath(PresumtiveRootPath,SplittedPath,Info).
 
1054
 
 
1055
 
 
1056
getRootPath(PresumtiveRootPath,[[],Splittedpath],Info)->
 
1057
    getRootPath(PresumtiveRootPath,["/",Splittedpath],Info);
 
1058
 
 
1059
 
 
1060
getRootPath(PresumtiveRootPath,[Part,NextPart|SplittedPath],Info)->
 
1061
    case lists:member(Part,PresumtiveRootPath)of
 
1062
        true->
 
1063
            {ok,Part,[NextPart|SplittedPath]};
 
1064
        false ->
 
1065
            getRootPath(PresumtiveRootPath,
 
1066
                        [Part++"/"++NextPart|SplittedPath],Info)
 
1067
    end;
 
1068
 
 
1069
getRootPath(PresumtiveRootPath, [Part], _Info)->
 
1070
    case lists:member(Part,PresumtiveRootPath)of
 
1071
        true->
 
1072
            {ok,Part,[]};
 
1073
        false ->
 
1074
            {error,Part}
 
1075
    end.