| 
 #include ”apr_strings.h” 
#include ”apr_hash.h” 
#include ”apr_tables.h” 
#include ”apr_md5.h”
/* for apr_password_validate */ 
#include ”apr_lib.h”
/* for apr_isspace */ 
#include ”apr_base64.h”
/* for apr_base64_decode et al */ 
#define APR_WANT_STRFUNC
/* for strcasecmp */ 
#include ”apr_want.h” 
 
#include ”ap_config.h” 
#include ”httpd.h” 
#include ”http_config.h” 
#include ”http_core.h” 
#include ”http_log.h” 
#include ”http_protocol.h” 
#include ”http_request.h” 
#include ”ap_provider.h” 
#include  
 
#define ENABLED 1 
#define DISABLED 0 
 
/* data need by our module */ 
typedef struct 
{ 
    short enabled; 
    short debug; 
    char *dir; 
    // the starting path 
 
    char *prefixPath; 
    // the stop url pattern 
 
    char *stopPattern; 
    // the svn access file 
 
    char *accessFile; 
} authSVN_rec; 
 
struct access_rec 
{ 
   // 0: group 
 
   // 1: user 
 
   // 2: all 
 
   short type; 
   // the group or user name 
 
   char *name; 
   // 0: don’t have read access 
 
   // 1: have read access 
 
   short access; 
   // the next access record 
 
   struct access_rec 
*next; 
}; 
module AP_MODULE_DECLARE_DATA authSVN_module; 
 
// src starts with start 
 
static short start_with(const
char *src,
const char 
*start) 
{ 
 int i = 0; 
 if(strlen(src)
< strlen(start)) 
     return 0; 
  
 i = strlen(start)
- 1; 
  
 while(i 
>= 0) 
 { 
  if(src[i]
!= start[i]) 
      return 0; 
  i–; 
 } 
  
 return 1; 
} 
 
// parse the SVN access file 
 
