Change the format of the revprops block sent in svnserve for
[svn.git] / subversion / mod_dav_svn / mod_dav_svn.c
blobfed2c95d267a1160d9bed19f4bca3b1c2e7e6f22
1 /*
2 * mod_dav_svn.c: an Apache mod_dav sub-module to provide a Subversion
3 * repository.
5 * ====================================================================
6 * Copyright (c) 2000-2007 CollabNet. All rights reserved.
8 * This software is licensed as described in the file COPYING, which
9 * you should have received as part of this distribution. The terms
10 * are also available at http://subversion.tigris.org/license-1.html.
11 * If newer versions of this license are posted there, you may use a
12 * newer version instead, at your option.
14 * This software consists of voluntary contributions made by many
15 * individuals. For exact contribution history, see the revision
16 * history and logs, available at http://subversion.tigris.org/.
17 * ====================================================================
20 #include <apr_strings.h>
22 #include <httpd.h>
23 #include <http_config.h>
24 #include <http_request.h>
25 #include <http_log.h>
26 #include <ap_provider.h>
27 #include <mod_dav.h>
29 #include "svn_version.h"
30 #include "svn_fs.h"
31 #include "svn_utf.h"
32 #include "svn_dso.h"
33 #include "mod_dav_svn.h"
35 #include "dav_svn.h"
36 #include "mod_authz_svn.h"
39 /* This is the default "special uri" used for SVN's special resources
40 (e.g. working resources, activities) */
41 #define SVN_DEFAULT_SPECIAL_URI "!svn"
43 /* This is the value to be given to SVNPathAuthz to bypass the apache
44 * subreq mechanism and make a call directly to mod_authz_svn. */
45 #define PATHAUTHZ_BYPASS_ARG "short_circuit"
47 /* per-server configuration */
48 typedef struct {
49 const char *special_uri;
50 } server_conf_t;
53 /* A tri-state enum used for per directory on/off flags. Note that
54 it's important that CONF_FLAG_DEFAULT is 0 to make
55 dav_svn_merge_dir_config do the right thing. */
56 enum conf_flag {
57 CONF_FLAG_DEFAULT,
58 CONF_FLAG_ON,
59 CONF_FLAG_OFF
62 /* An enum used for the per directory configuration path_authz_method. */
63 enum path_authz_conf {
64 CONF_PATHAUTHZ_DEFAULT,
65 CONF_PATHAUTHZ_ON,
66 CONF_PATHAUTHZ_OFF,
67 CONF_PATHAUTHZ_BYPASS
70 /* per-dir configuration */
71 typedef struct {
72 const char *fs_path; /* path to the SVN FS */
73 const char *repo_name; /* repository name */
74 const char *xslt_uri; /* XSL transform URI */
75 const char *fs_parent_path; /* path to parent of SVN FS'es */
76 enum conf_flag autoversioning; /* whether autoversioning is active */
77 enum path_authz_conf path_authz_method; /* how GET subrequests are handled */
78 enum conf_flag list_parentpath; /* whether to allow GET of parentpath */
79 const char *root_dir; /* our top-level directory */
80 const char *master_uri; /* URI to the master SVN repos */
81 const char *activities_db; /* path to activities database(s) */
82 } dir_conf_t;
85 #define INHERIT_VALUE(parent, child, field) \
86 ((child)->field ? (child)->field : (parent)->field)
89 extern module AP_MODULE_DECLARE_DATA dav_svn_module;
91 /* The authz_svn provider for bypassing path authz. */
92 static authz_svn__subreq_bypass_func_t pathauthz_bypass_func = NULL;
94 static int
95 init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
97 svn_error_t *serr;
98 ap_add_version_component(p, "SVN/" SVN_VER_NUMBER);
100 serr = svn_fs_initialize(p);
101 if (serr)
103 ap_log_perror(APLOG_MARK, APLOG_ERR, serr->apr_err, p,
104 "mod_dav_svn: error calling svn_fs_initialize: '%s'",
105 serr->message ? serr->message : "(no more info)");
106 return HTTP_INTERNAL_SERVER_ERROR;
109 /* This returns void, so we can't check for error. */
110 svn_utf_initialize(p);
112 return OK;
115 static int
116 init_dso(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
118 /* This isn't ideal, we're not actually being called before any
119 pool is created, but we are being called before the server or
120 request pools are created, which is probably good enough for
121 98% of cases. */
123 svn_dso_initialize();
125 return OK;
128 static void *
129 create_server_config(apr_pool_t *p, server_rec *s)
131 return apr_pcalloc(p, sizeof(server_conf_t));
135 static void *
136 merge_server_config(apr_pool_t *p, void *base, void *overrides)
138 server_conf_t *parent = base;
139 server_conf_t *child = overrides;
140 server_conf_t *newconf;
142 newconf = apr_pcalloc(p, sizeof(*newconf));
144 newconf->special_uri = INHERIT_VALUE(parent, child, special_uri);
146 return newconf;
150 static void *
151 create_dir_config(apr_pool_t *p, char *dir)
153 /* NOTE: dir==NULL creates the default per-dir config */
154 dir_conf_t *conf = apr_pcalloc(p, sizeof(*conf));
156 conf->root_dir = dir;
158 return conf;
162 static void *
163 merge_dir_config(apr_pool_t *p, void *base, void *overrides)
165 dir_conf_t *parent = base;
166 dir_conf_t *child = overrides;
167 dir_conf_t *newconf;
169 newconf = apr_pcalloc(p, sizeof(*newconf));
171 newconf->fs_path = INHERIT_VALUE(parent, child, fs_path);
172 newconf->master_uri = INHERIT_VALUE(parent, child, master_uri);
173 newconf->activities_db = INHERIT_VALUE(parent, child, activities_db);
174 newconf->repo_name = INHERIT_VALUE(parent, child, repo_name);
175 newconf->xslt_uri = INHERIT_VALUE(parent, child, xslt_uri);
176 newconf->fs_parent_path = INHERIT_VALUE(parent, child, fs_parent_path);
177 newconf->autoversioning = INHERIT_VALUE(parent, child, autoversioning);
178 newconf->path_authz_method = INHERIT_VALUE(parent, child, path_authz_method);
179 newconf->list_parentpath = INHERIT_VALUE(parent, child, list_parentpath);
180 /* Prefer our parent's value over our new one - hence the swap. */
181 newconf->root_dir = INHERIT_VALUE(child, parent, root_dir);
183 return newconf;
187 static const char *
188 SVNReposName_cmd(cmd_parms *cmd, void *config, const char *arg1)
190 dir_conf_t *conf = config;
192 conf->repo_name = apr_pstrdup(cmd->pool, arg1);
194 return NULL;
198 static const char *
199 SVNMasterURI_cmd(cmd_parms *cmd, void *config, const char *arg1)
201 dir_conf_t *conf = config;
203 conf->master_uri = apr_pstrdup(cmd->pool, arg1);
205 return NULL;
209 static const char *
210 SVNActivitiesDB_cmd(cmd_parms *cmd, void *config, const char *arg1)
212 dir_conf_t *conf = config;
214 conf->activities_db = apr_pstrdup(cmd->pool, arg1);
216 return NULL;
220 static const char *
221 SVNIndexXSLT_cmd(cmd_parms *cmd, void *config, const char *arg1)
223 dir_conf_t *conf = config;
225 conf->xslt_uri = apr_pstrdup(cmd->pool, arg1);
227 return NULL;
231 static const char *
232 SVNAutoversioning_cmd(cmd_parms *cmd, void *config, int arg)
234 dir_conf_t *conf = config;
236 if (arg)
237 conf->autoversioning = CONF_FLAG_ON;
238 else
239 conf->autoversioning = CONF_FLAG_OFF;
241 return NULL;
245 static const char *
246 SVNPathAuthz_cmd(cmd_parms *cmd, void *config, const char *arg1)
248 dir_conf_t *conf = config;
250 if (apr_strnatcasecmp("off", arg1) == 0)
251 conf->path_authz_method = CONF_PATHAUTHZ_OFF;
252 else if (apr_strnatcasecmp(PATHAUTHZ_BYPASS_ARG,arg1) == 0)
254 conf->path_authz_method = CONF_PATHAUTHZ_BYPASS;
255 if (pathauthz_bypass_func == NULL)
256 pathauthz_bypass_func=ap_lookup_provider(
257 AUTHZ_SVN__SUBREQ_BYPASS_PROV_GRP,
258 AUTHZ_SVN__SUBREQ_BYPASS_PROV_NAME,
259 AUTHZ_SVN__SUBREQ_BYPASS_PROV_VER);
261 else
262 conf->path_authz_method = CONF_PATHAUTHZ_ON;
264 return NULL;
268 static const char *
269 SVNListParentPath_cmd(cmd_parms *cmd, void *config, int arg)
271 dir_conf_t *conf = config;
273 if (arg)
274 conf->list_parentpath = CONF_FLAG_ON;
275 else
276 conf->list_parentpath = CONF_FLAG_OFF;
278 return NULL;
282 static const char *
283 SVNPath_cmd(cmd_parms *cmd, void *config, const char *arg1)
285 dir_conf_t *conf = config;
287 if (conf->fs_parent_path != NULL)
288 return "SVNPath cannot be defined at same time as SVNParentPath.";
290 conf->fs_path = svn_path_internal_style(apr_pstrdup(cmd->pool, arg1),
291 cmd->pool);
293 return NULL;
297 static const char *
298 SVNParentPath_cmd(cmd_parms *cmd, void *config, const char *arg1)
300 dir_conf_t *conf = config;
302 if (conf->fs_path != NULL)
303 return "SVNParentPath cannot be defined at same time as SVNPath.";
305 conf->fs_parent_path = svn_path_internal_style(apr_pstrdup(cmd->pool, arg1),
306 cmd->pool);
308 return NULL;
312 static const char *
313 SVNSpecialURI_cmd(cmd_parms *cmd, void *config, const char *arg1)
315 server_conf_t *conf;
316 char *uri;
317 apr_size_t len;
319 uri = apr_pstrdup(cmd->pool, arg1);
321 /* apply a bit of processing to the thing:
322 - eliminate .. and . components
323 - eliminate double slashes
324 - eliminate leading and trailing slashes
326 ap_getparents(uri);
327 ap_no2slash(uri);
328 if (*uri == '/')
329 ++uri;
330 len = strlen(uri);
331 if (len > 0 && uri[len - 1] == '/')
332 uri[--len] = '\0';
333 if (len == 0)
334 return "The special URI path must have at least one component.";
336 conf = ap_get_module_config(cmd->server->module_config,
337 &dav_svn_module);
338 conf->special_uri = uri;
340 return NULL;
344 /** Accessor functions for the module's configuration state **/
346 const char *
347 dav_svn__get_fs_path(request_rec *r)
349 dir_conf_t *conf;
351 conf = ap_get_module_config(r->per_dir_config, &dav_svn_module);
352 return conf->fs_path;
356 const char *
357 dav_svn__get_fs_parent_path(request_rec *r)
359 dir_conf_t *conf;
361 conf = ap_get_module_config(r->per_dir_config, &dav_svn_module);
362 return conf->fs_parent_path;
366 AP_MODULE_DECLARE(dav_error *)
367 dav_svn_get_repos_path(request_rec *r,
368 const char *root_path,
369 const char **repos_path)
372 const char *fs_path;
373 const char *fs_parent_path;
374 const char *repos_name;
375 const char *ignored_path_in_repos;
376 const char *ignored_cleaned_uri;
377 const char *ignored_relative;
378 int ignored_had_slash;
379 dav_error *derr;
381 /* Handle the SVNPath case. */
382 fs_path = dav_svn__get_fs_path(r);
384 if (fs_path != NULL)
386 *repos_path = fs_path;
387 return NULL;
390 /* Handle the SVNParentPath case. If neither directive was used,
391 dav_svn_split_uri will throw a suitable error for us - we do
392 not need to check that here. */
393 fs_parent_path = dav_svn__get_fs_parent_path(r);
395 /* Split the svn URI to get the name of the repository below
396 the parent path. */
397 derr = dav_svn_split_uri(r, r->uri, root_path,
398 &ignored_cleaned_uri, &ignored_had_slash,
399 &repos_name,
400 &ignored_relative, &ignored_path_in_repos);
401 if (derr)
402 return derr;
404 /* Construct the full path from the parent path base directory
405 and the repository name. */
406 *repos_path = svn_path_join(fs_parent_path, repos_name, r->pool);
407 return NULL;
411 const char *
412 dav_svn__get_repo_name(request_rec *r)
414 dir_conf_t *conf;
416 conf = ap_get_module_config(r->per_dir_config, &dav_svn_module);
417 return conf->repo_name;
421 const char *
422 dav_svn__get_root_dir(request_rec *r)
424 dir_conf_t *conf;
426 conf = ap_get_module_config(r->per_dir_config, &dav_svn_module);
427 return conf->root_dir;
431 const char *
432 dav_svn__get_master_uri(request_rec *r)
434 dir_conf_t *conf;
436 conf = ap_get_module_config(r->per_dir_config, &dav_svn_module);
437 return conf->master_uri;
441 const char *
442 dav_svn__get_xslt_uri(request_rec *r)
444 dir_conf_t *conf;
446 conf = ap_get_module_config(r->per_dir_config, &dav_svn_module);
447 return conf->xslt_uri;
451 const char *
452 dav_svn__get_special_uri(request_rec *r)
454 server_conf_t *conf;
456 conf = ap_get_module_config(r->server->module_config,
457 &dav_svn_module);
458 return conf->special_uri ? conf->special_uri : SVN_DEFAULT_SPECIAL_URI;
462 svn_boolean_t
463 dav_svn__get_autoversioning_flag(request_rec *r)
465 dir_conf_t *conf;
467 conf = ap_get_module_config(r->per_dir_config, &dav_svn_module);
468 return conf->autoversioning == CONF_FLAG_ON;
472 /* FALSE if path authorization should be skipped.
473 * TRUE if either the bypass or the apache subrequest methods should be used.
475 svn_boolean_t
476 dav_svn__get_pathauthz_flag(request_rec *r)
478 dir_conf_t *conf;
480 conf = ap_get_module_config(r->per_dir_config, &dav_svn_module);
481 return conf->path_authz_method != CONF_PATHAUTHZ_OFF;
484 /* Function pointer if we should use the bypass directly to mod_authz_svn.
485 * NULL otherwise. */
486 authz_svn__subreq_bypass_func_t
487 dav_svn__get_pathauthz_bypass(request_rec *r)
489 dir_conf_t *conf;
491 conf = ap_get_module_config(r->per_dir_config, &dav_svn_module);
493 if (conf->path_authz_method==CONF_PATHAUTHZ_BYPASS)
494 return pathauthz_bypass_func;
495 return NULL;
499 svn_boolean_t
500 dav_svn__get_list_parentpath_flag(request_rec *r)
502 dir_conf_t *conf;
504 conf = ap_get_module_config(r->per_dir_config, &dav_svn_module);
505 return conf->list_parentpath == CONF_FLAG_ON;
509 const char *
510 dav_svn__get_activities_db(request_rec *r)
512 dir_conf_t *conf;
514 conf = ap_get_module_config(r->per_dir_config, &dav_svn_module);
515 return conf->activities_db;
519 static void
520 merge_xml_filter_insert(request_rec *r)
522 /* We only care about MERGE and DELETE requests. */
523 if ((r->method_number == M_MERGE)
524 || (r->method_number == M_DELETE))
526 dir_conf_t *conf;
527 conf = ap_get_module_config(r->per_dir_config, &dav_svn_module);
529 /* We only care if we are configured. */
530 if (conf->fs_path || conf->fs_parent_path)
532 ap_add_input_filter("SVN-MERGE", NULL, r, r->connection);
538 typedef struct {
539 apr_bucket_brigade *bb;
540 apr_xml_parser *parser;
541 apr_pool_t *pool;
542 } merge_ctx_t;
545 static apr_status_t
546 merge_xml_in_filter(ap_filter_t *f,
547 apr_bucket_brigade *bb,
548 ap_input_mode_t mode,
549 apr_read_type_e block,
550 apr_off_t readbytes)
552 apr_status_t rv;
553 request_rec *r = f->r;
554 merge_ctx_t *ctx = f->ctx;
555 apr_bucket *bucket;
556 int seen_eos = 0;
558 /* We shouldn't be added if we're not a MERGE/DELETE, but double check. */
559 if ((r->method_number != M_MERGE)
560 && (r->method_number != M_DELETE))
562 ap_remove_input_filter(f);
563 return ap_get_brigade(f->next, bb, mode, block, readbytes);
566 if (!ctx)
568 f->ctx = ctx = apr_palloc(r->pool, sizeof(*ctx));
569 ctx->parser = apr_xml_parser_create(r->pool);
570 ctx->bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
571 apr_pool_create(&ctx->pool, r->pool);
574 rv = ap_get_brigade(f->next, ctx->bb, mode, block, readbytes);
576 if (rv != APR_SUCCESS)
577 return rv;
579 for (bucket = APR_BRIGADE_FIRST(ctx->bb);
580 bucket != APR_BRIGADE_SENTINEL(ctx->bb);
581 bucket = APR_BUCKET_NEXT(bucket))
583 const char *data;
584 apr_size_t len;
586 if (APR_BUCKET_IS_EOS(bucket))
588 seen_eos = 1;
589 break;
592 if (APR_BUCKET_IS_METADATA(bucket))
593 continue;
595 rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
596 if (rv != APR_SUCCESS)
597 return rv;
599 rv = apr_xml_parser_feed(ctx->parser, data, len);
600 if (rv != APR_SUCCESS)
602 /* Clean up the parser. */
603 (void) apr_xml_parser_done(ctx->parser, NULL);
604 break;
608 /* This will clear-out the ctx->bb as well. */
609 APR_BRIGADE_CONCAT(bb, ctx->bb);
611 if (seen_eos)
613 apr_xml_doc *pdoc;
615 /* Remove ourselves now. */
616 ap_remove_input_filter(f);
618 /* tell the parser that we're done */
619 rv = apr_xml_parser_done(ctx->parser, &pdoc);
620 if (rv == APR_SUCCESS)
622 #if APR_CHARSET_EBCDIC
623 apr_xml_parser_convert_doc(r->pool, pdoc, ap_hdrs_from_ascii);
624 #endif
625 /* stash the doc away for mod_dav_svn's later use. */
626 rv = apr_pool_userdata_set(pdoc, "svn-request-body",
627 NULL, r->pool);
628 if (rv != APR_SUCCESS)
629 return rv;
634 return APR_SUCCESS;
639 /** Module framework stuff **/
641 static const command_rec cmds[] =
643 /* per directory/location */
644 AP_INIT_TAKE1("SVNPath", SVNPath_cmd, NULL, ACCESS_CONF,
645 "specifies the location in the filesystem for a Subversion "
646 "repository's files."),
648 /* per server */
649 AP_INIT_TAKE1("SVNSpecialURI", SVNSpecialURI_cmd, NULL, RSRC_CONF,
650 "specify the URI component for special Subversion "
651 "resources"),
653 /* per directory/location */
654 AP_INIT_TAKE1("SVNReposName", SVNReposName_cmd, NULL, ACCESS_CONF,
655 "specify the name of a Subversion repository"),
657 /* per directory/location */
658 AP_INIT_TAKE1("SVNIndexXSLT", SVNIndexXSLT_cmd, NULL, ACCESS_CONF,
659 "specify the URI of an XSL transformation for "
660 "directory indexes"),
662 /* per directory/location */
663 AP_INIT_TAKE1("SVNParentPath", SVNParentPath_cmd, NULL, ACCESS_CONF,
664 "specifies the location in the filesystem whose "
665 "subdirectories are assumed to be Subversion repositories."),
667 /* per directory/location */
668 AP_INIT_FLAG("SVNAutoversioning", SVNAutoversioning_cmd, NULL,
669 ACCESS_CONF|RSRC_CONF, "turn on deltaV autoversioning."),
671 /* per directory/location */
672 AP_INIT_TAKE1("SVNPathAuthz", SVNPathAuthz_cmd, NULL,
673 ACCESS_CONF|RSRC_CONF,
674 "control path-based authz by enabling subrequests(On,default), "
675 "disabling subrequests(Off), or"
676 "querying mod_authz_svn directly(" PATHAUTHZ_BYPASS_ARG ")"),
678 /* per directory/location */
679 AP_INIT_FLAG("SVNListParentPath", SVNListParentPath_cmd, NULL,
680 ACCESS_CONF|RSRC_CONF, "allow GET of SVNParentPath."),
682 /* per directory/location */
683 AP_INIT_TAKE1("SVNMasterURI", SVNMasterURI_cmd, NULL, ACCESS_CONF,
684 "specifies a URI to access a master Subversion repository"),
686 /* per directory/location */
687 AP_INIT_TAKE1("SVNActivitiesDB", SVNActivitiesDB_cmd, NULL, ACCESS_CONF,
688 "specifies the location in the filesystem in which the "
689 "activities database(s) should be stored"),
692 { NULL }
696 static dav_provider provider =
698 &dav_svn__hooks_repository,
699 &dav_svn__hooks_propdb,
700 &dav_svn__hooks_locks,
701 &dav_svn__hooks_vsn,
702 NULL, /* binding */
703 NULL /* search */
707 static void
708 register_hooks(apr_pool_t *pconf)
710 ap_hook_pre_config(init_dso, NULL, NULL, APR_HOOK_REALLY_FIRST);
711 ap_hook_post_config(init, NULL, NULL, APR_HOOK_MIDDLE);
713 /* our provider */
714 dav_register_provider(pconf, "svn", &provider);
716 /* input filter to read MERGE bodies. */
717 ap_register_input_filter("SVN-MERGE", merge_xml_in_filter, NULL,
718 AP_FTYPE_RESOURCE);
719 ap_hook_insert_filter(merge_xml_filter_insert, NULL, NULL,
720 APR_HOOK_MIDDLE);
722 /* live property handling */
723 dav_hook_gather_propsets(dav_svn__gather_propsets, NULL, NULL,
724 APR_HOOK_MIDDLE);
725 dav_hook_find_liveprop(dav_svn__find_liveprop, NULL, NULL, APR_HOOK_MIDDLE);
726 dav_hook_insert_all_liveprops(dav_svn__insert_all_liveprops, NULL, NULL,
727 APR_HOOK_MIDDLE);
728 dav_register_liveprop_group(pconf, &dav_svn__liveprop_group);
730 /* Proxy / mirroring filters and fixups */
731 ap_register_output_filter("LocationRewrite", dav_svn__location_header_filter,
732 NULL, AP_FTYPE_CONTENT_SET);
733 ap_register_output_filter("ReposRewrite", dav_svn__location_body_filter,
734 NULL, AP_FTYPE_CONTENT_SET);
735 ap_register_input_filter("IncomingRewrite", dav_svn__location_in_filter,
736 NULL, AP_FTYPE_CONTENT_SET);
737 ap_hook_fixups(dav_svn__proxy_merge_fixup, NULL, NULL, APR_HOOK_MIDDLE);
741 module AP_MODULE_DECLARE_DATA dav_svn_module =
743 STANDARD20_MODULE_STUFF,
744 create_dir_config, /* dir config creater */
745 merge_dir_config, /* dir merger --- default is to override */
746 create_server_config, /* server config */
747 merge_server_config, /* merge server config */
748 cmds, /* command table */
749 register_hooks, /* register hooks */