1 /*
2  * get-locations.c: mod_dav_svn REPORT handler for finding repos locations
3  *                  (path/revision pairs) in an object's history.
4  *
5  * ====================================================================
6  *    Licensed to the Apache Software Foundation (ASF) under one
7  *    or more contributor license agreements.  See the NOTICE file
8  *    distributed with this work for additional information
9  *    regarding copyright ownership.  The ASF licenses this file
10  *    to you under the Apache License, Version 2.0 (the
11  *    "License"); you may not use this file except in compliance
12  *    with the License.  You may obtain a copy of the License at
13  *
14  *      http://www.apache.org/licenses/LICENSE-2.0
15  *
16  *    Unless required by applicable law or agreed to in writing,
17  *    software distributed under the License is distributed on an
18  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19  *    KIND, either express or implied.  See the License for the
20  *    specific language governing permissions and limitations
21  *    under the License.
22  * ====================================================================
23  */
24 
25 #include <apr_tables.h>
26 #include <apr_uuid.h>
27 
28 #include <httpd.h>
29 #include <http_log.h>
30 #include <mod_dav.h>
31 
32 #include "svn_fs.h"
33 #include "svn_xml.h"
34 #include "svn_repos.h"
35 #include "svn_dav.h"
36 #include "svn_time.h"
37 #include "svn_pools.h"
38 #include "svn_props.h"
39 #include "svn_dav.h"
40 #include "svn_base64.h"
41 
42 #include "private/svn_fspath.h"
43 
44 #include "../dav_svn.h"
45 
46 
47 static svn_error_t *
send_get_locations_report(dav_svn__output * output,apr_bucket_brigade * bb,const dav_resource * resource,apr_hash_t * fs_locations)48 send_get_locations_report(dav_svn__output *output,
49                           apr_bucket_brigade *bb,
50                           const dav_resource *resource,
51                           apr_hash_t *fs_locations)
52 {
53   apr_hash_index_t *hi;
54   apr_pool_t *pool = resource->pool;
55 
56   SVN_ERR(dav_svn__brigade_printf(
57                        bb, output,
58                        DAV_XML_HEADER DEBUG_CR
59                        "<S:get-locations-report xmlns:S=\"" SVN_XML_NAMESPACE
60                        "\" xmlns:D=\"DAV:\">" DEBUG_CR));
61 
62   for (hi = apr_hash_first(pool, fs_locations); hi; hi = apr_hash_next(hi))
63     {
64       const void *key;
65       void *value;
66       const char *path_quoted;
67 
68       apr_hash_this(hi, &key, NULL, &value);
69       path_quoted = apr_xml_quote_string(pool, value, 1);
70       SVN_ERR(dav_svn__brigade_printf(
71                            bb, output, "<S:location "
72                            "rev=\"%ld\" path=\"%s\"/>" DEBUG_CR,
73                            *(const svn_revnum_t *)key, path_quoted));
74     }
75 
76   SVN_ERR(dav_svn__brigade_printf(bb, output,
77                                   "</S:get-locations-report>" DEBUG_CR));
78   return SVN_NO_ERROR;
79 }
80 
81 
82 dav_error *
dav_svn__get_locations_report(const dav_resource * resource,const apr_xml_doc * doc,dav_svn__output * output)83 dav_svn__get_locations_report(const dav_resource *resource,
84                               const apr_xml_doc *doc,
85                               dav_svn__output *output)
86 {
87   svn_error_t *serr;
88   dav_error *derr = NULL;
89   apr_bucket_brigade *bb;
90   dav_svn__authz_read_baton arb;
91 
92   /* The parameters to do the operation on. */
93   const char *abs_path = NULL;
94   svn_revnum_t peg_revision = SVN_INVALID_REVNUM;
95   apr_array_header_t *location_revisions;
96 
97   /* XML Parsing Variables */
98   int ns;
99   apr_xml_elem *child;
100 
101   apr_hash_t *fs_locations;
102 
103   location_revisions = apr_array_make(resource->pool, 0,
104                                       sizeof(svn_revnum_t));
105 
106   /* Sanity check. */
107   if (!resource->info->repos_path)
108     return dav_svn__new_error(resource->pool, HTTP_BAD_REQUEST, 0, 0,
109                               "The request does not specify a repository path");
110   ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE);
111   if (ns == -1)
112     {
113       return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0, 0,
114                                     "The request does not contain the 'svn:' "
115                                     "namespace, so it is not going to have "
116                                     "certain required elements");
117     }
118 
119   /* Gather the parameters. */
120   for (child = doc->root->first_child; child != NULL; child = child->next)
121     {
122       /* If this element isn't one of ours, then skip it. */
123       if (child->ns != ns)
124         continue;
125 
126       if (strcmp(child->name, "peg-revision") == 0)
127         peg_revision = SVN_STR_TO_REV(dav_xml_get_cdata(child,
128                                                         resource->pool, 1));
129       else if (strcmp(child->name, "location-revision") == 0)
130         {
131           svn_revnum_t revision
132             = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1));
133           APR_ARRAY_PUSH(location_revisions, svn_revnum_t) = revision;
134         }
135       else if (strcmp(child->name, "path") == 0)
136         {
137           const char *rel_path = dav_xml_get_cdata(child, resource->pool, 0);
138           if ((derr = dav_svn__test_canonical(rel_path, resource->pool)))
139             return derr;
140 
141           /* Force REL_PATH to be a relative path, not an fspath. */
142           rel_path = svn_relpath_canonicalize(rel_path, resource->pool);
143 
144           /* Append the REL_PATH to the base FS path to get an absolute
145              repository path. */
146           abs_path = svn_fspath__join(resource->info->repos_path, rel_path,
147                                       resource->pool);
148         }
149     }
150 
151   /* Check that all parameters are present and valid. */
152   if (! (abs_path && SVN_IS_VALID_REVNUM(peg_revision)))
153     return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0, 0,
154                                   "Not all parameters passed");
155 
156   /* Build an authz read baton */
157   arb.r = resource->info->r;
158   arb.repos = resource->info->repos;
159 
160   serr = svn_repos_trace_node_locations(resource->info->repos->fs,
161                                         &fs_locations, abs_path, peg_revision,
162                                         location_revisions,
163                                         dav_svn__authz_read_func(&arb), &arb,
164                                         resource->pool);
165 
166   if (serr)
167     {
168       return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, NULL,
169                                   resource->pool);
170     }
171 
172   bb = apr_brigade_create(resource->pool,
173                           dav_svn__output_get_bucket_alloc(output));
174 
175   serr = send_get_locations_report(output, bb, resource, fs_locations);
176 
177   if (serr)
178     derr = dav_svn__convert_err(serr,
179                                 HTTP_INTERNAL_SERVER_ERROR,
180                                 "Error writing REPORT response.",
181                                 resource->pool);
182 
183   return dav_svn__final_flush_or_error(resource->info->r, bb, output,
184                                        derr, resource->pool);
185 }
186