static short parse_access_file(request_rec
*r, 
const char*
file, 
                                     const authSVN_rec
*conf, 
                                     apr_hash_t* ugMap, 
                                     apr_hash_t* accessMap) 
{ 
    ap_configfile_t *f 
= NULL; 
    apr_status_t status; 
    char l[MAX_STRING_LEN], dir[256]; 
    status = ap_pcfg_openfile(&f, r->pool,
file); 
    short flag = 0; 
    
    if (status
!= APR_SUCCESS) 
        return 0; 
    
    while(!(ap_cfg_getline(l, MAX_STRING_LEN,
 f))) 
    { 
        const char
*w = 
NULL; 
        char *last
= NULL; 
        apr_table_t *apt 
= NULL; 
        struct access_rec 
*arec = NULL,
*arecp = 
NULL; 
        
        if ((l[0]
== ’#') || (!l[0])) { 
            continue; 
        } 
        
        if(start_with(l, ”[groups]”)) { 
            flag = 1; 
            continue; 
        } 
        
        if(l[0] == ’[') 
{ 
            flag = 2; 
            w = apr_strtok(l, ”[]:\n”,
&last); 
            
            if(w
&& w[0]
== ’/') { 
                // the root directory 
                snprintf(dir, sizeof(dir), ”%s”, conf->prefixPath); 
                dir[strlen(dir) - 1] = ’\0′; 
            } 
            else if(w && w[0] != ’/') 
            { 
                const 
char *project 
= w; 
                w = apr_strtok(NULL, ”[]:\n”,
&last); 
                if(w) 
                { 
                    snprintf(dir,
sizeof(dir), ”%s%s%s”,
 conf->prefixPath, project, w); 
                    // make sure the dir is not end with / 
 
                    int len 
= strlen(dir); 
                    if(dir[len
- 1] 
== ’/') dir[len - 1] = ’\0′; 
                } 
                else 
                    dir[0] = ’\0′; 
            } 
            else 
            { 
                dir[0] = ’\0′; 
            } 
            
            continue; 
        } 
        
        if(flag == 1) { 
            // this is the groups and users definition 
            w = apr_strtok(l, ”=, \n”, &last); 
            if(w == NULL) 
                // group name not found 
                continue; 
                
            apt = (apr_table_t *)apr_hash_get(ugMap, (const void *)w, APR_HASH_KEY_STRING); 
            if(apt == NULL) { 
                apt = apr_table_make(r->pool, 10); 
                apr_hash_set(ugMap, (const void *)apr_pstrdup(r->pool, w), 
                             APR_HASH_KEY_STRING, (const void *)apt); 
            } 
            
            while((w = apr_strtok(NULL, ”=, \n”, &last)) != NULL) { 
                // this is group name or user name 
                if(w[0] == ‘@’) { 
                    w++; 
                if(w) { 
                    apr_table_setn(apt, apr_pstrdup(r->pool, w), ”0″); 
                }  
                } 
                else 
                { 
                    // this is user name 
                    apr_table_setn(apt, apr_pstrdup(r->pool, w), ”1″); 
                } 
            } 
            
        } 
        
        if(flag == 2) { 
            if(dir[0] == ’\0′) continue; 
            w = apr_strtok(l, ”= \n”, &last); 
            if(w) 
            { 
                arec = (struct access_rec *)apr_pcalloc(r->pool, sizeof(struct access_rec)); 
                arec->access = 0; 
                
                if(w[0] == ‘@’) { 
                    w++; 
                    if(w) { 
                        arec->name = apr_pstrdup(r->pool, w); 
                        arec->type = 0; 
                    }  
                    else continue; 
                } 
                else if(w[0] == ’*') 
                { 
                    arec->name
= apr_pstrdup(r->pool, ”*”); 
                    // this is all 
 
                    arec->type
= 2; 
                } 
                else 
                { 
                    arec->name
= apr_pstrdup(r->pool, w); 
                    // this is user name 
 
                    arec->type
= 1; 
                } 
            } 
            else continue; 
            
            w = apr_strtok(NULL, ”= \n”,
&last); 
            if(!w) 
            { 
                arec->access
= 0; 
            } 
            else 
            { 
                arec->access
= 1; 
            } 
            
            arecp = 
(struct access_rec 
*)apr_hash_get(accessMap,
(const 
void *)dir, APR_HASH_KEY_STRING); 
            if(arecp
== 
NULL) { 
                arec->next
= NULL; 
                apr_hash_set(accessMap,
(const 
void *)apr_pstrdup(r->pool, dir), 
                             APR_HASH_KEY_STRING,
(const 
void *)arec); 
            } 
            else 
            { 
                while(arecp->next
!= 
NULL) arecp 
= arecp->next; 
                arecp->next
= arec; 
            } 
            
        } 
        
    } 
    
    ap_cfg_closefile(f); 
    
    return 1; 
} 
/* init per dir */ 
static void
*create_authSVN_dir_config(apr_pool_t
*p, 
char *d) 
{ 
    authSVN_rec *conf 
= (authSVN_rec 
*)apr_pcalloc(p,
sizeof(*conf)); 
    if(conf
== 
NULL) return
NULL; 
        
    conf->enabled
= DISABLED; 
    conf->debug
= DISABLED; 
    conf->dir
= d; 
    conf->prefixPath
= NULL; 
    conf->stopPattern
= NULL; 
    conf->accessFile
= NULL; 
 
    return conf; 
} 
 
/* hex to int */ 
static char hex2int(char c) 
{ 
 if( c>=’0′
&& c<='9'
) return 
(c - '0'); 
 return (c 
- 'A' + 10); 
} 
 
/* url decode */ 
static void url_decode(char
*url) 
{ 
 char *p 
= url; 
 char *p1 
= NULL; 
  
 while(*p) 
 { 
  if(*p
== 
'+') *p
= ' '; 
   
  /* %AB */ 
  if(*p=='%'
&& 
*(p+1)
&& 
*(p+2)) 
  { 
   *p = hex2int(toupper(*(p+1)))
* 16 + hex2int(toupper(*(p+
 2))); 
   strcpy(p
+ 1, p 
+ 3); 
  } 
  /* \xAB */ 
  if(*p=='\\'
&& 
*(p+1)
&& 
*(p+2)
&& 
*(p+3)) 
       { 
              p1 = p 
+ 1; 
              if(*p1
&& 
*p1=='x') 
              { 
                   *p 
= hex2int(toupper(*(p+2)))
* 16 + hex2int(toupper(*(p+3))); 
                   strcpy(p+1, p+4); 
              } 
        } 
  p++; 
 } 
  
 return; 
} 
 
static void parent_path(char
*url) 
{ 
    char *p
= url + 
strlen(url)
- 1; 
    
    while(p
!= url 
&& *p
!= 
'/') {
*p = 
'\0'; p--;
} 
    if(p 
!= url &&
*p=='/')
*p = 
'\0'; 
    
    return; 
} 
 
// return 
 
// 0: the user don't belong to this group 
 
// 1: the user belong to this group 
 
static short find_user_in_group(const
char* user,
const char 
*group, apr_hash_t* ugMap) 
{ 
    apr_table_t *apt 
= (apr_table_t 
*)apr_hash_get(ugMap, 
                                                 (const
void *)group, 
                                                 APR_HASH_KEY_STRING); 
    if(apt 
== NULL)
return 0; 
    apr_array_header_t *arr; 
    apr_table_entry_t *elts; 
    int i; 
    arr = (apr_array_header_t
*)apr_table_elts(apt); 
    elts = (apr_table_entry_t
*)arr->elts; 
    
    for(i=0; inelts; i++) 
    { 
        if(elts[i].key
== 
NULL || elts[i].val
== 
NULL) continue; 
                    
        if(elts[i].val[0]
== ’1′ 
&& strcmp(elts[i].key,
 user) == 0) 
        { 
            return 1; 
        } 
        
        if(elts[i].val[0]
== ’0′) 
        { 
            if(find_user_in_group(user, elts[i].key,
 ugMap)) 
                return 1; 
        } 
    } 
    
    return 0; 
} 
 
// return 
 
// 0:don’t have access 
 
// 1:have read access 
 
// 2:access not found 
 
static short find_access(const
char* user,
const char* url, 
                         apr_hash_t* ugMap, apr_hash_t* accessMap) 
{ 
    struct access_rec 
*arec= (struct access_rec
*)apr_hash_get(accessMap, 
                             (const
void *)url, APR_HASH_KEY_STRING); 
    
    short access = 2; 
    
    while(arec
!= 
NULL) 
    { 
        if(strcmp(arec->name,
 ”*”) 
== 0) 
        { 
            // specified access to all users and groups on this url 
 
            access = arec->access; 
        } 
        
        if(arec->type
== 1 
&& strcmp(arec->name, user)
== 0) 
        { 
            // specified user access on this url 
 
            access = arec->access; 
        } 
        
        if(arec->type
== 0) 
        { 
            // this is group access 
 
            if(find_user_in_group(user, arec->name,
 ugMap)) 
                access = arec->access; 
        } 
        
        // if this user have access, we return 
 
        if(access
== 1)
return access;
 
        
        arec = arec->next; 
    } 
    
    return access; 
} 
static short estimate_access( request_rec
*r, 
const authSVN_rec* conf, 
                              char* url, apr_hash_t* ugMap, 
                              apr_hash_t* accessMap
) 
{ 
    const char* user
= r->user; 
    // unauthorized 
 
    if(!user
|| 
!user[0])
return 0; 
    
    short access = find_access(user, url, ugMap,
 accessMap); 
    if(access
< 2) 
return access; 
    
    if(url[0]
== 
'/' && url[1]
== 
'\0') return 0; 
    
    parent_path(url); 
    
    return estimate_access(r, conf, url, ugMap,
 accessMap); 
} 
 
// do regexp matching 
 
static short regexp_match(char
*str, 
char *pattern) 
{ 
    regex_t reg; 
   regmatch_t pm[1]; 
   const size_t nmatch
= 1; 
   int res = 0; 
 short r = 0; 
   char ebuf[MAX_STRING_LEN]; 
    
 res = regcomp(®, pattern, REG_EXTENDED); 
    
 if(res 
!= 0) 
   { 
     regfree(®);
 
    return 0; 
   } 
    
   res = regexec(®, str, nmatch, pm,
 0); 
    
   if(res 
== REG_NOMATCH) 
       r = 0; 
   else 
       r = 1; 
    
 regfree(®); 
  
 return r; 
} 
 
/* all pages need to pass from this handler */ 
static int authSVN_handler(request_rec
*r) 
{ 
        authSVN_rec *conf 
= ap_get_module_config(r->per_dir_config, 
                                                 &authSVN_module); 
        if(!conf
|| 
!conf->enabled) 
             return DECLINED; 
        
        if(conf->prefixPath
== 
NULL ||
!start_with(r->uri, conf->prefixPath)) 
            return DECLINED; 
        
        if(conf->stopPattern
!=NULL
&& regexp_match(r->uri, conf->stopPattern)) 
            return DECLINED; 
        
        apr_hash_t* ugMap 
= apr_hash_make(r->pool); 
        apr_hash_t* accessMap 
= apr_hash_make(r->pool); 
        
        if(!parse_access_file(r, conf->accessFile,
 conf, ugMap, accessMap)) 
            return 403; 
        
        if(conf->debug) 
        { 
            // run in debug mode 
 
            // print all users/groups and access information 
 
            apr_hash_index_t* hi; 
            char *key; 
            apr_table_t *val; 
            struct access_rec 
*arec; 
            apr_array_header_t *arr; 
            apr_table_entry_t *elts; 
            int i; 
            
            r->content_type=”text/plain”; 
            ap_rprintf(r, ”Parsed Users
and Groups:\n”); 
            
            hi = apr_hash_first(r->pool, ugMap); 
            while(hi
!= 
NULL) 
            { 
                apr_hash_this(hi,
(void 
*)&key,
NULL, 
(void *)&val); 
                
                ap_rprintf(r, ”%s: ”, key); 
                arr = 
(apr_array_header_t *)apr_table_elts(val); 
                elts = 
(apr_table_entry_t *)arr->elts; 
                for(i=0; inelts; i++) 
                { 
                    if(elts[i].key
== 
NULL || elts[i].val
== 
NULL) continue; 
                    if(elts[i].val[0]
== ’0′) 
                    { 
                        ap_rprintf(r, ”@”); 
                    } 
                    ap_rprintf(r, ”%s ”, elts[i].key); 
                } 
                
                ap_rprintf(r, ”\n”); 
                
                hi = apr_hash_next(hi); 
            } 
            
            ap_rprintf(r, ”Parsed Path Access:\n”); 
            hi = apr_hash_first(r->pool, accessMap); 
            while(hi
!= 
NULL) 
            { 
                apr_hash_this(hi,
(void 
*)&key,
NULL, 
(void *)&arec); 
                ap_rprintf(r, ”%s:\n”, key); 
                while(arec
!= 
NULL) 
                { 
                    if(arec->type
== 0) 
                        ap_rprintf(r, ”group:%s ”, arec->name); 
                    else 
if(arec->type
== 1) 
                        ap_rprintf(r, ”user:%s ”, arec->name); 
                    else 
                        ap_rprintf(r, ”all ”); 
                    
                    ap_rprintf(r, ”access:%d ”, arec->access); 
                    ap_rprintf(r, ”\n”); 
                    arec = arec->next; 
                } 
                
                ap_rprintf(r, ”\n”); 
                hi = apr_hash_next(hi); 
            } 
        } 
        
        char *url
= apr_pstrdup(r->pool, r->uri); 
        // decode the url for some chinese characters 
 
        url_decode(url); 
        
        // analyze the access 
 
        if(estimate_access(r, conf, url, ugMap,
 accessMap)) 
        { 
            if(conf->debug) 
            { 
                ap_rprintf(r, ”%s have access on:%s\n”,
 r->user, r->uri); 
                return OK; 
            } 
            return DECLINED; 
        } 
        else 
        { 
            if(conf->debug) 
            { 
                ap_rprintf(r, ”%s don’t have access on:%s\n”,
 r->user, r->uri); 
                return OK; 
            } 
        } 
        
        return 403; 
} 
 
/* enable this module or not */ 
static const
char *set_authSVN_enable(cmd_parms
*cmd, 
                                      void 
*mconfig, 
                                      int 
arg) 
{ 
    authSVN_rec *conf 
= (authSVN_rec 
*) mconfig; 
    conf->enabled
= arg; 
    return NULL; 
} 
 
/* debug this module or not */ 
static const
char *set_authSVN_debug( cmd_parms
*cmd, 
                                      void 
*mconfig, 
                                      int 
arg) 
{ 
    authSVN_rec *conf 
= (authSVN_rec 
*) mconfig; 
    conf->debug
= arg; 
    return NULL; 
} 
 
/* setting prefix path */ 
static const
char *set_prefix_path(cmd_parms
*cmd, 
                                   void 
*mconfig, 
                                   const 
char *name) 
{ 
    authSVN_rec *conf 
= (authSVN_rec 
*) mconfig; 
    
    if(strlen(name)
<= 0) 
        return "AuthSVNPrefixPath can not be null."; 
    
    if(name[0]
!= 
'/' || name[strlen(name)
- 1] 
!= '/') 
        return "AuthSVNPrefixPath must start and end with '/'."; 
    
    conf->prefixPath
= apr_pstrdup(cmd->pool, name); 
    
    return NULL; 
} 
 
/* setting stop url pattern */ 
static const
char *set_stop_pattern(cmd_parms
*cmd, 
                                    void 
*mconfig, 
                                   const 
char *name) 
{ 
    authSVN_rec *conf 
= (authSVN_rec 
*) mconfig; 
    
    if(strlen(name)
<= 0) 
        return "AuthSVNStopPattern can not be null."; 
    
    conf->stopPattern
= apr_pstrdup(cmd->pool, name); 
    
    return NULL; 
} 
 
/* setting SVN access file */ 
static const
char *set_authSVN_accessFile(cmd_parms
*cmd, 
                                          void
*mconfig, 
                                          const
char *name) 
{ 
    authSVN_rec *conf 
= (authSVN_rec 
*) mconfig; 
    ap_configfile_t *f 
= NULL; 
    apr_status_t status; 
    
    if(strlen(name)
<= 0) 
        return "SVNAccessFile can not be null."; 
    
    status = ap_pcfg_openfile(&f, cmd->pool,
 name); 
 
    if (status
!= APR_SUCCESS)
{ 
        return ”Can 
not open given SVN access 
file.”; 
    } 
    ap_cfg_closefile(f); 
    
    conf->accessFile
= apr_pstrdup(cmd->pool, name); 
    
    return NULL; 
} 
static const command_rec auth_cmds[]
= 
{ 
    AP_INIT_FLAG(”EnableAuthSVN”, set_authSVN_enable,
NULL, OR_FILEINFO, 
     ”enable authSVN or 
not.”), 
    AP_INIT_FLAG(”DebugAuthSVN”, set_authSVN_debug,
NULL, OR_FILEINFO, 
     ”debug authSVN or 
not.”), 
    AP_INIT_TAKE1(”AuthSVNPrefixPath”, set_prefix_path,
NULL, OR_FILEINFO, 
     ”set prefix path.”), 
    AP_INIT_TAKE1(”AuthSVNStopPattern”, set_stop_pattern,
NULL, OR_FILEINFO, 
     ”the url pattern we do 
not do the access checking.”), 
    AP_INIT_TAKE1(”SVNAccessFile”, set_authSVN_accessFile,
NULL, OR_FILEINFO, 
     ”set SVN access file.”), 
    { NULL 
} 
}; 
 
static void register_hooks(apr_pool_t
*p) 
{ 
    ap_hook_handler(authSVN_handler,
NULL, 
NULL, APR_HOOK_FIRST); 
} 
 
module AP_MODULE_DECLARE_DATA authSVN_module = 
{ 
    STANDARD20_MODULE_STUFF, 
    create_authSVN_dir_config, 
/* dir config creater */ 
    NULL, 
/* dir merger — default is to override */ 
    NULL, 
/* server config */ 
    NULL, 
/* merge server config */ 
    auth_cmds, /* command apr_table_t */ 
    register_hooks /* register hooks */ 
}; 
 |