1251881Speter/*
2251881Speter * serve.c :  Functions for serving the Subversion protocol
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter
25251881Speter
26251881Speter
27251881Speter#include <limits.h> /* for UINT_MAX */
28251881Speter#include <stdarg.h>
29251881Speter
30251881Speter#define APR_WANT_STRFUNC
31251881Speter#include <apr_want.h>
32251881Speter#include <apr_general.h>
33251881Speter#include <apr_lib.h>
34251881Speter#include <apr_strings.h>
35251881Speter
36251881Speter#include "svn_compat.h"
37251881Speter#include "svn_private_config.h"  /* For SVN_PATH_LOCAL_SEPARATOR */
38251881Speter#include "svn_hash.h"
39251881Speter#include "svn_types.h"
40251881Speter#include "svn_string.h"
41251881Speter#include "svn_pools.h"
42251881Speter#include "svn_error.h"
43251881Speter#include "svn_ra.h"              /* for SVN_RA_CAPABILITY_* */
44251881Speter#include "svn_ra_svn.h"
45251881Speter#include "svn_repos.h"
46251881Speter#include "svn_dirent_uri.h"
47251881Speter#include "svn_path.h"
48251881Speter#include "svn_time.h"
49251881Speter#include "svn_config.h"
50251881Speter#include "svn_props.h"
51251881Speter#include "svn_mergeinfo.h"
52251881Speter#include "svn_user.h"
53251881Speter
54251881Speter#include "private/svn_log.h"
55251881Speter#include "private/svn_mergeinfo_private.h"
56251881Speter#include "private/svn_ra_svn_private.h"
57251881Speter#include "private/svn_fspath.h"
58251881Speter
59251881Speter#ifdef HAVE_UNISTD_H
60251881Speter#include <unistd.h>   /* For getpid() */
61251881Speter#endif
62251881Speter
63251881Speter#include "server.h"
64289180Speter#include "logger.h"
65251881Speter
66251881Spetertypedef struct commit_callback_baton_t {
67251881Speter  apr_pool_t *pool;
68251881Speter  svn_revnum_t *new_rev;
69251881Speter  const char **date;
70251881Speter  const char **author;
71251881Speter  const char **post_commit_err;
72251881Speter} commit_callback_baton_t;
73251881Speter
74251881Spetertypedef struct report_driver_baton_t {
75251881Speter  server_baton_t *sb;
76251881Speter  const char *repos_url;  /* Decoded repository URL. */
77251881Speter  void *report_baton;
78251881Speter  svn_error_t *err;
79251881Speter  /* so update() can distinguish checkout from update in logging */
80251881Speter  int entry_counter;
81251881Speter  svn_boolean_t only_empty_entries;
82251881Speter  /* for diff() logging */
83251881Speter  svn_revnum_t *from_rev;
84251881Speter} report_driver_baton_t;
85251881Speter
86251881Spetertypedef struct log_baton_t {
87251881Speter  const char *fs_path;
88251881Speter  svn_ra_svn_conn_t *conn;
89251881Speter  int stack_depth;
90362181Sdim
91362181Sdim  /* Set to TRUE when at least one changed path has been sent. */
92362181Sdim  svn_boolean_t started;
93251881Speter} log_baton_t;
94251881Speter
95251881Spetertypedef struct file_revs_baton_t {
96251881Speter  svn_ra_svn_conn_t *conn;
97251881Speter  apr_pool_t *pool;  /* Pool provided in the handler call. */
98251881Speter} file_revs_baton_t;
99251881Speter
100251881Spetertypedef struct fs_warning_baton_t {
101251881Speter  server_baton_t *server;
102251881Speter  svn_ra_svn_conn_t *conn;
103251881Speter} fs_warning_baton_t;
104251881Speter
105251881Spetertypedef struct authz_baton_t {
106251881Speter  server_baton_t *server;
107251881Speter  svn_ra_svn_conn_t *conn;
108251881Speter} authz_baton_t;
109251881Speter
110362181Sdim/* Log an error. */
111251881Speterstatic void
112362181Sdimlog_error(const svn_error_t *err, server_baton_t *server)
113251881Speter{
114289180Speter  logger__log_error(server->logger, err, server->repository,
115289180Speter                    server->client_info);
116251881Speter}
117251881Speter
118362181Sdim/* Log a warning. */
119362181Sdimstatic void
120362181Sdimlog_warning(const svn_error_t *err, server_baton_t *server)
121362181Sdim{
122362181Sdim  logger__log_warning(server->logger, err, server->repository,
123362181Sdim                      server->client_info);
124362181Sdim}
125362181Sdim
126251881Speter/* svn_error_create() a new error, log_server_error() it, and
127251881Speter   return it. */
128251881Speterstatic svn_error_t *
129251881Spetererror_create_and_log(apr_status_t apr_err, svn_error_t *child,
130289180Speter                     const char *message, server_baton_t *server)
131251881Speter{
132251881Speter  svn_error_t *err = svn_error_create(apr_err, child, message);
133289180Speter  log_error(err, server);
134251881Speter  return err;
135251881Speter}
136251881Speter
137251881Speter/* Log a failure ERR, transmit ERR back to the client (as part of a
138251881Speter   "failure" notification), consume ERR, and flush the connection. */
139251881Speterstatic svn_error_t *
140251881Speterlog_fail_and_flush(svn_error_t *err, server_baton_t *server,
141251881Speter                   svn_ra_svn_conn_t *conn, apr_pool_t *pool)
142251881Speter{
143251881Speter  svn_error_t *io_err;
144251881Speter
145289180Speter  log_error(err, server);
146251881Speter  io_err = svn_ra_svn__write_cmd_failure(conn, pool, err);
147251881Speter  svn_error_clear(err);
148251881Speter  SVN_ERR(io_err);
149251881Speter  return svn_ra_svn__flush(conn, pool);
150251881Speter}
151251881Speter
152251881Speter/* Log a client command. */
153251881Speterstatic svn_error_t *log_command(server_baton_t *b,
154251881Speter                                svn_ra_svn_conn_t *conn,
155251881Speter                                apr_pool_t *pool,
156251881Speter                                const char *fmt, ...)
157251881Speter{
158251881Speter  const char *remote_host, *timestr, *log, *line;
159251881Speter  va_list ap;
160251881Speter  apr_size_t nbytes;
161251881Speter
162289180Speter  if (b->logger == NULL)
163251881Speter    return SVN_NO_ERROR;
164251881Speter
165251881Speter  remote_host = svn_ra_svn_conn_remote_host(conn);
166251881Speter  timestr = svn_time_to_cstring(apr_time_now(), pool);
167251881Speter
168251881Speter  va_start(ap, fmt);
169251881Speter  log = apr_pvsprintf(pool, fmt, ap);
170251881Speter  va_end(ap);
171251881Speter
172251881Speter  line = apr_psprintf(pool, "%" APR_PID_T_FMT
173251881Speter                      " %s %s %s %s %s" APR_EOL_STR,
174251881Speter                      getpid(), timestr,
175251881Speter                      (remote_host ? remote_host : "-"),
176289180Speter                      (b->client_info->user ? b->client_info->user : "-"),
177289180Speter                      b->repository->repos_name, log);
178251881Speter  nbytes = strlen(line);
179251881Speter
180289180Speter  return logger__write(b->logger, line, nbytes);
181251881Speter}
182251881Speter
183251881Speter/* Log an authz failure */
184251881Speterstatic svn_error_t *
185251881Speterlog_authz_denied(const char *path,
186251881Speter                 svn_repos_authz_access_t required,
187251881Speter                 server_baton_t *b,
188251881Speter                 apr_pool_t *pool)
189251881Speter{
190251881Speter  const char *timestr, *remote_host, *line;
191251881Speter
192289180Speter  if (!b->logger)
193251881Speter    return SVN_NO_ERROR;
194251881Speter
195289180Speter  if (!b->client_info || !b->client_info->user)
196251881Speter    return SVN_NO_ERROR;
197251881Speter
198251881Speter  timestr = svn_time_to_cstring(apr_time_now(), pool);
199289180Speter  remote_host = b->client_info->remote_host;
200251881Speter
201251881Speter  line = apr_psprintf(pool, "%" APR_PID_T_FMT
202251881Speter                      " %s %s %s %s Authorization Failed %s%s %s" APR_EOL_STR,
203251881Speter                      getpid(), timestr,
204251881Speter                      (remote_host ? remote_host : "-"),
205289180Speter                      b->client_info->user,
206289180Speter                      b->repository->repos_name,
207251881Speter                      (required & svn_authz_recursive ? "recursive " : ""),
208251881Speter                      (required & svn_authz_write ? "write" : "read"),
209251881Speter                      (path && path[0] ? path : "/"));
210251881Speter
211289180Speter  return logger__write(b->logger, line, strlen(line));
212251881Speter}
213251881Speter
214289180Speter/* If CFG specifies a path to the password DB, read that DB through
215289180Speter * CONFIG_POOL and store it in REPOSITORY->PWDB.
216289180Speter */
217289180Speterstatic svn_error_t *
218289180Speterload_pwdb_config(repository_t *repository,
219289180Speter                 svn_config_t *cfg,
220289180Speter                 svn_repos__config_pool_t *config_pool,
221289180Speter                 apr_pool_t *pool)
222251881Speter{
223251881Speter  const char *pwdb_path;
224251881Speter  svn_error_t *err;
225251881Speter
226289180Speter  svn_config_get(cfg, &pwdb_path,
227289180Speter                 SVN_CONFIG_SECTION_GENERAL,
228251881Speter                 SVN_CONFIG_OPTION_PASSWORD_DB, NULL);
229251881Speter
230289180Speter  repository->pwdb = NULL;
231251881Speter  if (pwdb_path)
232251881Speter    {
233251881Speter      pwdb_path = svn_dirent_internal_style(pwdb_path, pool);
234289180Speter      pwdb_path = svn_dirent_join(repository->base, pwdb_path, pool);
235251881Speter
236362181Sdim      err = svn_repos__config_pool_get(&repository->pwdb, config_pool,
237362181Sdim                                       pwdb_path, TRUE,
238289180Speter                                       repository->repos, pool);
239251881Speter      if (err)
240251881Speter        {
241251881Speter          /* Because it may be possible to read the pwdb file with some
242251881Speter             access methods and not others, ignore errors reading the pwdb
243251881Speter             file and just don't present password authentication as an
244251881Speter             option.  Also, some authentications (e.g. --tunnel) can
245251881Speter             proceed without it anyway.
246251881Speter
247251881Speter             ### Not entirely sure why SVN_ERR_BAD_FILENAME is checked
248251881Speter             ### for here.  That seems to have been introduced in r856914,
249251881Speter             ### and only in r870942 was the APR_EACCES check introduced. */
250251881Speter          if (err->apr_err != SVN_ERR_BAD_FILENAME
251251881Speter              && ! APR_STATUS_IS_EACCES(err->apr_err))
252251881Speter            {
253289180Speter              return svn_error_create(SVN_ERR_AUTHN_FAILED, err, NULL);
254251881Speter            }
255251881Speter          else
256251881Speter            /* Ignore SVN_ERR_BAD_FILENAME and APR_EACCES and proceed. */
257251881Speter            svn_error_clear(err);
258251881Speter        }
259251881Speter    }
260251881Speter
261251881Speter  return SVN_NO_ERROR;
262251881Speter}
263251881Speter
264251881Speter/* Canonicalize *ACCESS_FILE based on the type of argument.  Results are
265289180Speter * placed in *ACCESS_FILE.  REPOSITORY is used to convert relative paths to
266251881Speter * absolute paths rooted at the server root.  REPOS_ROOT is used to calculate
267251881Speter * an absolute URL for repos-relative URLs. */
268251881Speterstatic svn_error_t *
269289180Spetercanonicalize_access_file(const char **access_file, repository_t *repository,
270251881Speter                         const char *repos_root, apr_pool_t *pool)
271251881Speter{
272251881Speter  if (svn_path_is_url(*access_file))
273251881Speter    {
274362181Sdim      const char *canonical_url;
275362181Sdim      SVN_ERR(svn_uri_canonicalize_safe(&canonical_url, NULL, *access_file,
276362181Sdim                                        pool, pool));
277362181Sdim      *access_file = canonical_url;
278251881Speter    }
279251881Speter  else if (svn_path_is_repos_relative_url(*access_file))
280251881Speter    {
281251881Speter      const char *repos_root_url;
282362181Sdim      const char *canonical_url;
283251881Speter
284251881Speter      SVN_ERR(svn_uri_get_file_url_from_dirent(&repos_root_url, repos_root,
285251881Speter                                               pool));
286251881Speter      SVN_ERR(svn_path_resolve_repos_relative_url(access_file, *access_file,
287251881Speter                                                  repos_root_url, pool));
288362181Sdim      SVN_ERR(svn_uri_canonicalize_safe(&canonical_url, NULL, *access_file,
289362181Sdim                                        pool, pool));
290362181Sdim      *access_file = canonical_url;
291251881Speter    }
292251881Speter  else
293251881Speter    {
294251881Speter      *access_file = svn_dirent_internal_style(*access_file, pool);
295289180Speter      *access_file = svn_dirent_join(repository->base, *access_file, pool);
296251881Speter    }
297251881Speter
298251881Speter  return SVN_NO_ERROR;
299251881Speter}
300251881Speter
301362181Sdim/* Load the authz database for the listening server based on the entries
302362181Sdim   in the SERVER struct.
303289180Speter
304289180Speter   SERVER and CONN must not be NULL. The real errors will be logged with
305289180Speter   SERVER and CONN but return generic errors to the client. */
306289180Speterstatic svn_error_t *
307289180Speterload_authz_config(repository_t *repository,
308289180Speter                  const char *repos_root,
309289180Speter                  svn_config_t *cfg,
310362181Sdim                  svn_repos_authz_warning_func_t warning_func,
311362181Sdim                  void *warning_baton,
312362181Sdim                  apr_pool_t *result_pool,
313362181Sdim                  apr_pool_t *scratch_pool)
314251881Speter{
315251881Speter  const char *authzdb_path;
316251881Speter  const char *groupsdb_path;
317251881Speter  svn_error_t *err;
318251881Speter
319251881Speter  /* Read authz configuration. */
320289180Speter  svn_config_get(cfg, &authzdb_path, SVN_CONFIG_SECTION_GENERAL,
321251881Speter                 SVN_CONFIG_OPTION_AUTHZ_DB, NULL);
322251881Speter
323289180Speter  svn_config_get(cfg, &groupsdb_path, SVN_CONFIG_SECTION_GENERAL,
324251881Speter                 SVN_CONFIG_OPTION_GROUPS_DB, NULL);
325251881Speter
326251881Speter  if (authzdb_path)
327251881Speter    {
328251881Speter      const char *case_force_val;
329251881Speter
330251881Speter      /* Canonicalize and add the base onto the authzdb_path (if needed). */
331289180Speter      err = canonicalize_access_file(&authzdb_path, repository,
332362181Sdim                                     repos_root, scratch_pool);
333251881Speter
334251881Speter      /* Same for the groupsdb_path if it is present. */
335251881Speter      if (groupsdb_path && !err)
336289180Speter        err = canonicalize_access_file(&groupsdb_path, repository,
337362181Sdim                                       repos_root, scratch_pool);
338251881Speter
339251881Speter      if (!err)
340362181Sdim        err = svn_repos_authz_read4(&repository->authzdb, authzdb_path,
341362181Sdim                                    groupsdb_path, TRUE, repository->repos,
342362181Sdim                                    warning_func, warning_baton,
343362181Sdim                                    result_pool, scratch_pool);
344251881Speter
345251881Speter      if (err)
346289180Speter        return svn_error_create(SVN_ERR_AUTHZ_INVALID_CONFIG, err, NULL);
347251881Speter
348251881Speter      /* Are we going to be case-normalizing usernames when we consult
349251881Speter       * this authz file? */
350289180Speter      svn_config_get(cfg, &case_force_val,
351289180Speter                     SVN_CONFIG_SECTION_GENERAL,
352251881Speter                     SVN_CONFIG_OPTION_FORCE_USERNAME_CASE, NULL);
353251881Speter      if (case_force_val)
354251881Speter        {
355251881Speter          if (strcmp(case_force_val, "upper") == 0)
356289180Speter            repository->username_case = CASE_FORCE_UPPER;
357251881Speter          else if (strcmp(case_force_val, "lower") == 0)
358289180Speter            repository->username_case = CASE_FORCE_LOWER;
359251881Speter          else
360289180Speter            repository->username_case = CASE_ASIS;
361251881Speter        }
362251881Speter    }
363251881Speter  else
364251881Speter    {
365289180Speter      repository->authzdb = NULL;
366289180Speter      repository->username_case = CASE_ASIS;
367251881Speter    }
368251881Speter
369251881Speter  return SVN_NO_ERROR;
370251881Speter}
371251881Speter
372289180Speter/* If ERROR is a AUTH* error as returned by load_pwdb_config or
373289180Speter * load_authz_config, write it to SERVER's log file.
374289180Speter * Return a sanitized version of ERROR.
375289180Speter */
376289180Speterstatic svn_error_t *
377289180Speterhandle_config_error(svn_error_t *error,
378289180Speter                    server_baton_t *server)
379289180Speter{
380289180Speter  if (   error
381289180Speter      && (   error->apr_err == SVN_ERR_AUTHZ_INVALID_CONFIG
382289180Speter          || error->apr_err == SVN_ERR_AUTHN_FAILED))
383289180Speter    {
384289180Speter      apr_status_t apr_err = error->apr_err;
385289180Speter      log_error(error, server);
386289180Speter
387289180Speter      /* Now that we've logged the error, clear it and return a
388289180Speter       * nice, generic error to the user:
389362181Sdim       * https://issues.apache.org/jira/browse/SVN-2271 */
390289180Speter      svn_error_clear(error);
391289180Speter      return svn_error_create(apr_err, NULL, NULL);
392289180Speter    }
393289180Speter
394289180Speter  return error;
395289180Speter}
396289180Speter
397251881Speter/* Set *FS_PATH to the portion of URL that is the path within the
398251881Speter   repository, if URL is inside REPOS_URL (if URL is not inside
399251881Speter   REPOS_URL, then error, with the effect on *FS_PATH undefined).
400251881Speter
401251881Speter   If the resultant fs path would be the empty string (i.e., URL and
402251881Speter   REPOS_URL are the same), then set *FS_PATH to "/".
403251881Speter
404251881Speter   Assume that REPOS_URL and URL are already URI-decoded. */
405251881Speterstatic svn_error_t *get_fs_path(const char *repos_url, const char *url,
406251881Speter                                const char **fs_path)
407251881Speter{
408251881Speter  apr_size_t len;
409251881Speter
410251881Speter  len = strlen(repos_url);
411251881Speter  if (strncmp(url, repos_url, len) != 0)
412251881Speter    return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
413251881Speter                             "'%s' is not the same repository as '%s'",
414251881Speter                             url, repos_url);
415251881Speter  *fs_path = url + len;
416251881Speter  if (! **fs_path)
417251881Speter    *fs_path = "/";
418251881Speter
419251881Speter  return SVN_NO_ERROR;
420251881Speter}
421251881Speter
422251881Speter/* --- AUTHENTICATION AND AUTHORIZATION FUNCTIONS --- */
423251881Speter
424251881Speter/* Convert TEXT to upper case if TO_UPPERCASE is TRUE, else
425251881Speter   converts it to lower case. */
426251881Speterstatic void convert_case(char *text, svn_boolean_t to_uppercase)
427251881Speter{
428251881Speter  char *c = text;
429251881Speter  while (*c)
430251881Speter    {
431251881Speter      *c = (char)(to_uppercase ? apr_toupper(*c) : apr_tolower(*c));
432251881Speter      ++c;
433251881Speter    }
434251881Speter}
435251881Speter
436251881Speter/* Set *ALLOWED to TRUE if PATH is accessible in the REQUIRED mode to
437251881Speter   the user described in BATON according to the authz rules in BATON.
438251881Speter   Use POOL for temporary allocations only.  If no authz rules are
439251881Speter   present in BATON, grant access by default. */
440251881Speterstatic svn_error_t *authz_check_access(svn_boolean_t *allowed,
441251881Speter                                       const char *path,
442251881Speter                                       svn_repos_authz_access_t required,
443251881Speter                                       server_baton_t *b,
444251881Speter                                       apr_pool_t *pool)
445251881Speter{
446289180Speter  repository_t *repository = b->repository;
447289180Speter  client_info_t *client_info = b->client_info;
448289180Speter
449251881Speter  /* If authz cannot be performed, grant access.  This is NOT the same
450251881Speter     as the default policy when authz is performed on a path with no
451251881Speter     rules.  In the latter case, the default is to deny access, and is
452251881Speter     set by svn_repos_authz_check_access. */
453289180Speter  if (!repository->authzdb)
454251881Speter    {
455251881Speter      *allowed = TRUE;
456251881Speter      return SVN_NO_ERROR;
457251881Speter    }
458251881Speter
459251881Speter  /* If the authz request is for the empty path (ie. ""), replace it
460251881Speter     with the root path.  This happens because of stripping done at
461251881Speter     various levels in svnserve that remove the leading / on an
462251881Speter     absolute path. Passing such a malformed path to the authz
463251881Speter     routines throws them into an infinite loop and makes them miss
464251881Speter     ACLs. */
465362181Sdim  if (path && *path != '/')
466251881Speter    path = svn_fspath__canonicalize(path, pool);
467251881Speter
468251881Speter  /* If we have a username, and we've not yet used it + any username
469251881Speter     case normalization that might be requested to determine "the
470251881Speter     username we used for authz purposes", do so now. */
471289180Speter  if (client_info->user && (! client_info->authz_user))
472251881Speter    {
473289180Speter      char *authz_user = apr_pstrdup(b->pool, client_info->user);
474289180Speter      if (repository->username_case == CASE_FORCE_UPPER)
475251881Speter        convert_case(authz_user, TRUE);
476289180Speter      else if (repository->username_case == CASE_FORCE_LOWER)
477251881Speter        convert_case(authz_user, FALSE);
478289180Speter
479289180Speter      client_info->authz_user = authz_user;
480251881Speter    }
481251881Speter
482289180Speter  SVN_ERR(svn_repos_authz_check_access(repository->authzdb,
483289180Speter                                       repository->authz_repos_name,
484289180Speter                                       path, client_info->authz_user,
485289180Speter                                       required, allowed, pool));
486251881Speter  if (!*allowed)
487289180Speter    SVN_ERR(log_authz_denied(path, required, b, pool));
488251881Speter
489251881Speter  return SVN_NO_ERROR;
490251881Speter}
491251881Speter
492251881Speter/* Set *ALLOWED to TRUE if PATH is readable by the user described in
493251881Speter * BATON.  Use POOL for temporary allocations only.  ROOT is not used.
494251881Speter * Implements the svn_repos_authz_func_t interface.
495251881Speter */
496251881Speterstatic svn_error_t *authz_check_access_cb(svn_boolean_t *allowed,
497251881Speter                                          svn_fs_root_t *root,
498251881Speter                                          const char *path,
499251881Speter                                          void *baton,
500251881Speter                                          apr_pool_t *pool)
501251881Speter{
502251881Speter  authz_baton_t *sb = baton;
503251881Speter
504251881Speter  return authz_check_access(allowed, path, svn_authz_read,
505289180Speter                            sb->server, pool);
506251881Speter}
507251881Speter
508251881Speter/* If authz is enabled in the specified BATON, return a read authorization
509251881Speter   function. Otherwise, return NULL. */
510251881Speterstatic svn_repos_authz_func_t authz_check_access_cb_func(server_baton_t *baton)
511251881Speter{
512289180Speter  if (baton->repository->authzdb)
513251881Speter     return authz_check_access_cb;
514251881Speter  return NULL;
515251881Speter}
516251881Speter
517251881Speter/* Set *ALLOWED to TRUE if the REQUIRED access to PATH is granted,
518251881Speter * according to the state in BATON.  Use POOL for temporary
519251881Speter * allocations only.  ROOT is not used.  Implements the
520251881Speter * svn_repos_authz_callback_t interface.
521251881Speter */
522251881Speterstatic svn_error_t *authz_commit_cb(svn_repos_authz_access_t required,
523251881Speter                                    svn_boolean_t *allowed,
524251881Speter                                    svn_fs_root_t *root,
525251881Speter                                    const char *path,
526251881Speter                                    void *baton,
527251881Speter                                    apr_pool_t *pool)
528251881Speter{
529251881Speter  authz_baton_t *sb = baton;
530251881Speter
531289180Speter  return authz_check_access(allowed, path, required, sb->server, pool);
532251881Speter}
533251881Speter
534289180Speter/* Return the access level specified for OPTION in CFG.  If no such
535289180Speter * setting exists, use DEF.  If READ_ONLY is set, unconditionally disable
536289180Speter * write access.
537289180Speter */
538289180Speterstatic enum access_type
539289180Speterget_access(svn_config_t *cfg,
540289180Speter           const char *option,
541289180Speter           const char *def,
542289180Speter           svn_boolean_t read_only)
543251881Speter{
544251881Speter  enum access_type result;
545289180Speter  const char *val;
546251881Speter
547289180Speter  svn_config_get(cfg, &val, SVN_CONFIG_SECTION_GENERAL, option, def);
548251881Speter  result = (strcmp(val, "write") == 0 ? WRITE_ACCESS :
549251881Speter            strcmp(val, "read") == 0 ? READ_ACCESS : NO_ACCESS);
550289180Speter
551289180Speter  return result == WRITE_ACCESS && read_only ? READ_ACCESS : result;
552251881Speter}
553251881Speter
554289180Speter/* Set the *_ACCESS members in REPOSITORY according to the settings in
555289180Speter * CFG.  If READ_ONLY is set, unconditionally disable write access.
556289180Speter */
557289180Speterstatic void
558289180Speterset_access(repository_t *repository,
559289180Speter           svn_config_t *cfg,
560289180Speter           svn_boolean_t read_only)
561251881Speter{
562289180Speter  repository->auth_access = get_access(cfg, SVN_CONFIG_OPTION_AUTH_ACCESS,
563289180Speter                                       "write", read_only);
564289180Speter  repository->anon_access = get_access(cfg, SVN_CONFIG_OPTION_ANON_ACCESS,
565289180Speter                                       "read", read_only);
566251881Speter}
567251881Speter
568289180Speter/* Return the access level for the user in B.
569289180Speter */
570289180Speterstatic enum access_type
571289180Spetercurrent_access(server_baton_t *b)
572289180Speter{
573289180Speter  return b->client_info->user ? b->repository->auth_access
574289180Speter                              : b->repository->anon_access;
575289180Speter}
576289180Speter
577251881Speter/* Send authentication mechs for ACCESS_TYPE to the client.  If NEEDS_USERNAME
578251881Speter   is true, don't send anonymous mech even if that would give the desired
579251881Speter   access. */
580251881Speterstatic svn_error_t *send_mechs(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
581251881Speter                               server_baton_t *b, enum access_type required,
582251881Speter                               svn_boolean_t needs_username)
583251881Speter{
584289180Speter  if (!needs_username && b->repository->anon_access >= required)
585251881Speter    SVN_ERR(svn_ra_svn__write_word(conn, pool, "ANONYMOUS"));
586289180Speter  if (b->client_info->tunnel_user && b->repository->auth_access >= required)
587251881Speter    SVN_ERR(svn_ra_svn__write_word(conn, pool, "EXTERNAL"));
588289180Speter  if (b->repository->pwdb && b->repository->auth_access >= required)
589251881Speter    SVN_ERR(svn_ra_svn__write_word(conn, pool, "CRAM-MD5"));
590251881Speter  return SVN_NO_ERROR;
591251881Speter}
592251881Speter
593251881Speter/* Context for cleanup handler. */
594251881Speterstruct cleanup_fs_access_baton
595251881Speter{
596251881Speter  svn_fs_t *fs;
597251881Speter  apr_pool_t *pool;
598251881Speter};
599251881Speter
600251881Speter/* Pool cleanup handler.  Make sure fs's access_t points to NULL when
601251881Speter   the command pool is destroyed. */
602251881Speterstatic apr_status_t cleanup_fs_access(void *data)
603251881Speter{
604251881Speter  svn_error_t *serr;
605251881Speter  struct cleanup_fs_access_baton *baton = data;
606251881Speter
607251881Speter  serr = svn_fs_set_access(baton->fs, NULL);
608251881Speter  if (serr)
609251881Speter    {
610251881Speter      apr_status_t apr_err = serr->apr_err;
611251881Speter      svn_error_clear(serr);
612251881Speter      return apr_err;
613251881Speter    }
614251881Speter
615251881Speter  return APR_SUCCESS;
616251881Speter}
617251881Speter
618251881Speter
619251881Speter/* Create an svn_fs_access_t in POOL for USER and associate it with
620251881Speter   B's filesystem.  Also, register a cleanup handler with POOL which
621251881Speter   de-associates the svn_fs_access_t from B's filesystem. */
622251881Speterstatic svn_error_t *
623251881Spetercreate_fs_access(server_baton_t *b, apr_pool_t *pool)
624251881Speter{
625251881Speter  svn_fs_access_t *fs_access;
626251881Speter  struct cleanup_fs_access_baton *cleanup_baton;
627251881Speter
628289180Speter  if (!b->client_info->user)
629251881Speter    return SVN_NO_ERROR;
630251881Speter
631289180Speter  SVN_ERR(svn_fs_create_access(&fs_access, b->client_info->user, pool));
632289180Speter  SVN_ERR(svn_fs_set_access(b->repository->fs, fs_access));
633251881Speter
634251881Speter  cleanup_baton = apr_pcalloc(pool, sizeof(*cleanup_baton));
635251881Speter  cleanup_baton->pool = pool;
636289180Speter  cleanup_baton->fs = b->repository->fs;
637251881Speter  apr_pool_cleanup_register(pool, cleanup_baton, cleanup_fs_access,
638251881Speter                            apr_pool_cleanup_null);
639251881Speter
640251881Speter  return SVN_NO_ERROR;
641251881Speter}
642251881Speter
643251881Speter/* Authenticate, once the client has chosen a mechanism and possibly
644251881Speter * sent an initial mechanism token.  On success, set *success to true
645251881Speter * and b->user to the authenticated username (or NULL for anonymous).
646251881Speter * On authentication failure, report failure to the client and set
647251881Speter * *success to FALSE.  On communications failure, return an error.
648251881Speter * If NEEDS_USERNAME is TRUE, don't allow anonymous authentication. */
649362181Sdimstatic svn_error_t *auth(svn_boolean_t *success,
650362181Sdim                         svn_ra_svn_conn_t *conn,
651251881Speter                         const char *mech, const char *mecharg,
652251881Speter                         server_baton_t *b, enum access_type required,
653251881Speter                         svn_boolean_t needs_username,
654362181Sdim                         apr_pool_t *scratch_pool)
655251881Speter{
656251881Speter  const char *user;
657251881Speter  *success = FALSE;
658251881Speter
659289180Speter  if (b->repository->auth_access >= required
660289180Speter      && b->client_info->tunnel_user && strcmp(mech, "EXTERNAL") == 0)
661251881Speter    {
662289180Speter      if (*mecharg && strcmp(mecharg, b->client_info->tunnel_user) != 0)
663362181Sdim        return svn_ra_svn__write_tuple(conn, scratch_pool, "w(c)", "failure",
664251881Speter                                       "Requested username does not match");
665289180Speter      b->client_info->user = b->client_info->tunnel_user;
666362181Sdim      SVN_ERR(svn_ra_svn__write_tuple(conn, scratch_pool, "w()", "success"));
667251881Speter      *success = TRUE;
668251881Speter      return SVN_NO_ERROR;
669251881Speter    }
670251881Speter
671289180Speter  if (b->repository->anon_access >= required
672251881Speter      && strcmp(mech, "ANONYMOUS") == 0 && ! needs_username)
673251881Speter    {
674362181Sdim      SVN_ERR(svn_ra_svn__write_tuple(conn, scratch_pool, "w()", "success"));
675251881Speter      *success = TRUE;
676251881Speter      return SVN_NO_ERROR;
677251881Speter    }
678251881Speter
679289180Speter  if (b->repository->auth_access >= required
680289180Speter      && b->repository->pwdb && strcmp(mech, "CRAM-MD5") == 0)
681251881Speter    {
682362181Sdim      SVN_ERR(svn_ra_svn_cram_server(conn, scratch_pool, b->repository->pwdb,
683289180Speter                                     &user, success));
684289180Speter      b->client_info->user = apr_pstrdup(b->pool, user);
685251881Speter      return SVN_NO_ERROR;
686251881Speter    }
687251881Speter
688362181Sdim  return svn_ra_svn__write_tuple(conn, scratch_pool, "w(c)", "failure",
689251881Speter                                "Must authenticate with listed mechanism");
690251881Speter}
691251881Speter
692251881Speter/* Perform an authentication request using the built-in SASL implementation. */
693251881Speterstatic svn_error_t *
694251881Speterinternal_auth_request(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
695251881Speter                      server_baton_t *b, enum access_type required,
696251881Speter                      svn_boolean_t needs_username)
697251881Speter{
698251881Speter  svn_boolean_t success;
699251881Speter  const char *mech, *mecharg;
700362181Sdim  apr_pool_t *iterpool;
701251881Speter
702251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success"));
703251881Speter  SVN_ERR(send_mechs(conn, pool, b, required, needs_username));
704289180Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)c)", b->repository->realm));
705362181Sdim
706362181Sdim  iterpool = svn_pool_create(pool);
707251881Speter  do
708251881Speter    {
709362181Sdim      svn_pool_clear(iterpool);
710362181Sdim
711251881Speter      SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "w(?c)", &mech, &mecharg));
712251881Speter      if (!*mech)
713251881Speter        break;
714362181Sdim      SVN_ERR(auth(&success, conn, mech, mecharg, b, required,
715362181Sdim                   needs_username, iterpool));
716251881Speter    }
717251881Speter  while (!success);
718362181Sdim  svn_pool_destroy(iterpool);
719362181Sdim
720251881Speter  return SVN_NO_ERROR;
721251881Speter}
722251881Speter
723251881Speter/* Perform an authentication request in order to get an access level of
724251881Speter * REQUIRED or higher.  Since the client may escape the authentication
725251881Speter * exchange, the caller should check current_access(b) to see if
726251881Speter * authentication succeeded. */
727251881Speterstatic svn_error_t *auth_request(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
728251881Speter                                 server_baton_t *b, enum access_type required,
729251881Speter                                 svn_boolean_t needs_username)
730251881Speter{
731251881Speter#ifdef SVN_HAVE_SASL
732289180Speter  if (b->repository->use_sasl)
733251881Speter    return cyrus_auth_request(conn, pool, b, required, needs_username);
734251881Speter#endif
735251881Speter
736251881Speter  return internal_auth_request(conn, pool, b, required, needs_username);
737251881Speter}
738251881Speter
739251881Speter/* Send a trivial auth notification on CONN which lists no mechanisms,
740251881Speter * indicating that authentication is unnecessary.  Usually called in
741251881Speter * response to invocation of a svnserve command.
742251881Speter */
743251881Speterstatic svn_error_t *trivial_auth_request(svn_ra_svn_conn_t *conn,
744251881Speter                                         apr_pool_t *pool, server_baton_t *b)
745251881Speter{
746251881Speter  return svn_ra_svn__write_cmd_response(conn, pool, "()c", "");
747251881Speter}
748251881Speter
749251881Speter/* Ensure that the client has the REQUIRED access by checking the
750251881Speter * access directives (both blanket and per-directory) in BATON.  If
751251881Speter * PATH is NULL, then only the blanket access configuration will
752251881Speter * impact the result.
753251881Speter *
754251881Speter * If NEEDS_USERNAME is TRUE, then a lookup is only successful if the
755251881Speter * user described in BATON is authenticated and, well, has a username
756251881Speter * assigned to him.
757251881Speter *
758251881Speter * Use POOL for temporary allocations only.
759251881Speter */
760251881Speterstatic svn_boolean_t lookup_access(apr_pool_t *pool,
761251881Speter                                   server_baton_t *baton,
762251881Speter                                   svn_repos_authz_access_t required,
763251881Speter                                   const char *path,
764251881Speter                                   svn_boolean_t needs_username)
765251881Speter{
766251881Speter  enum access_type req = (required & svn_authz_write) ?
767251881Speter    WRITE_ACCESS : READ_ACCESS;
768251881Speter  svn_boolean_t authorized;
769251881Speter  svn_error_t *err;
770251881Speter
771251881Speter  /* Get authz's opinion on the access. */
772289180Speter  err = authz_check_access(&authorized, path, required, baton, pool);
773251881Speter
774251881Speter  /* If an error made lookup fail, deny access. */
775251881Speter  if (err)
776251881Speter    {
777289180Speter      log_error(err, baton);
778251881Speter      svn_error_clear(err);
779251881Speter      return FALSE;
780251881Speter    }
781251881Speter
782251881Speter  /* If the required access is blanket-granted AND granted by authz
783251881Speter     AND we already have a username if one is required, then the
784251881Speter     lookup has succeeded. */
785251881Speter  if (current_access(baton) >= req
786251881Speter      && authorized
787289180Speter      && (! needs_username || baton->client_info->user))
788251881Speter    return TRUE;
789251881Speter
790251881Speter  return FALSE;
791251881Speter}
792251881Speter
793251881Speter/* Check that the client has the REQUIRED access by consulting the
794251881Speter * authentication and authorization states stored in BATON.  If the
795251881Speter * client does not have the required access credentials, attempt to
796251881Speter * authenticate the client to get that access, using CONN for
797251881Speter * communication.
798251881Speter *
799251881Speter * This function is supposed to be called to handle the authentication
800251881Speter * half of a standard svn protocol reply.  If an error is returned, it
801251881Speter * probably means that the server can terminate the client connection
802251881Speter * with an apologetic error, as it implies an authentication failure.
803251881Speter *
804251881Speter * PATH and NEEDS_USERNAME are passed along to lookup_access, their
805251881Speter * behaviour is documented there.
806251881Speter */
807251881Speterstatic svn_error_t *must_have_access(svn_ra_svn_conn_t *conn,
808251881Speter                                     apr_pool_t *pool,
809251881Speter                                     server_baton_t *b,
810251881Speter                                     svn_repos_authz_access_t required,
811251881Speter                                     const char *path,
812251881Speter                                     svn_boolean_t needs_username)
813251881Speter{
814251881Speter  enum access_type req = (required & svn_authz_write) ?
815251881Speter    WRITE_ACCESS : READ_ACCESS;
816251881Speter
817251881Speter  /* See whether the user already has the required access.  If so,
818251881Speter     nothing needs to be done.  Create the FS access and send a
819251881Speter     trivial auth request. */
820289180Speter  if (lookup_access(pool, b, required, path, needs_username))
821251881Speter    {
822251881Speter      SVN_ERR(create_fs_access(b, pool));
823251881Speter      return trivial_auth_request(conn, pool, b);
824251881Speter    }
825251881Speter
826251881Speter  /* If the required blanket access can be obtained by authenticating,
827251881Speter     try that.  Unfortunately, we can't tell until after
828251881Speter     authentication whether authz will work or not.  We force
829251881Speter     requiring a username because we need one to be able to check
830251881Speter     authz configuration again with a different user credentials than
831251881Speter     the first time round. */
832289180Speter  if (b->client_info->user == NULL
833289180Speter      && b->repository->auth_access >= req
834289180Speter      && (b->client_info->tunnel_user || b->repository->pwdb
835289180Speter          || b->repository->use_sasl))
836251881Speter    SVN_ERR(auth_request(conn, pool, b, req, TRUE));
837251881Speter
838251881Speter  /* Now that an authentication has been done get the new take of
839251881Speter     authz on the request. */
840289180Speter  if (! lookup_access(pool, b, required, path, needs_username))
841251881Speter    return svn_error_create(SVN_ERR_RA_SVN_CMD_ERR,
842251881Speter                            error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED,
843289180Speter                                                 NULL, NULL, b),
844251881Speter                            NULL);
845251881Speter
846251881Speter  /* Else, access is granted, and there is much rejoicing. */
847251881Speter  SVN_ERR(create_fs_access(b, pool));
848251881Speter
849251881Speter  return SVN_NO_ERROR;
850251881Speter}
851251881Speter
852251881Speter/* --- REPORTER COMMAND SET --- */
853251881Speter
854251881Speter/* To allow for pipelining, reporter commands have no reponses.  If we
855251881Speter * get an error, we ignore all subsequent reporter commands and return
856251881Speter * the error finish_report, to be handled by the calling command.
857251881Speter */
858251881Speter
859251881Speterstatic svn_error_t *set_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
860362181Sdim                             svn_ra_svn__list_t *params, void *baton)
861251881Speter{
862251881Speter  report_driver_baton_t *b = baton;
863362181Sdim  const char *path, *lock_token, *depth_word, *canonical_relpath;
864251881Speter  svn_revnum_t rev;
865251881Speter  /* Default to infinity, for old clients that don't send depth. */
866251881Speter  svn_depth_t depth = svn_depth_infinity;
867251881Speter  svn_boolean_t start_empty;
868251881Speter
869362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "crb?(?c)?w",
870362181Sdim                                  &path, &rev, &start_empty, &lock_token,
871362181Sdim                                  &depth_word));
872251881Speter  if (depth_word)
873251881Speter    depth = svn_depth_from_word(depth_word);
874362181Sdim  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_relpath, NULL, path,
875362181Sdim                                        pool, pool));
876362181Sdim  path = canonical_relpath;
877251881Speter  if (b->from_rev && strcmp(path, "") == 0)
878251881Speter    *b->from_rev = rev;
879251881Speter  if (!b->err)
880251881Speter    b->err = svn_repos_set_path3(b->report_baton, path, rev, depth,
881251881Speter                                 start_empty, lock_token, pool);
882251881Speter  b->entry_counter++;
883251881Speter  if (!start_empty)
884251881Speter    b->only_empty_entries = FALSE;
885251881Speter  return SVN_NO_ERROR;
886251881Speter}
887251881Speter
888251881Speterstatic svn_error_t *delete_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
889362181Sdim                                svn_ra_svn__list_t *params, void *baton)
890251881Speter{
891251881Speter  report_driver_baton_t *b = baton;
892362181Sdim  const char *path, *canonical_relpath;
893251881Speter
894362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "c", &path));
895362181Sdim  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_relpath, NULL, path,
896362181Sdim                                        pool, pool));
897362181Sdim  path = canonical_relpath;
898251881Speter  if (!b->err)
899251881Speter    b->err = svn_repos_delete_path(b->report_baton, path, pool);
900251881Speter  return SVN_NO_ERROR;
901251881Speter}
902251881Speter
903251881Speterstatic svn_error_t *link_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
904362181Sdim                              svn_ra_svn__list_t *params, void *baton)
905251881Speter{
906251881Speter  report_driver_baton_t *b = baton;
907362181Sdim  const char *path, *url, *lock_token, *fs_path, *depth_word, *canonical_url;
908362181Sdim  const char *canonical_path;
909251881Speter  svn_revnum_t rev;
910251881Speter  svn_boolean_t start_empty;
911251881Speter  /* Default to infinity, for old clients that don't send depth. */
912251881Speter  svn_depth_t depth = svn_depth_infinity;
913251881Speter
914362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "ccrb?(?c)?w",
915251881Speter                                 &path, &url, &rev, &start_empty,
916251881Speter                                 &lock_token, &depth_word));
917251881Speter
918251881Speter  /* ### WHAT?!  The link path is an absolute URL?!  Didn't see that
919251881Speter     coming...   -- cmpilato  */
920362181Sdim
921362181Sdim  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
922362181Sdim                                        pool, pool));
923362181Sdim  path = canonical_path;
924362181Sdim  SVN_ERR(svn_uri_canonicalize_safe(&canonical_url, NULL, url, pool, pool));
925362181Sdim  url = canonical_url;
926251881Speter  if (depth_word)
927251881Speter    depth = svn_depth_from_word(depth_word);
928251881Speter  if (!b->err)
929251881Speter    b->err = get_fs_path(svn_path_uri_decode(b->repos_url, pool),
930251881Speter                         svn_path_uri_decode(url, pool),
931251881Speter                         &fs_path);
932251881Speter  if (!b->err)
933251881Speter    b->err = svn_repos_link_path3(b->report_baton, path, fs_path, rev,
934251881Speter                                  depth, start_empty, lock_token, pool);
935251881Speter  b->entry_counter++;
936251881Speter  return SVN_NO_ERROR;
937251881Speter}
938251881Speter
939251881Speterstatic svn_error_t *finish_report(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
940362181Sdim                                  svn_ra_svn__list_t *params, void *baton)
941251881Speter{
942251881Speter  report_driver_baton_t *b = baton;
943251881Speter
944251881Speter  /* No arguments to parse. */
945251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b->sb));
946251881Speter  if (!b->err)
947251881Speter    b->err = svn_repos_finish_report(b->report_baton, pool);
948251881Speter  return SVN_NO_ERROR;
949251881Speter}
950251881Speter
951362181Sdimstatic svn_error_t *
952362181Sdimabort_report(svn_ra_svn_conn_t *conn,
953362181Sdim             apr_pool_t *pool,
954362181Sdim             svn_ra_svn__list_t *params,
955362181Sdim             void *baton)
956251881Speter{
957251881Speter  report_driver_baton_t *b = baton;
958251881Speter
959251881Speter  /* No arguments to parse. */
960251881Speter  svn_error_clear(svn_repos_abort_report(b->report_baton, pool));
961251881Speter  return SVN_NO_ERROR;
962251881Speter}
963251881Speter
964362181Sdimstatic const svn_ra_svn__cmd_entry_t report_commands[] = {
965251881Speter  { "set-path",      set_path },
966251881Speter  { "delete-path",   delete_path },
967251881Speter  { "link-path",     link_path },
968362181Sdim  { "finish-report", finish_report, NULL, TRUE },
969362181Sdim  { "abort-report",  abort_report,  NULL, TRUE },
970251881Speter  { NULL }
971251881Speter};
972251881Speter
973251881Speter/* Accept a report from the client, drive the network editor with the
974251881Speter * result, and then write an empty command response.  If there is a
975251881Speter * non-protocol failure, accept_report will abort the edit and return
976251881Speter * a command error to be reported by handle_commands().
977251881Speter *
978251881Speter * If only_empty_entry is not NULL and the report contains only one
979251881Speter * item, and that item is empty, set *only_empty_entry to TRUE, else
980251881Speter * set it to FALSE.
981251881Speter *
982251881Speter * If from_rev is not NULL, set *from_rev to the revision number from
983251881Speter * the set-path on ""; if somehow set-path "" never happens, set
984251881Speter * *from_rev to SVN_INVALID_REVNUM.
985251881Speter */
986251881Speterstatic svn_error_t *accept_report(svn_boolean_t *only_empty_entry,
987251881Speter                                  svn_revnum_t *from_rev,
988251881Speter                                  svn_ra_svn_conn_t *conn, apr_pool_t *pool,
989251881Speter                                  server_baton_t *b, svn_revnum_t rev,
990251881Speter                                  const char *target, const char *tgt_path,
991251881Speter                                  svn_boolean_t text_deltas,
992251881Speter                                  svn_depth_t depth,
993251881Speter                                  svn_boolean_t send_copyfrom_args,
994251881Speter                                  svn_boolean_t ignore_ancestry)
995251881Speter{
996251881Speter  const svn_delta_editor_t *editor;
997251881Speter  void *edit_baton, *report_baton;
998251881Speter  report_driver_baton_t rb;
999251881Speter  svn_error_t *err;
1000251881Speter  authz_baton_t ab;
1001251881Speter
1002251881Speter  ab.server = b;
1003251881Speter  ab.conn = conn;
1004251881Speter
1005251881Speter  /* Make an svn_repos report baton.  Tell it to drive the network editor
1006251881Speter   * when the report is complete. */
1007251881Speter  svn_ra_svn_get_editor(&editor, &edit_baton, conn, pool, NULL, NULL);
1008289180Speter  SVN_CMD_ERR(svn_repos_begin_report3(&report_baton, rev,
1009289180Speter                                      b->repository->repos,
1010289180Speter                                      b->repository->fs_path->data, target,
1011289180Speter                                      tgt_path, text_deltas, depth,
1012289180Speter                                      ignore_ancestry, send_copyfrom_args,
1013251881Speter                                      editor, edit_baton,
1014251881Speter                                      authz_check_access_cb_func(b),
1015251881Speter                                      &ab, svn_ra_svn_zero_copy_limit(conn),
1016251881Speter                                      pool));
1017251881Speter
1018251881Speter  rb.sb = b;
1019289180Speter  rb.repos_url = svn_path_uri_decode(b->repository->repos_url, pool);
1020251881Speter  rb.report_baton = report_baton;
1021251881Speter  rb.err = NULL;
1022251881Speter  rb.entry_counter = 0;
1023251881Speter  rb.only_empty_entries = TRUE;
1024251881Speter  rb.from_rev = from_rev;
1025251881Speter  if (from_rev)
1026251881Speter    *from_rev = SVN_INVALID_REVNUM;
1027251881Speter  err = svn_ra_svn__handle_commands2(conn, pool, report_commands, &rb, TRUE);
1028251881Speter  if (err)
1029251881Speter    {
1030251881Speter      /* Network or protocol error while handling commands. */
1031251881Speter      svn_error_clear(rb.err);
1032251881Speter      return err;
1033251881Speter    }
1034251881Speter  else if (rb.err)
1035251881Speter    {
1036251881Speter      /* Some failure during the reporting or editing operations. */
1037251881Speter      SVN_CMD_ERR(rb.err);
1038251881Speter    }
1039251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
1040251881Speter
1041251881Speter  if (only_empty_entry)
1042251881Speter    *only_empty_entry = rb.entry_counter == 1 && rb.only_empty_entries;
1043251881Speter
1044251881Speter  return SVN_NO_ERROR;
1045251881Speter}
1046251881Speter
1047251881Speter/* --- MAIN COMMAND SET --- */
1048251881Speter
1049251881Speter/* Write out a list of property diffs.  PROPDIFFS is an array of svn_prop_t
1050251881Speter * values. */
1051251881Speterstatic svn_error_t *write_prop_diffs(svn_ra_svn_conn_t *conn,
1052251881Speter                                     apr_pool_t *pool,
1053251881Speter                                     const apr_array_header_t *propdiffs)
1054251881Speter{
1055251881Speter  int i;
1056251881Speter
1057251881Speter  for (i = 0; i < propdiffs->nelts; ++i)
1058251881Speter    {
1059251881Speter      const svn_prop_t *prop = &APR_ARRAY_IDX(propdiffs, i, svn_prop_t);
1060251881Speter
1061251881Speter      SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "c(?s)",
1062251881Speter                                      prop->name, prop->value));
1063251881Speter    }
1064251881Speter
1065251881Speter  return SVN_NO_ERROR;
1066251881Speter}
1067251881Speter
1068251881Speter/* Write out a lock to the client. */
1069251881Speterstatic svn_error_t *write_lock(svn_ra_svn_conn_t *conn,
1070251881Speter                               apr_pool_t *pool,
1071289180Speter                               const svn_lock_t *lock)
1072251881Speter{
1073251881Speter  const char *cdate, *edate;
1074251881Speter
1075251881Speter  cdate = svn_time_to_cstring(lock->creation_date, pool);
1076251881Speter  edate = lock->expiration_date
1077251881Speter    ? svn_time_to_cstring(lock->expiration_date, pool) : NULL;
1078251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "ccc(?c)c(?c)", lock->path,
1079251881Speter                                  lock->token, lock->owner, lock->comment,
1080251881Speter                                  cdate, edate));
1081251881Speter
1082251881Speter  return SVN_NO_ERROR;
1083251881Speter}
1084251881Speter
1085251881Speter/* ### This really belongs in libsvn_repos. */
1086251881Speter/* Get the explicit properties and/or inherited properties for a PATH in
1087251881Speter   ROOT, with hardcoded committed-info values. */
1088251881Speterstatic svn_error_t *
1089251881Speterget_props(apr_hash_t **props,
1090251881Speter          apr_array_header_t **iprops,
1091251881Speter          authz_baton_t *b,
1092251881Speter          svn_fs_root_t *root,
1093251881Speter          const char *path,
1094251881Speter          apr_pool_t *pool)
1095251881Speter{
1096251881Speter  /* Get the explicit properties. */
1097251881Speter  if (props)
1098251881Speter    {
1099251881Speter      svn_string_t *str;
1100251881Speter      svn_revnum_t crev;
1101251881Speter      const char *cdate, *cauthor, *uuid;
1102251881Speter
1103251881Speter      SVN_ERR(svn_fs_node_proplist(props, root, path, pool));
1104251881Speter
1105251881Speter      /* Hardcode the values for the committed revision, date, and author. */
1106251881Speter      SVN_ERR(svn_repos_get_committed_info(&crev, &cdate, &cauthor, root,
1107251881Speter                                           path, pool));
1108289180Speter      str = svn_string_createf(pool, "%ld", crev);
1109251881Speter      svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_REV, str);
1110251881Speter      str = (cdate) ? svn_string_create(cdate, pool) : NULL;
1111251881Speter      svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_DATE, str);
1112251881Speter      str = (cauthor) ? svn_string_create(cauthor, pool) : NULL;
1113251881Speter      svn_hash_sets(*props, SVN_PROP_ENTRY_LAST_AUTHOR, str);
1114251881Speter
1115251881Speter      /* Hardcode the values for the UUID. */
1116251881Speter      SVN_ERR(svn_fs_get_uuid(svn_fs_root_fs(root), &uuid, pool));
1117251881Speter      str = (uuid) ? svn_string_create(uuid, pool) : NULL;
1118251881Speter      svn_hash_sets(*props, SVN_PROP_ENTRY_UUID, str);
1119251881Speter    }
1120251881Speter
1121251881Speter  /* Get any inherited properties the user is authorized to. */
1122251881Speter  if (iprops)
1123251881Speter    {
1124251881Speter      SVN_ERR(svn_repos_fs_get_inherited_props(
1125251881Speter                iprops, root, path, NULL,
1126251881Speter                authz_check_access_cb_func(b->server),
1127251881Speter                b, pool, pool));
1128251881Speter    }
1129251881Speter
1130251881Speter  return SVN_NO_ERROR;
1131251881Speter}
1132251881Speter
1133251881Speter/* Set BATON->FS_PATH for the repository URL found in PARAMS. */
1134362181Sdimstatic svn_error_t *
1135362181Sdimreparent(svn_ra_svn_conn_t *conn,
1136362181Sdim         apr_pool_t *pool,
1137362181Sdim         svn_ra_svn__list_t *params,
1138362181Sdim         void *baton)
1139251881Speter{
1140251881Speter  server_baton_t *b = baton;
1141362181Sdim  const char *url, *canonical_url;
1142251881Speter  const char *fs_path;
1143251881Speter
1144362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "c", &url));
1145362181Sdim  SVN_ERR(svn_uri_canonicalize_safe(&canonical_url, NULL, url, pool, pool));
1146362181Sdim  url = canonical_url;
1147251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
1148289180Speter  SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repository->repos_url, pool),
1149251881Speter                          svn_path_uri_decode(url, pool),
1150251881Speter                          &fs_path));
1151251881Speter  SVN_ERR(log_command(b, conn, pool, "%s", svn_log__reparent(fs_path, pool)));
1152289180Speter  svn_stringbuf_set(b->repository->fs_path, fs_path);
1153251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
1154251881Speter  return SVN_NO_ERROR;
1155251881Speter}
1156251881Speter
1157362181Sdimstatic svn_error_t *
1158362181Sdimget_latest_rev(svn_ra_svn_conn_t *conn,
1159362181Sdim               apr_pool_t *pool,
1160362181Sdim               svn_ra_svn__list_t *params,
1161362181Sdim               void *baton)
1162251881Speter{
1163251881Speter  server_baton_t *b = baton;
1164251881Speter  svn_revnum_t rev;
1165251881Speter
1166251881Speter  SVN_ERR(log_command(b, conn, pool, "get-latest-rev"));
1167251881Speter
1168251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
1169289180Speter  SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
1170251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", rev));
1171251881Speter  return SVN_NO_ERROR;
1172251881Speter}
1173251881Speter
1174362181Sdimstatic svn_error_t *
1175362181Sdimget_dated_rev(svn_ra_svn_conn_t *conn,
1176362181Sdim              apr_pool_t *pool,
1177362181Sdim              svn_ra_svn__list_t *params,
1178362181Sdim              void *baton)
1179251881Speter{
1180251881Speter  server_baton_t *b = baton;
1181251881Speter  svn_revnum_t rev;
1182251881Speter  apr_time_t tm;
1183251881Speter  const char *timestr;
1184251881Speter
1185362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "c", &timestr));
1186251881Speter  SVN_ERR(log_command(b, conn, pool, "get-dated-rev %s", timestr));
1187251881Speter
1188251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
1189251881Speter  SVN_CMD_ERR(svn_time_from_cstring(&tm, timestr, pool));
1190289180Speter  SVN_CMD_ERR(svn_repos_dated_revision(&rev, b->repository->repos, tm, pool));
1191251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", rev));
1192251881Speter  return SVN_NO_ERROR;
1193251881Speter}
1194251881Speter
1195251881Speter/* Common logic for change_rev_prop() and change_rev_prop2(). */
1196251881Speterstatic svn_error_t *do_change_rev_prop(svn_ra_svn_conn_t *conn,
1197251881Speter                                       server_baton_t *b,
1198251881Speter                                       svn_revnum_t rev,
1199251881Speter                                       const char *name,
1200251881Speter                                       const svn_string_t *const *old_value_p,
1201251881Speter                                       const svn_string_t *value,
1202251881Speter                                       apr_pool_t *pool)
1203251881Speter{
1204251881Speter  authz_baton_t ab;
1205251881Speter
1206251881Speter  ab.server = b;
1207251881Speter  ab.conn = conn;
1208251881Speter
1209251881Speter  SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, FALSE));
1210251881Speter  SVN_ERR(log_command(b, conn, pool, "%s",
1211251881Speter                      svn_log__change_rev_prop(rev, name, pool)));
1212289180Speter  SVN_CMD_ERR(svn_repos_fs_change_rev_prop4(b->repository->repos, rev,
1213289180Speter                                            b->client_info->user,
1214251881Speter                                            name, old_value_p, value,
1215251881Speter                                            TRUE, TRUE,
1216251881Speter                                            authz_check_access_cb_func(b), &ab,
1217251881Speter                                            pool));
1218251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
1219251881Speter
1220251881Speter  return SVN_NO_ERROR;
1221251881Speter}
1222251881Speter
1223362181Sdimstatic svn_error_t *
1224362181Sdimchange_rev_prop2(svn_ra_svn_conn_t *conn,
1225362181Sdim                 apr_pool_t *pool,
1226362181Sdim                 svn_ra_svn__list_t *params,
1227362181Sdim                 void *baton)
1228251881Speter{
1229251881Speter  server_baton_t *b = baton;
1230251881Speter  svn_revnum_t rev;
1231251881Speter  const char *name;
1232251881Speter  svn_string_t *value;
1233251881Speter  const svn_string_t *const *old_value_p;
1234251881Speter  svn_string_t *old_value;
1235251881Speter  svn_boolean_t dont_care;
1236251881Speter
1237362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "rc(?s)(b?s)",
1238251881Speter                                  &rev, &name, &value,
1239251881Speter                                  &dont_care, &old_value));
1240251881Speter
1241251881Speter  /* Argument parsing. */
1242251881Speter  if (dont_care)
1243251881Speter    old_value_p = NULL;
1244251881Speter  else
1245251881Speter    old_value_p = (const svn_string_t *const *)&old_value;
1246251881Speter
1247251881Speter  /* Input validation. */
1248251881Speter  if (dont_care && old_value)
1249251881Speter    {
1250251881Speter      svn_error_t *err;
1251251881Speter      err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
1252251881Speter                             "'previous-value' and 'dont-care' cannot both be "
1253251881Speter                             "set in 'change-rev-prop2' request");
1254251881Speter      return log_fail_and_flush(err, b, conn, pool);
1255251881Speter    }
1256251881Speter
1257251881Speter  /* Do it. */
1258251881Speter  SVN_ERR(do_change_rev_prop(conn, b, rev, name, old_value_p, value, pool));
1259251881Speter
1260251881Speter  return SVN_NO_ERROR;
1261251881Speter}
1262251881Speter
1263362181Sdimstatic svn_error_t *
1264362181Sdimchange_rev_prop(svn_ra_svn_conn_t *conn,
1265362181Sdim                apr_pool_t *pool,
1266362181Sdim                svn_ra_svn__list_t *params,
1267362181Sdim                void *baton)
1268251881Speter{
1269251881Speter  server_baton_t *b = baton;
1270251881Speter  svn_revnum_t rev;
1271251881Speter  const char *name;
1272251881Speter  svn_string_t *value;
1273251881Speter
1274251881Speter  /* Because the revprop value was at one time mandatory, the usual
1275251881Speter     optional element pattern "(?s)" isn't used. */
1276362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "rc?s", &rev, &name, &value));
1277251881Speter
1278251881Speter  SVN_ERR(do_change_rev_prop(conn, b, rev, name, NULL, value, pool));
1279251881Speter
1280251881Speter  return SVN_NO_ERROR;
1281251881Speter}
1282251881Speter
1283362181Sdimstatic svn_error_t *
1284362181Sdimrev_proplist(svn_ra_svn_conn_t *conn,
1285362181Sdim             apr_pool_t *pool,
1286362181Sdim             svn_ra_svn__list_t *params,
1287362181Sdim             void *baton)
1288251881Speter{
1289251881Speter  server_baton_t *b = baton;
1290251881Speter  svn_revnum_t rev;
1291251881Speter  apr_hash_t *props;
1292251881Speter  authz_baton_t ab;
1293251881Speter
1294251881Speter  ab.server = b;
1295251881Speter  ab.conn = conn;
1296251881Speter
1297362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "r", &rev));
1298251881Speter  SVN_ERR(log_command(b, conn, pool, "%s", svn_log__rev_proplist(rev, pool)));
1299251881Speter
1300251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
1301289180Speter  SVN_CMD_ERR(svn_repos_fs_revision_proplist(&props, b->repository->repos,
1302289180Speter                                             rev,
1303289180Speter                                             authz_check_access_cb_func(b),
1304289180Speter                                             &ab, pool));
1305251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success"));
1306251881Speter  SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props));
1307251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));
1308251881Speter  return SVN_NO_ERROR;
1309251881Speter}
1310251881Speter
1311362181Sdimstatic svn_error_t *
1312362181Sdimrev_prop(svn_ra_svn_conn_t *conn,
1313362181Sdim         apr_pool_t *pool,
1314362181Sdim         svn_ra_svn__list_t *params,
1315362181Sdim         void *baton)
1316251881Speter{
1317251881Speter  server_baton_t *b = baton;
1318251881Speter  svn_revnum_t rev;
1319251881Speter  const char *name;
1320251881Speter  svn_string_t *value;
1321251881Speter  authz_baton_t ab;
1322251881Speter
1323251881Speter  ab.server = b;
1324251881Speter  ab.conn = conn;
1325251881Speter
1326362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "rc", &rev, &name));
1327251881Speter  SVN_ERR(log_command(b, conn, pool, "%s",
1328251881Speter                      svn_log__rev_prop(rev, name, pool)));
1329251881Speter
1330251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
1331289180Speter  SVN_CMD_ERR(svn_repos_fs_revision_prop(&value, b->repository->repos, rev,
1332289180Speter                                         name, authz_check_access_cb_func(b),
1333289180Speter                                         &ab, pool));
1334251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "(?s)", value));
1335251881Speter  return SVN_NO_ERROR;
1336251881Speter}
1337251881Speter
1338251881Speterstatic svn_error_t *commit_done(const svn_commit_info_t *commit_info,
1339251881Speter                                void *baton, apr_pool_t *pool)
1340251881Speter{
1341251881Speter  commit_callback_baton_t *ccb = baton;
1342251881Speter
1343251881Speter  *ccb->new_rev = commit_info->revision;
1344251881Speter  *ccb->date = commit_info->date
1345251881Speter    ? apr_pstrdup(ccb->pool, commit_info->date): NULL;
1346251881Speter  *ccb->author = commit_info->author
1347251881Speter    ? apr_pstrdup(ccb->pool, commit_info->author) : NULL;
1348251881Speter  *ccb->post_commit_err = commit_info->post_commit_err
1349251881Speter    ? apr_pstrdup(ccb->pool, commit_info->post_commit_err) : NULL;
1350251881Speter  return SVN_NO_ERROR;
1351251881Speter}
1352251881Speter
1353251881Speter/* Add the LOCK_TOKENS (if any) to the filesystem access context,
1354251881Speter * checking path authorizations using the state in SB as we go.
1355362181Sdim * LOCK_TOKENS is an array of svn_ra_svn__item_t structs.  Return a
1356251881Speter * client error if LOCK_TOKENS is not a list of lists.  If a lock
1357251881Speter * violates the authz configuration, return SVN_ERR_RA_NOT_AUTHORIZED
1358251881Speter * to the client.  Use POOL for temporary allocations only.
1359251881Speter */
1360362181Sdimstatic svn_error_t *
1361362181Sdimadd_lock_tokens(const svn_ra_svn__list_t *lock_tokens,
1362362181Sdim                server_baton_t *sb,
1363362181Sdim                apr_pool_t *pool)
1364251881Speter{
1365362181Sdim  const char *canonical_path;
1366251881Speter  int i;
1367251881Speter  svn_fs_access_t *fs_access;
1368251881Speter
1369289180Speter  SVN_ERR(svn_fs_get_access(&fs_access, sb->repository->fs));
1370251881Speter
1371251881Speter  /* If there is no access context, nowhere to add the tokens. */
1372251881Speter  if (! fs_access)
1373251881Speter    return SVN_NO_ERROR;
1374251881Speter
1375251881Speter  for (i = 0; i < lock_tokens->nelts; ++i)
1376251881Speter    {
1377251881Speter      const char *path, *token, *full_path;
1378362181Sdim      svn_ra_svn__item_t *path_item, *token_item;
1379362181Sdim      svn_ra_svn__item_t *item = &SVN_RA_SVN__LIST_ITEM(lock_tokens, i);
1380251881Speter      if (item->kind != SVN_RA_SVN_LIST)
1381251881Speter        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1382251881Speter                                "Lock tokens aren't a list of lists");
1383251881Speter
1384362181Sdim      path_item = &SVN_RA_SVN__LIST_ITEM(&item->u.list, 0);
1385251881Speter      if (path_item->kind != SVN_RA_SVN_STRING)
1386251881Speter        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1387251881Speter                                "Lock path isn't a string");
1388251881Speter
1389362181Sdim      token_item = &SVN_RA_SVN__LIST_ITEM(&item->u.list, 1);
1390251881Speter      if (token_item->kind != SVN_RA_SVN_STRING)
1391251881Speter        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1392251881Speter                                "Lock token isn't a string");
1393251881Speter
1394362181Sdim      path = path_item->u.string.data;
1395362181Sdim      SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
1396362181Sdim                                            pool, pool));
1397289180Speter      full_path = svn_fspath__join(sb->repository->fs_path->data,
1398362181Sdim                                   canonical_path, pool);
1399251881Speter
1400289180Speter      if (! lookup_access(pool, sb, svn_authz_write, full_path, TRUE))
1401251881Speter        return error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, NULL,
1402289180Speter                                    sb);
1403251881Speter
1404362181Sdim      token = token_item->u.string.data;
1405251881Speter      SVN_ERR(svn_fs_access_add_lock_token2(fs_access, path, token));
1406251881Speter    }
1407251881Speter
1408251881Speter  return SVN_NO_ERROR;
1409251881Speter}
1410251881Speter
1411289180Speter/* Implements svn_fs_lock_callback_t. */
1412289180Speterstatic svn_error_t *
1413289180Speterlock_cb(void *baton,
1414289180Speter        const char *path,
1415289180Speter        const svn_lock_t *lock,
1416289180Speter        svn_error_t *fs_err,
1417289180Speter        apr_pool_t *pool)
1418289180Speter{
1419289180Speter  server_baton_t *sb = baton;
1420289180Speter
1421289180Speter  log_error(fs_err, sb);
1422289180Speter
1423289180Speter  return SVN_NO_ERROR;
1424289180Speter}
1425289180Speter
1426251881Speter/* Unlock the paths with lock tokens in LOCK_TOKENS, ignoring any errors.
1427362181Sdim   LOCK_TOKENS contains svn_ra_svn__item_t elements, assumed to be lists. */
1428362181Sdimstatic svn_error_t *
1429362181Sdimunlock_paths(const svn_ra_svn__list_t *lock_tokens,
1430362181Sdim             server_baton_t *sb,
1431362181Sdim             apr_pool_t *pool)
1432251881Speter{
1433251881Speter  int i;
1434289180Speter  apr_pool_t *subpool = svn_pool_create(pool);
1435289180Speter  apr_hash_t *targets = apr_hash_make(subpool);
1436362181Sdim  const char *canonical_path;
1437289180Speter  svn_error_t *err;
1438251881Speter
1439251881Speter  for (i = 0; i < lock_tokens->nelts; ++i)
1440251881Speter    {
1441362181Sdim      svn_ra_svn__item_t *item, *path_item, *token_item;
1442251881Speter      const char *path, *token, *full_path;
1443251881Speter
1444362181Sdim      item = &SVN_RA_SVN__LIST_ITEM(lock_tokens, i);
1445362181Sdim      path_item = &SVN_RA_SVN__LIST_ITEM(&item->u.list, 0);
1446362181Sdim      token_item = &SVN_RA_SVN__LIST_ITEM(&item->u.list, 1);
1447251881Speter
1448362181Sdim      path = path_item->u.string.data;
1449362181Sdim      SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
1450362181Sdim                                            subpool, subpool));
1451289180Speter      full_path = svn_fspath__join(sb->repository->fs_path->data,
1452362181Sdim                                   canonical_path, subpool);
1453362181Sdim      token = token_item->u.string.data;
1454289180Speter      svn_hash_sets(targets, full_path, token);
1455289180Speter    }
1456251881Speter
1457251881Speter
1458289180Speter  /* The lock may have become defunct after the commit, so ignore such
1459289180Speter     errors. */
1460289180Speter  err = svn_repos_fs_unlock_many(sb->repository->repos, targets, FALSE,
1461289180Speter                                 lock_cb, sb, subpool, subpool);
1462289180Speter  log_error(err, sb);
1463289180Speter  svn_error_clear(err);
1464251881Speter
1465289180Speter  svn_pool_destroy(subpool);
1466251881Speter
1467251881Speter  return SVN_NO_ERROR;
1468251881Speter}
1469251881Speter
1470362181Sdimstatic svn_error_t *
1471362181Sdimcommit(svn_ra_svn_conn_t *conn,
1472362181Sdim       apr_pool_t *pool,
1473362181Sdim       svn_ra_svn__list_t *params,
1474362181Sdim       void *baton)
1475251881Speter{
1476251881Speter  server_baton_t *b = baton;
1477289180Speter  const char *log_msg,
1478251881Speter             *date = NULL,
1479251881Speter             *author = NULL,
1480251881Speter             *post_commit_err = NULL;
1481362181Sdim  svn_ra_svn__list_t *lock_tokens;
1482251881Speter  svn_boolean_t keep_locks;
1483362181Sdim  svn_ra_svn__list_t *revprop_list;
1484251881Speter  apr_hash_t *revprop_table;
1485251881Speter  const svn_delta_editor_t *editor;
1486251881Speter  void *edit_baton;
1487251881Speter  svn_boolean_t aborted;
1488251881Speter  commit_callback_baton_t ccb;
1489251881Speter  svn_revnum_t new_rev;
1490251881Speter  authz_baton_t ab;
1491251881Speter
1492251881Speter  ab.server = b;
1493251881Speter  ab.conn = conn;
1494251881Speter
1495251881Speter  if (params->nelts == 1)
1496251881Speter    {
1497251881Speter      /* Clients before 1.2 don't send lock-tokens, keep-locks,
1498251881Speter         and rev-props fields. */
1499362181Sdim      SVN_ERR(svn_ra_svn__parse_tuple(params, "c", &log_msg));
1500251881Speter      lock_tokens = NULL;
1501251881Speter      keep_locks = TRUE;
1502251881Speter      revprop_list = NULL;
1503251881Speter    }
1504251881Speter  else
1505251881Speter    {
1506251881Speter      /* Clients before 1.5 don't send the rev-props field. */
1507362181Sdim      SVN_ERR(svn_ra_svn__parse_tuple(params, "clb?l", &log_msg,
1508251881Speter                                      &lock_tokens, &keep_locks,
1509251881Speter                                      &revprop_list));
1510251881Speter    }
1511251881Speter
1512251881Speter  /* The handling for locks is a little problematic, because the
1513251881Speter     protocol won't let us send several auth requests once one has
1514251881Speter     succeeded.  So we request write access and a username before
1515251881Speter     adding tokens (if we have any), and subsequently fail if a lock
1516251881Speter     violates authz. */
1517251881Speter  SVN_ERR(must_have_access(conn, pool, b, svn_authz_write,
1518251881Speter                           NULL,
1519251881Speter                           (lock_tokens && lock_tokens->nelts)));
1520251881Speter
1521251881Speter  /* Authorize the lock tokens and give them to the FS if we got
1522251881Speter     any. */
1523251881Speter  if (lock_tokens && lock_tokens->nelts)
1524289180Speter    SVN_CMD_ERR(add_lock_tokens(lock_tokens, b, pool));
1525251881Speter
1526253734Speter  /* Ignore LOG_MSG, per the protocol.  See ra_svn_commit(). */
1527251881Speter  if (revprop_list)
1528251881Speter    SVN_ERR(svn_ra_svn__parse_proplist(revprop_list, pool, &revprop_table));
1529251881Speter  else
1530251881Speter    {
1531251881Speter      revprop_table = apr_hash_make(pool);
1532251881Speter      svn_hash_sets(revprop_table, SVN_PROP_REVISION_LOG,
1533251881Speter                    svn_string_create(log_msg, pool));
1534251881Speter    }
1535251881Speter
1536251881Speter  /* Get author from the baton, making sure clients can't circumvent
1537251881Speter     the authentication via the revision props. */
1538251881Speter  svn_hash_sets(revprop_table, SVN_PROP_REVISION_AUTHOR,
1539289180Speter                b->client_info->user
1540289180Speter                   ? svn_string_create(b->client_info->user, pool)
1541289180Speter                   : NULL);
1542251881Speter
1543251881Speter  ccb.pool = pool;
1544251881Speter  ccb.new_rev = &new_rev;
1545251881Speter  ccb.date = &date;
1546251881Speter  ccb.author = &author;
1547251881Speter  ccb.post_commit_err = &post_commit_err;
1548251881Speter  /* ### Note that svn_repos_get_commit_editor5 actually wants a decoded URL. */
1549251881Speter  SVN_CMD_ERR(svn_repos_get_commit_editor5
1550289180Speter              (&editor, &edit_baton, b->repository->repos, NULL,
1551289180Speter               svn_path_uri_decode(b->repository->repos_url, pool),
1552289180Speter               b->repository->fs_path->data, revprop_table,
1553251881Speter               commit_done, &ccb,
1554251881Speter               authz_commit_cb, &ab, pool));
1555251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
1556251881Speter  SVN_ERR(svn_ra_svn_drive_editor2(conn, pool, editor, edit_baton,
1557251881Speter                                   &aborted, FALSE));
1558251881Speter  if (!aborted)
1559251881Speter    {
1560251881Speter      SVN_ERR(log_command(b, conn, pool, "%s",
1561251881Speter                          svn_log__commit(new_rev, pool)));
1562251881Speter      SVN_ERR(trivial_auth_request(conn, pool, b));
1563251881Speter
1564251881Speter      /* In tunnel mode, deltify before answering the client, because
1565251881Speter         answering may cause the client to terminate the connection
1566251881Speter         and thus kill the server.  But otherwise, deltify after
1567251881Speter         answering the client, to avoid user-visible delay. */
1568251881Speter
1569289180Speter      if (b->client_info->tunnel)
1570289180Speter        SVN_ERR(svn_fs_deltify_revision(b->repository->fs, new_rev, pool));
1571251881Speter
1572251881Speter      /* Unlock the paths. */
1573251881Speter      if (! keep_locks && lock_tokens && lock_tokens->nelts)
1574289180Speter        SVN_ERR(unlock_paths(lock_tokens, b, pool));
1575251881Speter
1576251881Speter      SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "r(?c)(?c)(?c)",
1577251881Speter                                      new_rev, date, author, post_commit_err));
1578251881Speter
1579289180Speter      if (! b->client_info->tunnel)
1580289180Speter        SVN_ERR(svn_fs_deltify_revision(b->repository->fs, new_rev, pool));
1581251881Speter    }
1582251881Speter  return SVN_NO_ERROR;
1583251881Speter}
1584251881Speter
1585362181Sdimstatic svn_error_t *
1586362181Sdimget_file(svn_ra_svn_conn_t *conn,
1587362181Sdim         apr_pool_t *pool,
1588362181Sdim         svn_ra_svn__list_t *params,
1589362181Sdim         void *baton)
1590251881Speter{
1591251881Speter  server_baton_t *b = baton;
1592362181Sdim  const char *path, *full_path, *hex_digest, *canonical_path;
1593251881Speter  svn_revnum_t rev;
1594251881Speter  svn_fs_root_t *root;
1595251881Speter  svn_stream_t *contents;
1596251881Speter  apr_hash_t *props = NULL;
1597251881Speter  apr_array_header_t *inherited_props;
1598251881Speter  svn_string_t write_str;
1599251881Speter  char buf[4096];
1600251881Speter  apr_size_t len;
1601251881Speter  svn_boolean_t want_props, want_contents;
1602251881Speter  apr_uint64_t wants_inherited_props;
1603251881Speter  svn_checksum_t *checksum;
1604251881Speter  svn_error_t *err, *write_err;
1605251881Speter  int i;
1606251881Speter  authz_baton_t ab;
1607251881Speter
1608251881Speter  ab.server = b;
1609251881Speter  ab.conn = conn;
1610251881Speter
1611251881Speter  /* Parse arguments. */
1612362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)bb?B", &path, &rev,
1613251881Speter                                  &want_props, &want_contents,
1614251881Speter                                  &wants_inherited_props));
1615251881Speter
1616266731Speter  if (wants_inherited_props == SVN_RA_SVN_UNSPECIFIED_NUMBER)
1617266731Speter    wants_inherited_props = FALSE;
1618266731Speter
1619362181Sdim  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path, pool,
1620362181Sdim                                        pool));
1621362181Sdim  full_path = svn_fspath__join(b->repository->fs_path->data, canonical_path,
1622362181Sdim                               pool);
1623251881Speter
1624251881Speter  /* Check authorizations */
1625251881Speter  SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
1626251881Speter                           full_path, FALSE));
1627251881Speter
1628251881Speter  if (!SVN_IS_VALID_REVNUM(rev))
1629289180Speter    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
1630251881Speter
1631251881Speter  SVN_ERR(log_command(b, conn, pool, "%s",
1632251881Speter                      svn_log__get_file(full_path, rev,
1633251881Speter                                        want_contents, want_props, pool)));
1634251881Speter
1635251881Speter  /* Fetch the properties and a stream for the contents. */
1636289180Speter  SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool));
1637251881Speter  SVN_CMD_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, root,
1638251881Speter                                   full_path, TRUE, pool));
1639251881Speter  hex_digest = svn_checksum_to_cstring_display(checksum, pool);
1640266731Speter
1641266731Speter  /* Fetch the file's explicit and/or inherited properties if
1642266731Speter     requested.  Although the wants-iprops boolean was added to the
1643266731Speter     protocol in 1.8 a standard 1.8 client never requests iprops. */
1644251881Speter  if (want_props || wants_inherited_props)
1645266731Speter    SVN_CMD_ERR(get_props(want_props ? &props : NULL,
1646266731Speter                          wants_inherited_props ? &inherited_props : NULL,
1647266731Speter                          &ab, root, full_path,
1648251881Speter                          pool));
1649251881Speter  if (want_contents)
1650251881Speter    SVN_CMD_ERR(svn_fs_file_contents(&contents, root, full_path, pool));
1651251881Speter
1652251881Speter  /* Send successful command response with revision and props. */
1653251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((?c)r(!", "success",
1654251881Speter                                  hex_digest, rev));
1655251881Speter  SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props));
1656251881Speter
1657251881Speter  if (wants_inherited_props)
1658251881Speter    {
1659251881Speter      apr_pool_t *iterpool = svn_pool_create(pool);
1660251881Speter
1661251881Speter      SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(?!"));
1662251881Speter      for (i = 0; i < inherited_props->nelts; i++)
1663251881Speter        {
1664251881Speter          svn_prop_inherited_item_t *iprop =
1665251881Speter            APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *);
1666251881Speter
1667251881Speter          svn_pool_clear(iterpool);
1668251881Speter          SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!",
1669251881Speter                                          iprop->path_or_url));
1670251881Speter          SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash));
1671251881Speter          SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!",
1672251881Speter                                          iprop->path_or_url));
1673251881Speter        }
1674251881Speter      svn_pool_destroy(iterpool);
1675251881Speter    }
1676251881Speter
1677251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));
1678251881Speter
1679251881Speter  /* Now send the file's contents. */
1680251881Speter  if (want_contents)
1681251881Speter    {
1682251881Speter      err = SVN_NO_ERROR;
1683251881Speter      while (1)
1684251881Speter        {
1685251881Speter          len = sizeof(buf);
1686289180Speter          err = svn_stream_read_full(contents, buf, &len);
1687251881Speter          if (err)
1688251881Speter            break;
1689251881Speter          if (len > 0)
1690251881Speter            {
1691251881Speter              write_str.data = buf;
1692251881Speter              write_str.len = len;
1693251881Speter              SVN_ERR(svn_ra_svn__write_string(conn, pool, &write_str));
1694251881Speter            }
1695251881Speter          if (len < sizeof(buf))
1696251881Speter            {
1697251881Speter              err = svn_stream_close(contents);
1698251881Speter              break;
1699251881Speter            }
1700251881Speter        }
1701251881Speter      write_err = svn_ra_svn__write_cstring(conn, pool, "");
1702251881Speter      if (write_err)
1703251881Speter        {
1704251881Speter          svn_error_clear(err);
1705251881Speter          return write_err;
1706251881Speter        }
1707251881Speter      SVN_CMD_ERR(err);
1708251881Speter      SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
1709251881Speter    }
1710251881Speter
1711251881Speter  return SVN_NO_ERROR;
1712251881Speter}
1713251881Speter
1714362181Sdim/* Translate all the words in DIRENT_FIELDS_LIST into the flags in
1715362181Sdim * DIRENT_FIELDS_P.  If DIRENT_FIELDS_LIST is NULL, set all flags. */
1716362181Sdimstatic svn_error_t *
1717362181Sdimparse_dirent_fields(apr_uint32_t *dirent_fields_p,
1718362181Sdim                    svn_ra_svn__list_t *dirent_fields_list)
1719251881Speter{
1720362181Sdim  static const svn_string_t str_kind
1721362181Sdim    = SVN__STATIC_STRING(SVN_RA_SVN_DIRENT_KIND);
1722362181Sdim  static const svn_string_t str_size
1723362181Sdim    = SVN__STATIC_STRING(SVN_RA_SVN_DIRENT_SIZE);
1724362181Sdim  static const svn_string_t str_has_props
1725362181Sdim    = SVN__STATIC_STRING(SVN_RA_SVN_DIRENT_HAS_PROPS);
1726362181Sdim  static const svn_string_t str_created_rev
1727362181Sdim    = SVN__STATIC_STRING(SVN_RA_SVN_DIRENT_CREATED_REV);
1728362181Sdim  static const svn_string_t str_time
1729362181Sdim    = SVN__STATIC_STRING(SVN_RA_SVN_DIRENT_TIME);
1730362181Sdim  static const svn_string_t str_last_author
1731362181Sdim    = SVN__STATIC_STRING(SVN_RA_SVN_DIRENT_LAST_AUTHOR);
1732251881Speter
1733362181Sdim  apr_uint32_t dirent_fields;
1734251881Speter
1735251881Speter  if (! dirent_fields_list)
1736251881Speter    {
1737251881Speter      dirent_fields = SVN_DIRENT_ALL;
1738251881Speter    }
1739251881Speter  else
1740251881Speter    {
1741362181Sdim      int i;
1742251881Speter      dirent_fields = 0;
1743251881Speter
1744251881Speter      for (i = 0; i < dirent_fields_list->nelts; ++i)
1745251881Speter        {
1746362181Sdim          svn_ra_svn__item_t *elt
1747362181Sdim            = &SVN_RA_SVN__LIST_ITEM(dirent_fields_list, i);
1748251881Speter
1749251881Speter          if (elt->kind != SVN_RA_SVN_WORD)
1750251881Speter            return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1751362181Sdim                                    "Dirent field not a word");
1752251881Speter
1753362181Sdim          if (svn_string_compare(&str_kind, &elt->u.word))
1754251881Speter            dirent_fields |= SVN_DIRENT_KIND;
1755362181Sdim          else if (svn_string_compare(&str_size, &elt->u.word))
1756251881Speter            dirent_fields |= SVN_DIRENT_SIZE;
1757362181Sdim          else if (svn_string_compare(&str_has_props, &elt->u.word))
1758251881Speter            dirent_fields |= SVN_DIRENT_HAS_PROPS;
1759362181Sdim          else if (svn_string_compare(&str_created_rev, &elt->u.word))
1760251881Speter            dirent_fields |= SVN_DIRENT_CREATED_REV;
1761362181Sdim          else if (svn_string_compare(&str_time, &elt->u.word))
1762251881Speter            dirent_fields |= SVN_DIRENT_TIME;
1763362181Sdim          else if (svn_string_compare(&str_last_author, &elt->u.word))
1764251881Speter            dirent_fields |= SVN_DIRENT_LAST_AUTHOR;
1765251881Speter        }
1766251881Speter    }
1767251881Speter
1768362181Sdim  *dirent_fields_p = dirent_fields;
1769362181Sdim  return SVN_NO_ERROR;
1770362181Sdim}
1771251881Speter
1772362181Sdimstatic svn_error_t *
1773362181Sdimget_dir(svn_ra_svn_conn_t *conn,
1774362181Sdim        apr_pool_t *pool,
1775362181Sdim        svn_ra_svn__list_t *params,
1776362181Sdim        void *baton)
1777362181Sdim{
1778362181Sdim  server_baton_t *b = baton;
1779362181Sdim  const char *path, *full_path, *canonical_path;
1780362181Sdim  svn_revnum_t rev;
1781362181Sdim  apr_hash_t *entries, *props = NULL;
1782362181Sdim  apr_array_header_t *inherited_props;
1783362181Sdim  apr_hash_index_t *hi;
1784362181Sdim  svn_fs_root_t *root;
1785362181Sdim  apr_pool_t *subpool;
1786362181Sdim  svn_boolean_t want_props, want_contents;
1787362181Sdim  apr_uint64_t wants_inherited_props;
1788362181Sdim  apr_uint32_t dirent_fields;
1789362181Sdim  svn_ra_svn__list_t *dirent_fields_list = NULL;
1790362181Sdim  int i;
1791362181Sdim  authz_baton_t ab;
1792362181Sdim
1793362181Sdim  ab.server = b;
1794362181Sdim  ab.conn = conn;
1795362181Sdim
1796362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)bb?l?B", &path, &rev,
1797362181Sdim                                  &want_props, &want_contents,
1798362181Sdim                                  &dirent_fields_list,
1799362181Sdim                                  &wants_inherited_props));
1800362181Sdim
1801362181Sdim  if (wants_inherited_props == SVN_RA_SVN_UNSPECIFIED_NUMBER)
1802362181Sdim    wants_inherited_props = FALSE;
1803362181Sdim
1804362181Sdim  SVN_ERR(parse_dirent_fields(&dirent_fields, dirent_fields_list));
1805362181Sdim  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
1806362181Sdim                                        pool, pool));
1807362181Sdim  full_path = svn_fspath__join(b->repository->fs_path->data, canonical_path,
1808362181Sdim                               pool);
1809362181Sdim
1810251881Speter  /* Check authorizations */
1811251881Speter  SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
1812251881Speter                           full_path, FALSE));
1813251881Speter
1814251881Speter  if (!SVN_IS_VALID_REVNUM(rev))
1815289180Speter    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
1816251881Speter
1817251881Speter  SVN_ERR(log_command(b, conn, pool, "%s",
1818251881Speter                      svn_log__get_dir(full_path, rev,
1819251881Speter                                       want_contents, want_props,
1820251881Speter                                       dirent_fields, pool)));
1821251881Speter
1822251881Speter  /* Fetch the root of the appropriate revision. */
1823289180Speter  SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool));
1824251881Speter
1825266731Speter  /* Fetch the directory's explicit and/or inherited properties if
1826266731Speter     requested.  Although the wants-iprops boolean was added to the
1827266731Speter     protocol in 1.8 a standard 1.8 client never requests iprops. */
1828251881Speter  if (want_props || wants_inherited_props)
1829266731Speter    SVN_CMD_ERR(get_props(want_props ? &props : NULL,
1830266731Speter                          wants_inherited_props ? &inherited_props : NULL,
1831266731Speter                          &ab, root, full_path,
1832251881Speter                          pool));
1833251881Speter
1834286506Speter  /* Fetch the directories' entries before starting the response, to allow
1835286506Speter     proper error handling in cases like when FULL_PATH doesn't exist */
1836286506Speter  if (want_contents)
1837286506Speter      SVN_CMD_ERR(svn_fs_dir_entries(&entries, root, full_path, pool));
1838286506Speter
1839251881Speter  /* Begin response ... */
1840251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(r(!", "success", rev));
1841251881Speter  SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props));
1842251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(!"));
1843251881Speter
1844251881Speter  /* Fetch the directory entries if requested and send them immediately. */
1845251881Speter  if (want_contents)
1846251881Speter    {
1847251881Speter      /* Use epoch for a placeholder for a missing date.  */
1848251881Speter      const char *missing_date = svn_time_to_cstring(0, pool);
1849251881Speter
1850251881Speter      /* Transform the hash table's FS entries into dirents.  This probably
1851251881Speter       * belongs in libsvn_repos. */
1852251881Speter      subpool = svn_pool_create(pool);
1853251881Speter      for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
1854251881Speter        {
1855289180Speter          const char *name = apr_hash_this_key(hi);
1856289180Speter          svn_fs_dirent_t *fsent = apr_hash_this_val(hi);
1857251881Speter          const char *file_path;
1858251881Speter
1859251881Speter          /* The fields in the entry tuple.  */
1860251881Speter          svn_node_kind_t entry_kind = svn_node_none;
1861251881Speter          svn_filesize_t entry_size = 0;
1862251881Speter          svn_boolean_t has_props = FALSE;
1863251881Speter          /* If 'created rev' was not requested, send 0.  We can't use
1864251881Speter           * SVN_INVALID_REVNUM as the tuple field is not optional.
1865251881Speter           * See the email thread on dev@, 2012-03-28, subject
1866251881Speter           * "buildbot failure in ASF Buildbot on svn-slik-w2k3-x64-ra",
1867251881Speter           * <http://svn.haxx.se/dev/archive-2012-03/0655.shtml>. */
1868251881Speter          svn_revnum_t created_rev = 0;
1869251881Speter          const char *cdate = NULL;
1870251881Speter          const char *last_author = NULL;
1871251881Speter
1872251881Speter          svn_pool_clear(subpool);
1873251881Speter
1874251881Speter          file_path = svn_fspath__join(full_path, name, subpool);
1875289180Speter          if (! lookup_access(subpool, b, svn_authz_read, file_path, FALSE))
1876251881Speter            continue;
1877251881Speter
1878251881Speter          if (dirent_fields & SVN_DIRENT_KIND)
1879251881Speter              entry_kind = fsent->kind;
1880251881Speter
1881251881Speter          if (dirent_fields & SVN_DIRENT_SIZE)
1882362181Sdim              if (fsent->kind != svn_node_dir)
1883251881Speter                SVN_CMD_ERR(svn_fs_file_length(&entry_size, root, file_path,
1884251881Speter                                               subpool));
1885251881Speter
1886251881Speter          if (dirent_fields & SVN_DIRENT_HAS_PROPS)
1887251881Speter            {
1888251881Speter              /* has_props */
1889289180Speter              SVN_CMD_ERR(svn_fs_node_has_props(&has_props, root, file_path,
1890251881Speter                                               subpool));
1891251881Speter            }
1892251881Speter
1893251881Speter          if ((dirent_fields & SVN_DIRENT_LAST_AUTHOR)
1894251881Speter              || (dirent_fields & SVN_DIRENT_TIME)
1895251881Speter              || (dirent_fields & SVN_DIRENT_CREATED_REV))
1896251881Speter            {
1897251881Speter              /* created_rev, last_author, time */
1898251881Speter              SVN_CMD_ERR(svn_repos_get_committed_info(&created_rev,
1899251881Speter                                                       &cdate,
1900251881Speter                                                       &last_author,
1901251881Speter                                                       root,
1902251881Speter                                                       file_path,
1903251881Speter                                                       subpool));
1904251881Speter            }
1905251881Speter
1906251881Speter          /* The client does not properly handle a missing CDATE. For
1907251881Speter             interoperability purposes, we must fill in some junk.
1908251881Speter
1909251881Speter             See libsvn_ra_svn/client.c:ra_svn_get_dir()  */
1910251881Speter          if (cdate == NULL)
1911251881Speter            cdate = missing_date;
1912251881Speter
1913251881Speter          /* Send the entry. */
1914251881Speter          SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "cwnbr(?c)(?c)", name,
1915251881Speter                                          svn_node_kind_to_word(entry_kind),
1916251881Speter                                          (apr_uint64_t) entry_size,
1917251881Speter                                          has_props, created_rev,
1918251881Speter                                          cdate, last_author));
1919251881Speter        }
1920251881Speter      svn_pool_destroy(subpool);
1921251881Speter    }
1922251881Speter
1923251881Speter  if (wants_inherited_props)
1924251881Speter    {
1925251881Speter      apr_pool_t *iterpool = svn_pool_create(pool);
1926251881Speter
1927251881Speter      SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(?!"));
1928251881Speter      for (i = 0; i < inherited_props->nelts; i++)
1929251881Speter        {
1930251881Speter          svn_prop_inherited_item_t *iprop =
1931251881Speter            APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *);
1932251881Speter
1933251881Speter          svn_pool_clear(iterpool);
1934251881Speter          SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!",
1935251881Speter                                          iprop->path_or_url));
1936251881Speter          SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash));
1937251881Speter          SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!",
1938251881Speter                                          iprop->path_or_url));
1939251881Speter        }
1940251881Speter      svn_pool_destroy(iterpool);
1941251881Speter    }
1942251881Speter
1943251881Speter  /* Finish response. */
1944251881Speter  return svn_ra_svn__write_tuple(conn, pool, "!))");
1945251881Speter}
1946251881Speter
1947362181Sdimstatic svn_error_t *
1948362181Sdimupdate(svn_ra_svn_conn_t *conn,
1949362181Sdim       apr_pool_t *pool,
1950362181Sdim       svn_ra_svn__list_t *params,
1951362181Sdim       void *baton)
1952251881Speter{
1953251881Speter  server_baton_t *b = baton;
1954251881Speter  svn_revnum_t rev;
1955362181Sdim  const char *target, *full_path, *depth_word, *canonical_target;
1956251881Speter  svn_boolean_t recurse;
1957289180Speter  svn_tristate_t send_copyfrom_args; /* Optional; default FALSE */
1958289180Speter  svn_tristate_t ignore_ancestry; /* Optional; default FALSE */
1959251881Speter  /* Default to unknown.  Old clients won't send depth, but we'll
1960251881Speter     handle that by converting recurse if necessary. */
1961251881Speter  svn_depth_t depth = svn_depth_unknown;
1962251881Speter  svn_boolean_t is_checkout;
1963251881Speter
1964251881Speter  /* Parse the arguments. */
1965362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "(?r)cb?w3?3", &rev, &target,
1966251881Speter                                  &recurse, &depth_word,
1967251881Speter                                  &send_copyfrom_args, &ignore_ancestry));
1968362181Sdim  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_target, NULL, target,
1969362181Sdim                                        pool, pool));
1970362181Sdim  target = canonical_target;
1971251881Speter
1972251881Speter  if (depth_word)
1973251881Speter    depth = svn_depth_from_word(depth_word);
1974251881Speter  else
1975251881Speter    depth = SVN_DEPTH_INFINITY_OR_FILES(recurse);
1976251881Speter
1977289180Speter  full_path = svn_fspath__join(b->repository->fs_path->data, target, pool);
1978251881Speter  /* Check authorization and authenticate the user if necessary. */
1979251881Speter  SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, full_path, FALSE));
1980251881Speter
1981251881Speter  if (!SVN_IS_VALID_REVNUM(rev))
1982289180Speter    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
1983251881Speter
1984251881Speter  SVN_ERR(accept_report(&is_checkout, NULL,
1985251881Speter                        conn, pool, b, rev, target, NULL, TRUE,
1986251881Speter                        depth,
1987289180Speter                        (send_copyfrom_args == svn_tristate_true),
1988289180Speter                        (ignore_ancestry == svn_tristate_true)));
1989251881Speter  if (is_checkout)
1990251881Speter    {
1991251881Speter      SVN_ERR(log_command(b, conn, pool, "%s",
1992251881Speter                          svn_log__checkout(full_path, rev,
1993251881Speter                                            depth, pool)));
1994251881Speter    }
1995251881Speter  else
1996251881Speter    {
1997251881Speter      SVN_ERR(log_command(b, conn, pool, "%s",
1998251881Speter                          svn_log__update(full_path, rev, depth,
1999289180Speter                                          (send_copyfrom_args
2000289180Speter                                           == svn_tristate_true),
2001289180Speter                                          pool)));
2002251881Speter    }
2003251881Speter
2004251881Speter  return SVN_NO_ERROR;
2005251881Speter}
2006251881Speter
2007362181Sdimstatic svn_error_t *
2008362181Sdimswitch_cmd(svn_ra_svn_conn_t *conn,
2009362181Sdim           apr_pool_t *pool,
2010362181Sdim           svn_ra_svn__list_t *params,
2011362181Sdim           void *baton)
2012251881Speter{
2013251881Speter  server_baton_t *b = baton;
2014251881Speter  svn_revnum_t rev;
2015251881Speter  const char *target, *depth_word;
2016362181Sdim  const char *switch_url, *switch_path, *canonical_url, *canonical_target;
2017251881Speter  svn_boolean_t recurse;
2018251881Speter  /* Default to unknown.  Old clients won't send depth, but we'll
2019251881Speter     handle that by converting recurse if necessary. */
2020251881Speter  svn_depth_t depth = svn_depth_unknown;
2021289180Speter  svn_tristate_t send_copyfrom_args; /* Optional; default FALSE */
2022289180Speter  svn_tristate_t ignore_ancestry; /* Optional; default TRUE */
2023251881Speter
2024251881Speter  /* Parse the arguments. */
2025362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "(?r)cbc?w?33", &rev, &target,
2026251881Speter                                  &recurse, &switch_url, &depth_word,
2027251881Speter                                  &send_copyfrom_args, &ignore_ancestry));
2028362181Sdim  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_target, NULL, target,
2029362181Sdim                                        pool, pool));
2030362181Sdim  target = canonical_target;
2031362181Sdim  SVN_ERR(svn_uri_canonicalize_safe(&canonical_url, NULL, switch_url, pool,
2032362181Sdim                                    pool));
2033362181Sdim  switch_url = canonical_url;
2034251881Speter  if (depth_word)
2035251881Speter    depth = svn_depth_from_word(depth_word);
2036251881Speter  else
2037251881Speter    depth = SVN_DEPTH_INFINITY_OR_FILES(recurse);
2038251881Speter
2039251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
2040251881Speter  if (!SVN_IS_VALID_REVNUM(rev))
2041289180Speter    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
2042251881Speter
2043289180Speter  SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repository->repos_url,
2044289180Speter                                              pool),
2045251881Speter                          svn_path_uri_decode(switch_url, pool),
2046251881Speter                          &switch_path));
2047251881Speter
2048251881Speter  {
2049289180Speter    const char *full_path = svn_fspath__join(b->repository->fs_path->data,
2050289180Speter                                             target, pool);
2051251881Speter    SVN_ERR(log_command(b, conn, pool, "%s",
2052251881Speter                        svn_log__switch(full_path, switch_path, rev,
2053251881Speter                                        depth, pool)));
2054251881Speter  }
2055251881Speter
2056251881Speter  return accept_report(NULL, NULL,
2057251881Speter                       conn, pool, b, rev, target, switch_path, TRUE,
2058251881Speter                       depth,
2059289180Speter                       (send_copyfrom_args == svn_tristate_true),
2060289180Speter                       (ignore_ancestry != svn_tristate_false));
2061251881Speter}
2062251881Speter
2063362181Sdimstatic svn_error_t *
2064362181Sdimstatus(svn_ra_svn_conn_t *conn,
2065362181Sdim       apr_pool_t *pool,
2066362181Sdim       svn_ra_svn__list_t *params,
2067362181Sdim       void *baton)
2068251881Speter{
2069251881Speter  server_baton_t *b = baton;
2070251881Speter  svn_revnum_t rev;
2071362181Sdim  const char *target, *depth_word, *canonical_target;
2072251881Speter  svn_boolean_t recurse;
2073251881Speter  /* Default to unknown.  Old clients won't send depth, but we'll
2074251881Speter     handle that by converting recurse if necessary. */
2075251881Speter  svn_depth_t depth = svn_depth_unknown;
2076251881Speter
2077251881Speter  /* Parse the arguments. */
2078362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "cb?(?r)?w",
2079251881Speter                                  &target, &recurse, &rev, &depth_word));
2080362181Sdim  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_target, NULL, target,
2081362181Sdim                                        pool, pool));
2082362181Sdim  target = canonical_target;
2083251881Speter
2084251881Speter  if (depth_word)
2085251881Speter    depth = svn_depth_from_word(depth_word);
2086251881Speter  else
2087251881Speter    depth = SVN_DEPTH_INFINITY_OR_EMPTY(recurse);
2088251881Speter
2089251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
2090251881Speter  if (!SVN_IS_VALID_REVNUM(rev))
2091289180Speter    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
2092251881Speter
2093251881Speter  {
2094289180Speter    const char *full_path = svn_fspath__join(b->repository->fs_path->data,
2095289180Speter                                             target, pool);
2096251881Speter    SVN_ERR(log_command(b, conn, pool, "%s",
2097251881Speter                        svn_log__status(full_path, rev, depth, pool)));
2098251881Speter  }
2099251881Speter
2100251881Speter  return accept_report(NULL, NULL, conn, pool, b, rev, target, NULL, FALSE,
2101251881Speter                       depth, FALSE, FALSE);
2102251881Speter}
2103251881Speter
2104362181Sdimstatic svn_error_t *
2105362181Sdimdiff(svn_ra_svn_conn_t *conn,
2106362181Sdim     apr_pool_t *pool,
2107362181Sdim     svn_ra_svn__list_t *params,
2108362181Sdim     void *baton)
2109251881Speter{
2110251881Speter  server_baton_t *b = baton;
2111251881Speter  svn_revnum_t rev;
2112362181Sdim  const char *target, *versus_url, *versus_path, *depth_word, *canonical_url;
2113362181Sdim  const char *canonical_target;
2114251881Speter  svn_boolean_t recurse, ignore_ancestry;
2115251881Speter  svn_boolean_t text_deltas;
2116251881Speter  /* Default to unknown.  Old clients won't send depth, but we'll
2117251881Speter     handle that by converting recurse if necessary. */
2118251881Speter  svn_depth_t depth = svn_depth_unknown;
2119251881Speter
2120251881Speter  /* Parse the arguments. */
2121251881Speter  if (params->nelts == 5)
2122251881Speter    {
2123251881Speter      /* Clients before 1.4 don't send the text_deltas boolean or depth. */
2124362181Sdim      SVN_ERR(svn_ra_svn__parse_tuple(params, "(?r)cbbc", &rev, &target,
2125251881Speter                                      &recurse, &ignore_ancestry, &versus_url));
2126251881Speter      text_deltas = TRUE;
2127251881Speter      depth_word = NULL;
2128251881Speter    }
2129251881Speter  else
2130251881Speter    {
2131362181Sdim      SVN_ERR(svn_ra_svn__parse_tuple(params, "(?r)cbbcb?w",
2132251881Speter                                      &rev, &target, &recurse,
2133251881Speter                                      &ignore_ancestry, &versus_url,
2134251881Speter                                      &text_deltas, &depth_word));
2135251881Speter    }
2136362181Sdim  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_target, NULL, target,
2137362181Sdim                                        pool, pool));
2138362181Sdim  target = canonical_target;
2139362181Sdim  SVN_ERR(svn_uri_canonicalize_safe(&canonical_url, NULL, versus_url,
2140362181Sdim                                    pool, pool));
2141362181Sdim  versus_url = canonical_url;
2142251881Speter
2143251881Speter  if (depth_word)
2144251881Speter    depth = svn_depth_from_word(depth_word);
2145251881Speter  else
2146251881Speter    depth = SVN_DEPTH_INFINITY_OR_FILES(recurse);
2147251881Speter
2148251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
2149251881Speter
2150251881Speter  if (!SVN_IS_VALID_REVNUM(rev))
2151289180Speter    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
2152289180Speter  SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repository->repos_url,
2153289180Speter                                              pool),
2154251881Speter                          svn_path_uri_decode(versus_url, pool),
2155251881Speter                          &versus_path));
2156251881Speter
2157251881Speter  {
2158289180Speter    const char *full_path = svn_fspath__join(b->repository->fs_path->data,
2159289180Speter                                             target, pool);
2160251881Speter    svn_revnum_t from_rev;
2161251881Speter    SVN_ERR(accept_report(NULL, &from_rev,
2162251881Speter                          conn, pool, b, rev, target, versus_path,
2163251881Speter                          text_deltas, depth, FALSE, ignore_ancestry));
2164251881Speter    SVN_ERR(log_command(b, conn, pool, "%s",
2165251881Speter                        svn_log__diff(full_path, from_rev, versus_path,
2166251881Speter                                      rev, depth, ignore_ancestry,
2167251881Speter                                      pool)));
2168251881Speter  }
2169251881Speter  return SVN_NO_ERROR;
2170251881Speter}
2171251881Speter
2172362181Sdim/* Baton type to be used with mergeinfo_receiver. */
2173362181Sdimtypedef struct mergeinfo_receiver_baton_t
2174362181Sdim{
2175362181Sdim  /* Send the response over this connection. */
2176362181Sdim  svn_ra_svn_conn_t *conn;
2177362181Sdim
2178362181Sdim  /* Start path of the query; report paths relative to this one. */
2179362181Sdim  const char *fs_path;
2180362181Sdim
2181362181Sdim  /* Did we already send the opening sequence? */
2182362181Sdim  svn_boolean_t starting_tuple_sent;
2183362181Sdim} mergeinfo_receiver_baton_t;
2184362181Sdim
2185362181Sdim/* Utility method sending the start of the "get m/i" response once
2186362181Sdim   over BATON->CONN. */
2187362181Sdimstatic svn_error_t *
2188362181Sdimsend_mergeinfo_starting_tuple(mergeinfo_receiver_baton_t *baton,
2189362181Sdim                              apr_pool_t *scratch_pool)
2190362181Sdim{
2191362181Sdim  if (baton->starting_tuple_sent)
2192362181Sdim    return SVN_NO_ERROR;
2193362181Sdim
2194362181Sdim  SVN_ERR(svn_ra_svn__write_tuple(baton->conn, scratch_pool,
2195362181Sdim                                  "w((!", "success"));
2196362181Sdim  baton->starting_tuple_sent = TRUE;
2197362181Sdim
2198362181Sdim  return SVN_NO_ERROR;
2199362181Sdim}
2200362181Sdim
2201362181Sdim/* Implements svn_repos_mergeinfo_receiver_t, sending the MERGEINFO
2202362181Sdim * out over the connection in the mergeinfo_receiver_baton_t * BATON. */
2203362181Sdimstatic svn_error_t *
2204362181Sdimmergeinfo_receiver(const char *path,
2205362181Sdim                   svn_mergeinfo_t mergeinfo,
2206362181Sdim                   void *baton,
2207362181Sdim                   apr_pool_t *scratch_pool)
2208362181Sdim{
2209362181Sdim  mergeinfo_receiver_baton_t *b = baton;
2210362181Sdim  svn_string_t *mergeinfo_string;
2211362181Sdim
2212362181Sdim  /* Delay starting the response until we checked that the initial
2213362181Sdim     request went through.  We are at that point now b/c we've got
2214362181Sdim     the first results in. */
2215362181Sdim  SVN_ERR(send_mergeinfo_starting_tuple(b, scratch_pool));
2216362181Sdim
2217362181Sdim  /* Adjust the path info and send the m/i. */
2218362181Sdim  path = svn_fspath__skip_ancestor(b->fs_path, path);
2219362181Sdim  SVN_ERR(svn_mergeinfo_to_string(&mergeinfo_string, mergeinfo,
2220362181Sdim                                  scratch_pool));
2221362181Sdim  SVN_ERR(svn_ra_svn__write_tuple(b->conn, scratch_pool, "cs", path,
2222362181Sdim                                  mergeinfo_string));
2223362181Sdim
2224362181Sdim  return SVN_NO_ERROR;
2225362181Sdim}
2226362181Sdim
2227251881Speter/* Regardless of whether a client's capabilities indicate an
2228251881Speter   understanding of this command (by way of SVN_RA_SVN_CAP_MERGEINFO),
2229251881Speter   we provide a response.
2230251881Speter
2231251881Speter   ASSUMPTION: When performing a 'merge' with two URLs at different
2232251881Speter   revisions, the client will call this command more than once. */
2233362181Sdimstatic svn_error_t *
2234362181Sdimget_mergeinfo(svn_ra_svn_conn_t *conn,
2235362181Sdim              apr_pool_t *pool,
2236362181Sdim              svn_ra_svn__list_t *params,
2237362181Sdim              void *baton)
2238251881Speter{
2239251881Speter  server_baton_t *b = baton;
2240251881Speter  svn_revnum_t rev;
2241362181Sdim  svn_ra_svn__list_t *paths;
2242362181Sdim  apr_array_header_t *canonical_paths;
2243251881Speter  int i;
2244251881Speter  const char *inherit_word;
2245251881Speter  svn_mergeinfo_inheritance_t inherit;
2246251881Speter  svn_boolean_t include_descendants;
2247251881Speter  authz_baton_t ab;
2248362181Sdim  mergeinfo_receiver_baton_t mergeinfo_baton;
2249251881Speter
2250251881Speter  ab.server = b;
2251251881Speter  ab.conn = conn;
2252251881Speter
2253362181Sdim  mergeinfo_baton.conn = conn;
2254362181Sdim  mergeinfo_baton.fs_path = b->repository->fs_path->data;
2255362181Sdim  mergeinfo_baton.starting_tuple_sent = FALSE;
2256362181Sdim
2257362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "l(?r)wb", &paths, &rev,
2258251881Speter                                  &inherit_word, &include_descendants));
2259251881Speter  inherit = svn_inheritance_from_word(inherit_word);
2260251881Speter
2261251881Speter  /* Canonicalize the paths which mergeinfo has been requested for. */
2262251881Speter  canonical_paths = apr_array_make(pool, paths->nelts, sizeof(const char *));
2263251881Speter  for (i = 0; i < paths->nelts; i++)
2264251881Speter     {
2265362181Sdim        svn_ra_svn__item_t *item = &SVN_RA_SVN__LIST_ITEM(paths, i);
2266362181Sdim        const char *full_path, *canonical_path;
2267251881Speter
2268251881Speter        if (item->kind != SVN_RA_SVN_STRING)
2269251881Speter          return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
2270251881Speter                                  _("Path is not a string"));
2271362181Sdim        SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL,
2272362181Sdim            item->u.string.data, pool, pool));
2273362181Sdim        full_path = svn_fspath__join(b->repository->fs_path->data,
2274362181Sdim                                     canonical_path, pool);
2275251881Speter        APR_ARRAY_PUSH(canonical_paths, const char *) = full_path;
2276251881Speter     }
2277251881Speter
2278251881Speter  SVN_ERR(log_command(b, conn, pool, "%s",
2279251881Speter                      svn_log__get_mergeinfo(canonical_paths, inherit,
2280251881Speter                                             include_descendants,
2281251881Speter                                             pool)));
2282251881Speter
2283251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
2284251881Speter
2285362181Sdim  SVN_CMD_ERR(svn_repos_fs_get_mergeinfo2(b->repository->repos,
2286362181Sdim                                          canonical_paths, rev,
2287362181Sdim                                          inherit,
2288362181Sdim                                          include_descendants,
2289362181Sdim                                          authz_check_access_cb_func(b), &ab,
2290362181Sdim                                          mergeinfo_receiver,
2291362181Sdim                                          &mergeinfo_baton,
2292362181Sdim                                          pool));
2293251881Speter
2294362181Sdim  /* We might not have sent anything
2295362181Sdim     => ensure to begin the response in any case. */
2296362181Sdim  SVN_ERR(send_mergeinfo_starting_tuple(&mergeinfo_baton, pool));
2297251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));
2298251881Speter
2299251881Speter  return SVN_NO_ERROR;
2300251881Speter}
2301251881Speter
2302362181Sdim/* Send a changed paths list entry to the client.
2303362181Sdim   This implements svn_repos_path_change_receiver_t. */
2304362181Sdimstatic svn_error_t *
2305362181Sdimpath_change_receiver(void *baton,
2306362181Sdim                     svn_repos_path_change_t *change,
2307362181Sdim                     apr_pool_t *scratch_pool)
2308251881Speter{
2309362181Sdim  const char symbol[] = "MADR";
2310362181Sdim
2311251881Speter  log_baton_t *b = baton;
2312251881Speter  svn_ra_svn_conn_t *conn = b->conn;
2313362181Sdim
2314362181Sdim  /* Sanitize and convert change kind to ra-svn level action.
2315362181Sdim
2316362181Sdim     Pushing that conversion down into libsvn_ra_svn would add yet another
2317362181Sdim     API dependency there. */
2318362181Sdim  char action = (   change->change_kind < svn_fs_path_change_modify
2319362181Sdim                 || change->change_kind > svn_fs_path_change_replace)
2320362181Sdim              ? 0
2321362181Sdim              : symbol[change->change_kind];
2322362181Sdim
2323362181Sdim  /* Open lists once: LOG_ENTRY and LOG_ENTRY->CHANGED_PATHS. */
2324362181Sdim  if (!b->started)
2325362181Sdim    {
2326362181Sdim      SVN_ERR(svn_ra_svn__start_list(conn, scratch_pool));
2327362181Sdim      SVN_ERR(svn_ra_svn__start_list(conn, scratch_pool));
2328362181Sdim      b->started = TRUE;
2329362181Sdim    }
2330362181Sdim
2331362181Sdim  /* Serialize CHANGE. */
2332362181Sdim  SVN_ERR(svn_ra_svn__write_data_log_changed_path(
2333362181Sdim              conn, scratch_pool,
2334362181Sdim              &change->path,
2335362181Sdim              action,
2336362181Sdim              change->copyfrom_path,
2337362181Sdim              change->copyfrom_rev,
2338362181Sdim              change->node_kind,
2339362181Sdim              change->text_mod,
2340362181Sdim              change->prop_mod));
2341362181Sdim
2342362181Sdim  return SVN_NO_ERROR;
2343362181Sdim}
2344362181Sdim
2345362181Sdim/* Send a the meta data and the revpros for LOG_ENTRY to the client.
2346362181Sdim   This implements svn_log_entry_receiver_t. */
2347362181Sdimstatic svn_error_t *
2348362181Sdimrevision_receiver(void *baton,
2349362181Sdim                  svn_repos_log_entry_t *log_entry,
2350362181Sdim                  apr_pool_t *scratch_pool)
2351362181Sdim{
2352362181Sdim  log_baton_t *b = baton;
2353362181Sdim  svn_ra_svn_conn_t *conn = b->conn;
2354251881Speter  svn_boolean_t invalid_revnum = FALSE;
2355289180Speter  const svn_string_t *author, *date, *message;
2356289180Speter  unsigned revprop_count;
2357251881Speter
2358251881Speter  if (log_entry->revision == SVN_INVALID_REVNUM)
2359251881Speter    {
2360251881Speter      /* If the stack depth is zero, we've seen the last revision, so don't
2361251881Speter         send it, just return. */
2362251881Speter      if (b->stack_depth == 0)
2363251881Speter        return SVN_NO_ERROR;
2364251881Speter
2365251881Speter      /* Because the svn protocol won't let us send an invalid revnum, we have
2366251881Speter         to fudge here and send an additional flag. */
2367251881Speter      log_entry->revision = 0;
2368251881Speter      invalid_revnum = TRUE;
2369251881Speter      b->stack_depth--;
2370251881Speter    }
2371251881Speter
2372289180Speter  svn_compat_log_revprops_out_string(&author, &date, &message,
2373289180Speter                                     log_entry->revprops);
2374362181Sdim
2375362181Sdim  /* Revprops list filtering is somewhat expensive.
2376362181Sdim     Avoid doing that for the 90% case where only the standard revprops
2377362181Sdim     have been requested and delivered. */
2378362181Sdim  if (author && date && message && apr_hash_count(log_entry->revprops) == 3)
2379362181Sdim    {
2380362181Sdim      revprop_count = 0;
2381362181Sdim    }
2382289180Speter  else
2383362181Sdim    {
2384362181Sdim      svn_compat_log_revprops_clear(log_entry->revprops);
2385362181Sdim      if (log_entry->revprops)
2386362181Sdim        revprop_count = apr_hash_count(log_entry->revprops);
2387362181Sdim      else
2388362181Sdim        revprop_count = 0;
2389362181Sdim    }
2390289180Speter
2391362181Sdim  /* Open lists once: LOG_ENTRY and LOG_ENTRY->CHANGED_PATHS. */
2392362181Sdim  if (!b->started)
2393251881Speter    {
2394362181Sdim      SVN_ERR(svn_ra_svn__start_list(conn, scratch_pool));
2395362181Sdim      SVN_ERR(svn_ra_svn__start_list(conn, scratch_pool));
2396251881Speter    }
2397251881Speter
2398362181Sdim  /* Close LOG_ENTRY->CHANGED_PATHS. */
2399362181Sdim  SVN_ERR(svn_ra_svn__end_list(conn, scratch_pool));
2400362181Sdim  b->started = FALSE;
2401362181Sdim
2402289180Speter  /* send LOG_ENTRY main members */
2403362181Sdim  SVN_ERR(svn_ra_svn__write_data_log_entry(conn, scratch_pool,
2404289180Speter                                           log_entry->revision,
2405289180Speter                                           author, date, message,
2406289180Speter                                           log_entry->has_children,
2407289180Speter                                           invalid_revnum, revprop_count));
2408289180Speter
2409289180Speter  /* send LOG_ENTRY->REVPROPS */
2410362181Sdim  SVN_ERR(svn_ra_svn__start_list(conn, scratch_pool));
2411289180Speter  if (revprop_count)
2412362181Sdim    SVN_ERR(svn_ra_svn__write_proplist(conn, scratch_pool,
2413362181Sdim                                       log_entry->revprops));
2414362181Sdim  SVN_ERR(svn_ra_svn__end_list(conn, scratch_pool));
2415289180Speter
2416289180Speter  /* send LOG_ENTRY members that were added in later SVN releases */
2417362181Sdim  SVN_ERR(svn_ra_svn__write_boolean(conn, scratch_pool,
2418362181Sdim                                    log_entry->subtractive_merge));
2419362181Sdim  SVN_ERR(svn_ra_svn__end_list(conn, scratch_pool));
2420289180Speter
2421251881Speter  if (log_entry->has_children)
2422251881Speter    b->stack_depth++;
2423251881Speter
2424251881Speter  return SVN_NO_ERROR;
2425251881Speter}
2426251881Speter
2427362181Sdimstatic svn_error_t *
2428362181Sdimlog_cmd(svn_ra_svn_conn_t *conn,
2429362181Sdim        apr_pool_t *pool,
2430362181Sdim        svn_ra_svn__list_t *params,
2431362181Sdim        void *baton)
2432251881Speter{
2433251881Speter  svn_error_t *err, *write_err;
2434251881Speter  server_baton_t *b = baton;
2435251881Speter  svn_revnum_t start_rev, end_rev;
2436362181Sdim  const char *full_path, *canonical_path;
2437251881Speter  svn_boolean_t send_changed_paths, strict_node, include_merged_revisions;
2438362181Sdim  apr_array_header_t *full_paths, *revprops;
2439362181Sdim  svn_ra_svn__list_t *paths, *revprop_items;
2440251881Speter  char *revprop_word;
2441362181Sdim  svn_ra_svn__item_t *elt;
2442251881Speter  int i;
2443251881Speter  apr_uint64_t limit, include_merged_revs_param;
2444251881Speter  log_baton_t lb;
2445251881Speter  authz_baton_t ab;
2446251881Speter
2447251881Speter  ab.server = b;
2448251881Speter  ab.conn = conn;
2449251881Speter
2450362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "l(?r)(?r)bb?n?Bwl", &paths,
2451251881Speter                                  &start_rev, &end_rev, &send_changed_paths,
2452251881Speter                                  &strict_node, &limit,
2453251881Speter                                  &include_merged_revs_param,
2454251881Speter                                  &revprop_word, &revprop_items));
2455251881Speter
2456251881Speter  if (include_merged_revs_param == SVN_RA_SVN_UNSPECIFIED_NUMBER)
2457251881Speter    include_merged_revisions = FALSE;
2458251881Speter  else
2459251881Speter    include_merged_revisions = (svn_boolean_t) include_merged_revs_param;
2460251881Speter
2461251881Speter  if (revprop_word == NULL)
2462251881Speter    /* pre-1.5 client */
2463251881Speter    revprops = svn_compat_log_revprops_in(pool);
2464251881Speter  else if (strcmp(revprop_word, "all-revprops") == 0)
2465251881Speter    revprops = NULL;
2466251881Speter  else if (strcmp(revprop_word, "revprops") == 0)
2467251881Speter    {
2468251881Speter      SVN_ERR_ASSERT(revprop_items);
2469251881Speter
2470251881Speter      revprops = apr_array_make(pool, revprop_items->nelts,
2471251881Speter                                sizeof(char *));
2472251881Speter      for (i = 0; i < revprop_items->nelts; i++)
2473251881Speter        {
2474362181Sdim          elt = &SVN_RA_SVN__LIST_ITEM(revprop_items, i);
2475251881Speter          if (elt->kind != SVN_RA_SVN_STRING)
2476251881Speter            return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
2477251881Speter                                    _("Log revprop entry not a string"));
2478362181Sdim          APR_ARRAY_PUSH(revprops, const char *) = elt->u.string.data;
2479251881Speter        }
2480251881Speter    }
2481251881Speter  else
2482251881Speter    return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
2483251881Speter                             _("Unknown revprop word '%s' in log command"),
2484251881Speter                             revprop_word);
2485251881Speter
2486251881Speter  /* If we got an unspecified number then the user didn't send us anything,
2487251881Speter     so we assume no limit.  If it's larger than INT_MAX then someone is
2488251881Speter     messing with us, since we know the svn client libraries will never send
2489251881Speter     us anything that big, so play it safe and default to no limit. */
2490251881Speter  if (limit == SVN_RA_SVN_UNSPECIFIED_NUMBER || limit > INT_MAX)
2491251881Speter    limit = 0;
2492251881Speter
2493251881Speter  full_paths = apr_array_make(pool, paths->nelts, sizeof(const char *));
2494251881Speter  for (i = 0; i < paths->nelts; i++)
2495251881Speter    {
2496362181Sdim      elt = &SVN_RA_SVN__LIST_ITEM(paths, i);
2497251881Speter      if (elt->kind != SVN_RA_SVN_STRING)
2498251881Speter        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
2499251881Speter                                _("Log path entry not a string"));
2500362181Sdim      SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL,
2501362181Sdim                                            elt->u.string.data, pool, pool));
2502362181Sdim      full_path = svn_fspath__join(b->repository->fs_path->data,
2503362181Sdim                                   canonical_path, pool);
2504251881Speter      APR_ARRAY_PUSH(full_paths, const char *) = full_path;
2505251881Speter    }
2506251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
2507251881Speter
2508251881Speter  SVN_ERR(log_command(b, conn, pool, "%s",
2509251881Speter                      svn_log__log(full_paths, start_rev, end_rev,
2510251881Speter                                   (int) limit, send_changed_paths,
2511251881Speter                                   strict_node, include_merged_revisions,
2512251881Speter                                   revprops, pool)));
2513251881Speter
2514251881Speter  /* Get logs.  (Can't report errors back to the client at this point.) */
2515289180Speter  lb.fs_path = b->repository->fs_path->data;
2516251881Speter  lb.conn = conn;
2517251881Speter  lb.stack_depth = 0;
2518362181Sdim  lb.started = FALSE;
2519362181Sdim  err = svn_repos_get_logs5(b->repository->repos, full_paths, start_rev,
2520362181Sdim                            end_rev, (int) limit,
2521289180Speter                            strict_node, include_merged_revisions,
2522289180Speter                            revprops, authz_check_access_cb_func(b), &ab,
2523362181Sdim                            send_changed_paths ? path_change_receiver : NULL,
2524362181Sdim                            send_changed_paths ? &lb : NULL,
2525362181Sdim                            revision_receiver, &lb, pool);
2526251881Speter
2527251881Speter  write_err = svn_ra_svn__write_word(conn, pool, "done");
2528251881Speter  if (write_err)
2529251881Speter    {
2530251881Speter      svn_error_clear(err);
2531251881Speter      return write_err;
2532251881Speter    }
2533251881Speter  SVN_CMD_ERR(err);
2534251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
2535251881Speter  return SVN_NO_ERROR;
2536251881Speter}
2537251881Speter
2538362181Sdimstatic svn_error_t *
2539362181Sdimcheck_path(svn_ra_svn_conn_t *conn,
2540362181Sdim           apr_pool_t *pool,
2541362181Sdim           svn_ra_svn__list_t *params,
2542362181Sdim           void *baton)
2543251881Speter{
2544251881Speter  server_baton_t *b = baton;
2545251881Speter  svn_revnum_t rev;
2546362181Sdim  const char *path, *full_path, *canonical_path;
2547251881Speter  svn_fs_root_t *root;
2548251881Speter  svn_node_kind_t kind;
2549251881Speter
2550362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)", &path, &rev));
2551362181Sdim  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
2552362181Sdim                                        pool, pool));;
2553289180Speter  full_path = svn_fspath__join(b->repository->fs_path->data,
2554362181Sdim                               canonical_path, pool);
2555251881Speter
2556251881Speter  /* Check authorizations */
2557251881Speter  SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
2558251881Speter                           full_path, FALSE));
2559251881Speter
2560251881Speter  if (!SVN_IS_VALID_REVNUM(rev))
2561289180Speter    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
2562251881Speter
2563251881Speter  SVN_ERR(log_command(b, conn, pool, "check-path %s@%d",
2564251881Speter                      svn_path_uri_encode(full_path, pool), rev));
2565251881Speter
2566289180Speter  SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool));
2567251881Speter  SVN_CMD_ERR(svn_fs_check_path(&kind, root, full_path, pool));
2568251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "w",
2569251881Speter                                         svn_node_kind_to_word(kind)));
2570251881Speter  return SVN_NO_ERROR;
2571251881Speter}
2572251881Speter
2573362181Sdimstatic svn_error_t *
2574362181Sdimstat_cmd(svn_ra_svn_conn_t *conn,
2575362181Sdim         apr_pool_t *pool,
2576362181Sdim         svn_ra_svn__list_t *params,
2577362181Sdim         void *baton)
2578251881Speter{
2579251881Speter  server_baton_t *b = baton;
2580251881Speter  svn_revnum_t rev;
2581362181Sdim  const char *path, *full_path, *cdate, *canonical_path;
2582251881Speter  svn_fs_root_t *root;
2583251881Speter  svn_dirent_t *dirent;
2584251881Speter
2585362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)", &path, &rev));
2586362181Sdim  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path, pool,
2587362181Sdim                                        pool));
2588362181Sdim  full_path = svn_fspath__join(b->repository->fs_path->data, canonical_path,
2589362181Sdim                               pool);
2590251881Speter
2591251881Speter  /* Check authorizations */
2592251881Speter  SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
2593251881Speter                           full_path, FALSE));
2594251881Speter
2595251881Speter  if (!SVN_IS_VALID_REVNUM(rev))
2596289180Speter    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
2597251881Speter
2598251881Speter  SVN_ERR(log_command(b, conn, pool, "stat %s@%d",
2599251881Speter                      svn_path_uri_encode(full_path, pool), rev));
2600251881Speter
2601289180Speter  SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool));
2602251881Speter  SVN_CMD_ERR(svn_repos_stat(&dirent, root, full_path, pool));
2603251881Speter
2604251881Speter  /* Need to return the equivalent of "(?l)", since that's what the
2605251881Speter     client is reading.  */
2606251881Speter
2607251881Speter  if (dirent == NULL)
2608251881Speter    {
2609251881Speter      SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "()"));
2610251881Speter      return SVN_NO_ERROR;
2611251881Speter    }
2612251881Speter
2613251881Speter  cdate = (dirent->time == (time_t) -1) ? NULL
2614251881Speter    : svn_time_to_cstring(dirent->time, pool);
2615251881Speter
2616251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "((wnbr(?c)(?c)))",
2617251881Speter                                         svn_node_kind_to_word(dirent->kind),
2618251881Speter                                         (apr_uint64_t) dirent->size,
2619251881Speter                                         dirent->has_props, dirent->created_rev,
2620251881Speter                                         cdate, dirent->last_author));
2621251881Speter
2622251881Speter  return SVN_NO_ERROR;
2623251881Speter}
2624251881Speter
2625362181Sdimstatic svn_error_t *
2626362181Sdimget_locations(svn_ra_svn_conn_t *conn,
2627362181Sdim              apr_pool_t *pool,
2628362181Sdim              svn_ra_svn__list_t *params,
2629362181Sdim              void *baton)
2630251881Speter{
2631251881Speter  svn_error_t *err, *write_err;
2632251881Speter  server_baton_t *b = baton;
2633251881Speter  svn_revnum_t revision;
2634362181Sdim  apr_array_header_t *location_revisions;
2635362181Sdim  svn_ra_svn__list_t *loc_revs_proto;
2636362181Sdim  svn_ra_svn__item_t *elt;
2637251881Speter  int i;
2638362181Sdim  const char *relative_path, *canonical_path;
2639251881Speter  svn_revnum_t peg_revision;
2640251881Speter  apr_hash_t *fs_locations;
2641251881Speter  const char *abs_path;
2642251881Speter  authz_baton_t ab;
2643251881Speter
2644251881Speter  ab.server = b;
2645251881Speter  ab.conn = conn;
2646251881Speter
2647251881Speter  /* Parse the arguments. */
2648362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "crl", &relative_path,
2649251881Speter                                  &peg_revision,
2650251881Speter                                  &loc_revs_proto));
2651362181Sdim  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, relative_path,
2652362181Sdim                                        pool, pool));
2653362181Sdim  relative_path = canonical_path;
2654251881Speter
2655289180Speter  abs_path = svn_fspath__join(b->repository->fs_path->data, relative_path,
2656289180Speter                              pool);
2657251881Speter
2658251881Speter  location_revisions = apr_array_make(pool, loc_revs_proto->nelts,
2659251881Speter                                      sizeof(svn_revnum_t));
2660251881Speter  for (i = 0; i < loc_revs_proto->nelts; i++)
2661251881Speter    {
2662362181Sdim      elt = &SVN_RA_SVN__LIST_ITEM(loc_revs_proto, i);
2663251881Speter      if (elt->kind != SVN_RA_SVN_NUMBER)
2664251881Speter        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
2665251881Speter                                "Get-locations location revisions entry "
2666251881Speter                                "not a revision number");
2667251881Speter      revision = (svn_revnum_t)(elt->u.number);
2668251881Speter      APR_ARRAY_PUSH(location_revisions, svn_revnum_t) = revision;
2669251881Speter    }
2670251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
2671251881Speter  SVN_ERR(log_command(b, conn, pool, "%s",
2672251881Speter                      svn_log__get_locations(abs_path, peg_revision,
2673251881Speter                                             location_revisions, pool)));
2674251881Speter
2675251881Speter  /* All the parameters are fine - let's perform the query against the
2676251881Speter   * repository. */
2677251881Speter
2678251881Speter  /* We store both err and write_err here, so the client will get
2679251881Speter   * the "done" even if there was an error in fetching the results. */
2680251881Speter
2681289180Speter  err = svn_repos_trace_node_locations(b->repository->fs, &fs_locations,
2682289180Speter                                       abs_path, peg_revision,
2683289180Speter                                       location_revisions,
2684251881Speter                                       authz_check_access_cb_func(b), &ab,
2685251881Speter                                       pool);
2686251881Speter
2687251881Speter  /* Now, write the results to the connection. */
2688251881Speter  if (!err)
2689251881Speter    {
2690251881Speter      if (fs_locations)
2691251881Speter        {
2692251881Speter          apr_hash_index_t *iter;
2693251881Speter
2694251881Speter          for (iter = apr_hash_first(pool, fs_locations); iter;
2695251881Speter              iter = apr_hash_next(iter))
2696251881Speter            {
2697289180Speter              const svn_revnum_t *iter_key = apr_hash_this_key(iter);
2698289180Speter              const char *iter_value = apr_hash_this_val(iter);
2699251881Speter
2700251881Speter              SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "rc",
2701251881Speter                                              *iter_key, iter_value));
2702251881Speter            }
2703251881Speter        }
2704251881Speter    }
2705251881Speter
2706251881Speter  write_err = svn_ra_svn__write_word(conn, pool, "done");
2707251881Speter  if (write_err)
2708251881Speter    {
2709251881Speter      svn_error_clear(err);
2710251881Speter      return write_err;
2711251881Speter    }
2712251881Speter  SVN_CMD_ERR(err);
2713251881Speter
2714251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
2715251881Speter
2716251881Speter  return SVN_NO_ERROR;
2717251881Speter}
2718251881Speter
2719251881Speterstatic svn_error_t *gls_receiver(svn_location_segment_t *segment,
2720251881Speter                                 void *baton,
2721251881Speter                                 apr_pool_t *pool)
2722251881Speter{
2723251881Speter  svn_ra_svn_conn_t *conn = baton;
2724251881Speter  return svn_ra_svn__write_tuple(conn, pool, "rr(?c)",
2725251881Speter                                 segment->range_start,
2726251881Speter                                 segment->range_end,
2727251881Speter                                 segment->path);
2728251881Speter}
2729251881Speter
2730362181Sdimstatic svn_error_t *
2731362181Sdimget_location_segments(svn_ra_svn_conn_t *conn,
2732362181Sdim                      apr_pool_t *pool,
2733362181Sdim                      svn_ra_svn__list_t *params,
2734362181Sdim                      void *baton)
2735251881Speter{
2736251881Speter  svn_error_t *err, *write_err;
2737251881Speter  server_baton_t *b = baton;
2738251881Speter  svn_revnum_t peg_revision, start_rev, end_rev;
2739362181Sdim  const char *relative_path, *canonical_path;
2740251881Speter  const char *abs_path;
2741251881Speter  authz_baton_t ab;
2742251881Speter
2743251881Speter  ab.server = b;
2744251881Speter  ab.conn = conn;
2745251881Speter
2746251881Speter  /* Parse the arguments. */
2747362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)(?r)(?r)",
2748251881Speter                                  &relative_path, &peg_revision,
2749251881Speter                                  &start_rev, &end_rev));
2750362181Sdim  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, relative_path,
2751362181Sdim                                        pool, pool));
2752362181Sdim  relative_path = canonical_path;
2753251881Speter
2754289180Speter  abs_path = svn_fspath__join(b->repository->fs_path->data, relative_path,
2755289180Speter                              pool);
2756251881Speter
2757286506Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
2758286506Speter  SVN_ERR(log_command(baton, conn, pool, "%s",
2759286506Speter                      svn_log__get_location_segments(abs_path, peg_revision,
2760286506Speter                                                     start_rev, end_rev,
2761286506Speter                                                     pool)));
2762286506Speter
2763286506Speter  /* No START_REV or PEG_REVISION?  We'll use HEAD. */
2764286506Speter  if (!SVN_IS_VALID_REVNUM(start_rev) || !SVN_IS_VALID_REVNUM(peg_revision))
2765251881Speter    {
2766286506Speter      svn_revnum_t youngest;
2767286506Speter
2768289180Speter      err = svn_fs_youngest_rev(&youngest, b->repository->fs, pool);
2769286506Speter
2770289180Speter      if (err)
2771289180Speter        {
2772289180Speter          err = svn_error_compose_create(
2773289180Speter                    svn_ra_svn__write_word(conn, pool, "done"),
2774289180Speter                    err);
2775289180Speter
2776289180Speter          return log_fail_and_flush(err, b, conn, pool);
2777289180Speter        }
2778289180Speter
2779286506Speter      if (!SVN_IS_VALID_REVNUM(start_rev))
2780286506Speter        start_rev = youngest;
2781286506Speter      if (!SVN_IS_VALID_REVNUM(peg_revision))
2782286506Speter        peg_revision = youngest;
2783286506Speter    }
2784286506Speter
2785286506Speter  /* No END_REV?  We'll use 0. */
2786286506Speter  if (!SVN_IS_VALID_REVNUM(end_rev))
2787286506Speter    end_rev = 0;
2788286506Speter
2789286506Speter  if (end_rev > start_rev)
2790286506Speter    {
2791289180Speter      err = svn_ra_svn__write_word(conn, pool, "done");
2792362181Sdim      err = svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, err,
2793251881Speter                              "Get-location-segments end revision must not be "
2794251881Speter                              "younger than start revision");
2795251881Speter      return log_fail_and_flush(err, b, conn, pool);
2796251881Speter    }
2797251881Speter
2798286506Speter  if (start_rev > peg_revision)
2799251881Speter    {
2800289180Speter      err = svn_ra_svn__write_word(conn, pool, "done");
2801362181Sdim      err = svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, err,
2802251881Speter                              "Get-location-segments start revision must not "
2803251881Speter                              "be younger than peg revision");
2804251881Speter      return log_fail_and_flush(err, b, conn, pool);
2805251881Speter    }
2806251881Speter
2807251881Speter  /* All the parameters are fine - let's perform the query against the
2808251881Speter   * repository. */
2809251881Speter
2810251881Speter  /* We store both err and write_err here, so the client will get
2811251881Speter   * the "done" even if there was an error in fetching the results. */
2812251881Speter
2813289180Speter  err = svn_repos_node_location_segments(b->repository->repos, abs_path,
2814251881Speter                                         peg_revision, start_rev, end_rev,
2815251881Speter                                         gls_receiver, (void *)conn,
2816251881Speter                                         authz_check_access_cb_func(b), &ab,
2817251881Speter                                         pool);
2818251881Speter  write_err = svn_ra_svn__write_word(conn, pool, "done");
2819251881Speter  if (write_err)
2820251881Speter    {
2821289180Speter      return svn_error_compose_create(write_err, err);
2822251881Speter    }
2823251881Speter  SVN_CMD_ERR(err);
2824251881Speter
2825251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
2826251881Speter
2827251881Speter  return SVN_NO_ERROR;
2828251881Speter}
2829251881Speter
2830251881Speter/* This implements svn_write_fn_t.  Write LEN bytes starting at DATA to the
2831251881Speter   client as a string. */
2832251881Speterstatic svn_error_t *svndiff_handler(void *baton, const char *data,
2833251881Speter                                    apr_size_t *len)
2834251881Speter{
2835251881Speter  file_revs_baton_t *b = baton;
2836251881Speter  svn_string_t str;
2837251881Speter
2838251881Speter  str.data = data;
2839251881Speter  str.len = *len;
2840251881Speter  return svn_ra_svn__write_string(b->conn, b->pool, &str);
2841251881Speter}
2842251881Speter
2843251881Speter/* This implements svn_close_fn_t.  Mark the end of the data by writing an
2844251881Speter   empty string to the client. */
2845251881Speterstatic svn_error_t *svndiff_close_handler(void *baton)
2846251881Speter{
2847251881Speter  file_revs_baton_t *b = baton;
2848251881Speter
2849251881Speter  SVN_ERR(svn_ra_svn__write_cstring(b->conn, b->pool, ""));
2850251881Speter  return SVN_NO_ERROR;
2851251881Speter}
2852251881Speter
2853251881Speter/* This implements the svn_repos_file_rev_handler_t interface. */
2854251881Speterstatic svn_error_t *file_rev_handler(void *baton, const char *path,
2855251881Speter                                     svn_revnum_t rev, apr_hash_t *rev_props,
2856251881Speter                                     svn_boolean_t merged_revision,
2857251881Speter                                     svn_txdelta_window_handler_t *d_handler,
2858251881Speter                                     void **d_baton,
2859251881Speter                                     apr_array_header_t *prop_diffs,
2860251881Speter                                     apr_pool_t *pool)
2861251881Speter{
2862251881Speter  file_revs_baton_t *frb = baton;
2863251881Speter  svn_stream_t *stream;
2864251881Speter
2865251881Speter  SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "cr(!",
2866251881Speter                                  path, rev));
2867251881Speter  SVN_ERR(svn_ra_svn__write_proplist(frb->conn, pool, rev_props));
2868251881Speter  SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "!)(!"));
2869251881Speter  SVN_ERR(write_prop_diffs(frb->conn, pool, prop_diffs));
2870251881Speter  SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "!)b", merged_revision));
2871251881Speter
2872251881Speter  /* Store the pool for the delta stream. */
2873251881Speter  frb->pool = pool;
2874251881Speter
2875251881Speter  /* Prepare for the delta or just write an empty string. */
2876251881Speter  if (d_handler)
2877251881Speter    {
2878251881Speter      stream = svn_stream_create(baton, pool);
2879251881Speter      svn_stream_set_write(stream, svndiff_handler);
2880251881Speter      svn_stream_set_close(stream, svndiff_close_handler);
2881251881Speter
2882362181Sdim      svn_txdelta_to_svndiff3(d_handler, d_baton, stream,
2883362181Sdim                              svn_ra_svn__svndiff_version(frb->conn),
2884362181Sdim                              svn_ra_svn_compression_level(frb->conn), pool);
2885251881Speter    }
2886251881Speter  else
2887251881Speter    SVN_ERR(svn_ra_svn__write_cstring(frb->conn, pool, ""));
2888251881Speter
2889251881Speter  return SVN_NO_ERROR;
2890251881Speter}
2891251881Speter
2892362181Sdimstatic svn_error_t *
2893362181Sdimget_file_revs(svn_ra_svn_conn_t *conn,
2894362181Sdim              apr_pool_t *pool,
2895362181Sdim              svn_ra_svn__list_t *params,
2896362181Sdim              void *baton)
2897251881Speter{
2898251881Speter  server_baton_t *b = baton;
2899251881Speter  svn_error_t *err, *write_err;
2900251881Speter  file_revs_baton_t frb;
2901251881Speter  svn_revnum_t start_rev, end_rev;
2902251881Speter  const char *path;
2903251881Speter  const char *full_path;
2904362181Sdim  const char *canonical_path;
2905251881Speter  apr_uint64_t include_merged_revs_param;
2906251881Speter  svn_boolean_t include_merged_revisions;
2907251881Speter  authz_baton_t ab;
2908251881Speter
2909251881Speter  ab.server = b;
2910251881Speter  ab.conn = conn;
2911251881Speter
2912251881Speter  /* Parse arguments. */
2913362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)(?r)?B",
2914251881Speter                                  &path, &start_rev, &end_rev,
2915251881Speter                                  &include_merged_revs_param));
2916362181Sdim  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
2917362181Sdim                                        pool, pool));
2918362181Sdim  path = canonical_path;
2919251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
2920289180Speter  full_path = svn_fspath__join(b->repository->fs_path->data, path, pool);
2921251881Speter
2922251881Speter  if (include_merged_revs_param == SVN_RA_SVN_UNSPECIFIED_NUMBER)
2923251881Speter    include_merged_revisions = FALSE;
2924251881Speter  else
2925251881Speter    include_merged_revisions = (svn_boolean_t) include_merged_revs_param;
2926251881Speter
2927251881Speter  SVN_ERR(log_command(b, conn, pool, "%s",
2928251881Speter                      svn_log__get_file_revs(full_path, start_rev, end_rev,
2929251881Speter                                             include_merged_revisions,
2930251881Speter                                             pool)));
2931251881Speter
2932251881Speter  frb.conn = conn;
2933251881Speter  frb.pool = NULL;
2934251881Speter
2935289180Speter  err = svn_repos_get_file_revs2(b->repository->repos, full_path, start_rev,
2936289180Speter                                 end_rev, include_merged_revisions,
2937251881Speter                                 authz_check_access_cb_func(b), &ab,
2938251881Speter                                 file_rev_handler, &frb, pool);
2939251881Speter  write_err = svn_ra_svn__write_word(conn, pool, "done");
2940251881Speter  if (write_err)
2941251881Speter    {
2942251881Speter      svn_error_clear(err);
2943251881Speter      return write_err;
2944251881Speter    }
2945251881Speter  SVN_CMD_ERR(err);
2946251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
2947251881Speter
2948251881Speter  return SVN_NO_ERROR;
2949251881Speter}
2950251881Speter
2951362181Sdimstatic svn_error_t *
2952362181Sdimlock(svn_ra_svn_conn_t *conn,
2953362181Sdim     apr_pool_t *pool,
2954362181Sdim     svn_ra_svn__list_t *params,
2955362181Sdim     void *baton)
2956251881Speter{
2957251881Speter  server_baton_t *b = baton;
2958251881Speter  const char *path;
2959251881Speter  const char *comment;
2960251881Speter  const char *full_path;
2961362181Sdim  const char *canonical_path;
2962251881Speter  svn_boolean_t steal_lock;
2963251881Speter  svn_revnum_t current_rev;
2964251881Speter  svn_lock_t *l;
2965251881Speter
2966362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?c)b(?r)", &path, &comment,
2967251881Speter                                  &steal_lock, &current_rev));
2968362181Sdim  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
2969362181Sdim                                        pool, pool));;
2970289180Speter  full_path = svn_fspath__join(b->repository->fs_path->data,
2971362181Sdim                               canonical_path, pool);
2972251881Speter
2973251881Speter  SVN_ERR(must_have_access(conn, pool, b, svn_authz_write,
2974251881Speter                           full_path, TRUE));
2975251881Speter  SVN_ERR(log_command(b, conn, pool, "%s",
2976251881Speter                      svn_log__lock_one_path(full_path, steal_lock, pool)));
2977251881Speter
2978289180Speter  SVN_CMD_ERR(svn_repos_fs_lock(&l, b->repository->repos, full_path, NULL,
2979289180Speter                                comment, 0, 0, /* No expiration time. */
2980251881Speter                                current_rev, steal_lock, pool));
2981251881Speter
2982251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(!", "success"));
2983251881Speter  SVN_ERR(write_lock(conn, pool, l));
2984251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)"));
2985251881Speter
2986251881Speter  return SVN_NO_ERROR;
2987251881Speter}
2988251881Speter
2989289180Speterstruct lock_result_t {
2990289180Speter  const svn_lock_t *lock;
2991289180Speter  svn_error_t *err;
2992289180Speter};
2993289180Speter
2994289180Speterstruct lock_many_baton_t {
2995289180Speter  apr_hash_t *results;
2996289180Speter  apr_pool_t *pool;
2997289180Speter};
2998289180Speter
2999289180Speter/* Implements svn_fs_lock_callback_t. */
3000289180Speterstatic svn_error_t *
3001289180Speterlock_many_cb(void *baton,
3002289180Speter             const char *path,
3003289180Speter             const svn_lock_t *fs_lock,
3004289180Speter             svn_error_t *fs_err,
3005289180Speter             apr_pool_t *pool)
3006289180Speter{
3007289180Speter  struct lock_many_baton_t *b = baton;
3008289180Speter  struct lock_result_t *result = apr_palloc(b->pool,
3009289180Speter                                            sizeof(struct lock_result_t));
3010289180Speter
3011289180Speter  result->lock = fs_lock;
3012289180Speter  result->err = svn_error_dup(fs_err);
3013289180Speter  svn_hash_sets(b->results, apr_pstrdup(b->pool, path), result);
3014289180Speter
3015289180Speter  return SVN_NO_ERROR;
3016289180Speter}
3017289180Speter
3018289180Speterstatic void
3019289180Speterclear_lock_result_hash(apr_hash_t *results,
3020289180Speter                       apr_pool_t *scratch_pool)
3021289180Speter{
3022289180Speter  apr_hash_index_t *hi;
3023289180Speter
3024289180Speter  for (hi = apr_hash_first(scratch_pool, results); hi; hi = apr_hash_next(hi))
3025289180Speter    {
3026289180Speter      struct lock_result_t *result = apr_hash_this_val(hi);
3027289180Speter      svn_error_clear(result->err);
3028289180Speter    }
3029289180Speter}
3030289180Speter
3031362181Sdimstatic svn_error_t *
3032362181Sdimlock_many(svn_ra_svn_conn_t *conn,
3033362181Sdim          apr_pool_t *pool,
3034362181Sdim          svn_ra_svn__list_t *params,
3035362181Sdim          void *baton)
3036251881Speter{
3037251881Speter  server_baton_t *b = baton;
3038362181Sdim  svn_ra_svn__list_t *path_revs;
3039251881Speter  const char *comment;
3040251881Speter  svn_boolean_t steal_lock;
3041251881Speter  int i;
3042251881Speter  apr_pool_t *subpool;
3043289180Speter  svn_error_t *err, *write_err = SVN_NO_ERROR;
3044289180Speter  apr_hash_t *targets = apr_hash_make(pool);
3045289180Speter  apr_hash_t *authz_results = apr_hash_make(pool);
3046289180Speter  apr_hash_index_t *hi;
3047289180Speter  struct lock_many_baton_t lmb;
3048251881Speter
3049362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "(?c)bl", &comment, &steal_lock,
3050251881Speter                                  &path_revs));
3051251881Speter
3052251881Speter  subpool = svn_pool_create(pool);
3053251881Speter
3054251881Speter  /* Because we can only send a single auth reply per request, we send
3055251881Speter     a reply before parsing the lock commands.  This means an authz
3056251881Speter     access denial will abort the processing of the locks and return
3057251881Speter     an error. */
3058251881Speter  SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, TRUE));
3059251881Speter
3060289180Speter  /* Parse the lock requests from PATH_REVS into TARGETS. */
3061251881Speter  for (i = 0; i < path_revs->nelts; ++i)
3062251881Speter    {
3063362181Sdim      const char *path, *full_path, *canonical_path;
3064289180Speter      svn_revnum_t current_rev;
3065362181Sdim      svn_ra_svn__item_t *item = &SVN_RA_SVN__LIST_ITEM(path_revs, i);
3066289180Speter      svn_fs_lock_target_t *target;
3067251881Speter
3068251881Speter      svn_pool_clear(subpool);
3069251881Speter
3070251881Speter      if (item->kind != SVN_RA_SVN_LIST)
3071251881Speter        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
3072251881Speter                                "Lock requests should be list of lists");
3073251881Speter
3074362181Sdim      SVN_ERR(svn_ra_svn__parse_tuple(&item->u.list, "c(?r)", &path,
3075251881Speter                                      &current_rev));
3076251881Speter
3077362181Sdim      SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
3078362181Sdim                                            subpool, subpool));
3079289180Speter      full_path = svn_fspath__join(b->repository->fs_path->data,
3080362181Sdim                                   canonical_path, pool);
3081289180Speter      target = svn_fs_lock_target_create(NULL, current_rev, pool);
3082251881Speter
3083289180Speter      /* Any duplicate paths, once canonicalized, get collapsed into a
3084289180Speter         single path that is processed once.  The result is then
3085289180Speter         returned multiple times. */
3086289180Speter      svn_hash_sets(targets, full_path, target);
3087289180Speter    }
3088289180Speter
3089289180Speter  SVN_ERR(log_command(b, conn, subpool, "%s",
3090289180Speter                      svn_log__lock(targets, steal_lock, subpool)));
3091289180Speter
3092289180Speter  /* Check authz.
3093289180Speter
3094289180Speter     Note: From here on we need to make sure any errors in authz_results, or
3095289180Speter     results, are cleared before returning from this function. */
3096289180Speter  for (hi = apr_hash_first(pool, targets); hi; hi = apr_hash_next(hi))
3097289180Speter    {
3098289180Speter      const char *full_path = apr_hash_this_key(hi);
3099289180Speter
3100289180Speter      svn_pool_clear(subpool);
3101289180Speter
3102289180Speter      if (! lookup_access(subpool, b, svn_authz_write, full_path, TRUE))
3103251881Speter        {
3104289180Speter          struct lock_result_t *result
3105289180Speter            = apr_palloc(pool, sizeof(struct lock_result_t));
3106289180Speter
3107289180Speter          result->lock = NULL;
3108289180Speter          result->err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED,
3109289180Speter                                             NULL, NULL, b);
3110289180Speter          svn_hash_sets(authz_results, full_path, result);
3111289180Speter          svn_hash_sets(targets, full_path, NULL);
3112251881Speter        }
3113289180Speter    }
3114251881Speter
3115289180Speter  lmb.results = apr_hash_make(pool);
3116289180Speter  lmb.pool = pool;
3117251881Speter
3118289180Speter  err = svn_repos_fs_lock_many(b->repository->repos, targets,
3119289180Speter                               comment, FALSE,
3120289180Speter                               0, /* No expiration time. */
3121289180Speter                               steal_lock, lock_many_cb, &lmb,
3122289180Speter                               pool, subpool);
3123289180Speter
3124289180Speter  /* Return results in the same order as the paths were supplied. */
3125289180Speter  for (i = 0; i < path_revs->nelts; ++i)
3126289180Speter    {
3127362181Sdim      const char *path, *full_path, *canonical_path;
3128289180Speter      svn_revnum_t current_rev;
3129362181Sdim      svn_ra_svn__item_t *item = &SVN_RA_SVN__LIST_ITEM(path_revs, i);
3130289180Speter      struct lock_result_t *result;
3131289180Speter
3132289180Speter      svn_pool_clear(subpool);
3133289180Speter
3134362181Sdim      write_err = svn_ra_svn__parse_tuple(&item->u.list, "c(?r)",
3135362181Sdim                                          &path, &current_rev);
3136289180Speter      if (write_err)
3137289180Speter        break;
3138289180Speter
3139362181Sdim      SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
3140362181Sdim                                            subpool, subpool));
3141289180Speter      full_path = svn_fspath__join(b->repository->fs_path->data,
3142362181Sdim                                   canonical_path, subpool);
3143289180Speter
3144289180Speter      result = svn_hash_gets(lmb.results, full_path);
3145289180Speter      if (!result)
3146289180Speter        result = svn_hash_gets(authz_results, full_path);
3147289180Speter      if (!result)
3148251881Speter        {
3149289180Speter          /* No result?  Something really odd happened, create a
3150289180Speter             placeholder error so that any other results can be
3151289180Speter             reported in the correct order. */
3152289180Speter          result = apr_palloc(pool, sizeof(struct lock_result_t));
3153289180Speter          result->err = svn_error_createf(SVN_ERR_FS_LOCK_OPERATION_FAILED, 0,
3154289180Speter                                          _("No result for '%s'."), path);
3155289180Speter          svn_hash_sets(lmb.results, full_path, result);
3156251881Speter        }
3157289180Speter
3158289180Speter      if (result->err)
3159289180Speter        write_err = svn_ra_svn__write_cmd_failure(conn, subpool,
3160289180Speter                                                  result->err);
3161251881Speter      else
3162251881Speter        {
3163289180Speter          write_err = svn_ra_svn__write_tuple(conn, subpool,
3164289180Speter                                              "w!", "success");
3165289180Speter          if (!write_err)
3166289180Speter            write_err = write_lock(conn, subpool, result->lock);
3167289180Speter          if (!write_err)
3168289180Speter            write_err = svn_ra_svn__write_tuple(conn, subpool, "!");
3169251881Speter        }
3170289180Speter      if (write_err)
3171289180Speter        break;
3172251881Speter    }
3173251881Speter
3174289180Speter  clear_lock_result_hash(authz_results, subpool);
3175289180Speter  clear_lock_result_hash(lmb.results, subpool);
3176289180Speter
3177251881Speter  svn_pool_destroy(subpool);
3178251881Speter
3179251881Speter  if (!write_err)
3180289180Speter    write_err = svn_ra_svn__write_word(conn, pool, "done");
3181289180Speter  if (!write_err)
3182251881Speter    SVN_CMD_ERR(err);
3183251881Speter  svn_error_clear(err);
3184251881Speter  SVN_ERR(write_err);
3185251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
3186251881Speter
3187251881Speter  return SVN_NO_ERROR;
3188251881Speter}
3189251881Speter
3190362181Sdimstatic svn_error_t *
3191362181Sdimunlock(svn_ra_svn_conn_t *conn,
3192362181Sdim       apr_pool_t *pool,
3193362181Sdim       svn_ra_svn__list_t *params,
3194362181Sdim       void *baton)
3195251881Speter{
3196251881Speter  server_baton_t *b = baton;
3197362181Sdim  const char *path, *token, *full_path, *canonical_path;
3198251881Speter  svn_boolean_t break_lock;
3199251881Speter
3200362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?c)b", &path, &token,
3201251881Speter                                 &break_lock));
3202251881Speter
3203362181Sdim  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
3204362181Sdim                                        pool, pool));
3205289180Speter  full_path = svn_fspath__join(b->repository->fs_path->data,
3206362181Sdim                               canonical_path, pool);
3207251881Speter
3208251881Speter  /* Username required unless break_lock was specified. */
3209251881Speter  SVN_ERR(must_have_access(conn, pool, b, svn_authz_write,
3210251881Speter                           full_path, ! break_lock));
3211251881Speter  SVN_ERR(log_command(b, conn, pool, "%s",
3212251881Speter                      svn_log__unlock_one_path(full_path, break_lock, pool)));
3213251881Speter
3214289180Speter  SVN_CMD_ERR(svn_repos_fs_unlock(b->repository->repos, full_path, token,
3215289180Speter                                  break_lock, pool));
3216251881Speter
3217251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
3218251881Speter
3219251881Speter  return SVN_NO_ERROR;
3220251881Speter}
3221251881Speter
3222362181Sdimstatic svn_error_t *
3223362181Sdimunlock_many(svn_ra_svn_conn_t *conn,
3224362181Sdim            apr_pool_t *pool,
3225362181Sdim            svn_ra_svn__list_t *params,
3226362181Sdim            void *baton)
3227251881Speter{
3228251881Speter  server_baton_t *b = baton;
3229251881Speter  svn_boolean_t break_lock;
3230362181Sdim  svn_ra_svn__list_t *unlock_tokens;
3231251881Speter  int i;
3232251881Speter  apr_pool_t *subpool;
3233289180Speter  svn_error_t *err = SVN_NO_ERROR, *write_err = SVN_NO_ERROR;
3234289180Speter  apr_hash_t *targets = apr_hash_make(pool);
3235289180Speter  apr_hash_t *authz_results = apr_hash_make(pool);
3236289180Speter  apr_hash_index_t *hi;
3237289180Speter  struct lock_many_baton_t lmb;
3238251881Speter
3239362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "bl", &break_lock, &unlock_tokens));
3240251881Speter
3241251881Speter  /* Username required unless break_lock was specified. */
3242251881Speter  SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, ! break_lock));
3243251881Speter
3244251881Speter  subpool = svn_pool_create(pool);
3245251881Speter
3246289180Speter  /* Parse the unlock requests from PATH_REVS into TARGETS. */
3247251881Speter  for (i = 0; i < unlock_tokens->nelts; i++)
3248251881Speter    {
3249362181Sdim      svn_ra_svn__item_t *item = &SVN_RA_SVN__LIST_ITEM(unlock_tokens, i);
3250362181Sdim      const char *path, *full_path, *token, *canonical_path;
3251251881Speter
3252251881Speter      svn_pool_clear(subpool);
3253251881Speter
3254251881Speter      if (item->kind != SVN_RA_SVN_LIST)
3255251881Speter        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
3256251881Speter                                "Unlock request should be a list of lists");
3257251881Speter
3258362181Sdim      SVN_ERR(svn_ra_svn__parse_tuple(&item->u.list, "c(?c)", &path,
3259251881Speter                                      &token));
3260289180Speter      if (!token)
3261289180Speter        token = "";
3262251881Speter
3263362181Sdim      SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
3264362181Sdim                                            subpool, subpool));
3265289180Speter      full_path = svn_fspath__join(b->repository->fs_path->data,
3266362181Sdim                                   canonical_path, pool);
3267251881Speter
3268289180Speter      /* Any duplicate paths, once canonicalized, get collapsed into a
3269289180Speter         single path that is processed once.  The result is then
3270289180Speter         returned multiple times. */
3271289180Speter      svn_hash_sets(targets, full_path, token);
3272289180Speter    }
3273289180Speter
3274289180Speter  SVN_ERR(log_command(b, conn, subpool, "%s",
3275289180Speter                      svn_log__unlock(targets, break_lock, subpool)));
3276289180Speter
3277289180Speter  /* Check authz.
3278289180Speter
3279289180Speter     Note: From here on we need to make sure any errors in authz_results, or
3280289180Speter     results, are cleared before returning from this function. */
3281289180Speter  for (hi = apr_hash_first(pool, targets); hi; hi = apr_hash_next(hi))
3282289180Speter    {
3283289180Speter      const char *full_path = apr_hash_this_key(hi);
3284289180Speter
3285289180Speter      svn_pool_clear(subpool);
3286289180Speter
3287289180Speter      if (! lookup_access(subpool, b, svn_authz_write, full_path,
3288251881Speter                          ! break_lock))
3289289180Speter        {
3290289180Speter          struct lock_result_t *result
3291289180Speter            = apr_palloc(pool, sizeof(struct lock_result_t));
3292251881Speter
3293289180Speter          result->lock = NULL;
3294289180Speter          result->err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED,
3295289180Speter                                             NULL, NULL, b);
3296289180Speter          svn_hash_sets(authz_results, full_path, result);
3297289180Speter          svn_hash_sets(targets, full_path, NULL);
3298289180Speter        }
3299289180Speter    }
3300289180Speter
3301289180Speter  lmb.results = apr_hash_make(pool);
3302289180Speter  lmb.pool = pool;
3303289180Speter
3304289180Speter  err = svn_repos_fs_unlock_many(b->repository->repos, targets,
3305289180Speter                                 break_lock, lock_many_cb, &lmb,
3306289180Speter                                 pool, subpool);
3307289180Speter
3308289180Speter  /* Return results in the same order as the paths were supplied. */
3309289180Speter  for (i = 0; i < unlock_tokens->nelts; ++i)
3310289180Speter    {
3311362181Sdim      const char *path, *token, *full_path, *canonical_path;
3312362181Sdim      svn_ra_svn__item_t *item = &SVN_RA_SVN__LIST_ITEM(unlock_tokens, i);
3313289180Speter      struct lock_result_t *result;
3314289180Speter
3315289180Speter      svn_pool_clear(subpool);
3316289180Speter
3317362181Sdim      write_err = svn_ra_svn__parse_tuple(&item->u.list, "c(?c)",
3318362181Sdim                                          &path, &token);
3319289180Speter      if (write_err)
3320289180Speter        break;
3321289180Speter
3322362181Sdim      SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
3323362181Sdim                                            subpool, subpool));
3324289180Speter      full_path = svn_fspath__join(b->repository->fs_path->data,
3325362181Sdim                                   canonical_path, pool);
3326289180Speter
3327289180Speter      result = svn_hash_gets(lmb.results, full_path);
3328289180Speter      if (!result)
3329289180Speter        result = svn_hash_gets(authz_results, full_path);
3330289180Speter      if (!result)
3331251881Speter        {
3332289180Speter          /* No result?  Something really odd happened, create a
3333289180Speter             placeholder error so that any other results can be
3334289180Speter             reported in the correct order. */
3335289180Speter          result = apr_palloc(pool, sizeof(struct lock_result_t));
3336289180Speter          result->err = svn_error_createf(SVN_ERR_FS_LOCK_OPERATION_FAILED, 0,
3337289180Speter                                          _("No result for '%s'."), path);
3338289180Speter          svn_hash_sets(lmb.results, full_path, result);
3339251881Speter        }
3340289180Speter
3341289180Speter      if (result->err)
3342289180Speter        write_err = svn_ra_svn__write_cmd_failure(conn, pool, result->err);
3343251881Speter      else
3344289180Speter        write_err = svn_ra_svn__write_tuple(conn, subpool, "w(c)", "success",
3345289180Speter                                            path);
3346289180Speter      if (write_err)
3347289180Speter        break;
3348251881Speter    }
3349251881Speter
3350289180Speter  clear_lock_result_hash(authz_results, subpool);
3351289180Speter  clear_lock_result_hash(lmb.results, subpool);
3352289180Speter
3353251881Speter  svn_pool_destroy(subpool);
3354251881Speter
3355289180Speter  if (!write_err)
3356289180Speter    write_err = svn_ra_svn__write_word(conn, pool, "done");
3357251881Speter  if (! write_err)
3358251881Speter    SVN_CMD_ERR(err);
3359251881Speter  svn_error_clear(err);
3360289180Speter  SVN_ERR(write_err);
3361251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
3362251881Speter
3363251881Speter  return SVN_NO_ERROR;
3364251881Speter}
3365251881Speter
3366362181Sdimstatic svn_error_t *
3367362181Sdimget_lock(svn_ra_svn_conn_t *conn,
3368362181Sdim         apr_pool_t *pool,
3369362181Sdim         svn_ra_svn__list_t *params,
3370362181Sdim         void *baton)
3371251881Speter{
3372251881Speter  server_baton_t *b = baton;
3373251881Speter  const char *path;
3374251881Speter  const char *full_path;
3375362181Sdim  const char *canonical_path;
3376251881Speter  svn_lock_t *l;
3377251881Speter
3378362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "c", &path));
3379251881Speter
3380362181Sdim  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
3381362181Sdim                                        pool, pool));
3382289180Speter  full_path = svn_fspath__join(b->repository->fs_path->data,
3383362181Sdim                               canonical_path, pool);
3384251881Speter
3385251881Speter  SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
3386251881Speter                           full_path, FALSE));
3387251881Speter  SVN_ERR(log_command(b, conn, pool, "get-lock %s",
3388251881Speter                      svn_path_uri_encode(full_path, pool)));
3389251881Speter
3390289180Speter  SVN_CMD_ERR(svn_fs_get_lock(&l, b->repository->fs, full_path, pool));
3391251881Speter
3392251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success"));
3393251881Speter  if (l)
3394251881Speter    SVN_ERR(write_lock(conn, pool, l));
3395251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));
3396251881Speter
3397251881Speter  return SVN_NO_ERROR;
3398251881Speter}
3399251881Speter
3400362181Sdimstatic svn_error_t *
3401362181Sdimget_locks(svn_ra_svn_conn_t *conn,
3402362181Sdim          apr_pool_t *pool,
3403362181Sdim          svn_ra_svn__list_t *params,
3404362181Sdim          void *baton)
3405251881Speter{
3406251881Speter  server_baton_t *b = baton;
3407251881Speter  const char *path;
3408251881Speter  const char *full_path;
3409362181Sdim  const char *canonical_path;
3410251881Speter  const char *depth_word;
3411251881Speter  svn_depth_t depth;
3412251881Speter  apr_hash_t *locks;
3413251881Speter  apr_hash_index_t *hi;
3414251881Speter  svn_error_t *err;
3415251881Speter  authz_baton_t ab;
3416251881Speter
3417251881Speter  ab.server = b;
3418251881Speter  ab.conn = conn;
3419251881Speter
3420362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "c?(?w)", &path, &depth_word));
3421251881Speter
3422251881Speter  depth = depth_word ? svn_depth_from_word(depth_word) : svn_depth_infinity;
3423251881Speter  if ((depth != svn_depth_empty) &&
3424251881Speter      (depth != svn_depth_files) &&
3425251881Speter      (depth != svn_depth_immediates) &&
3426251881Speter      (depth != svn_depth_infinity))
3427251881Speter    {
3428251881Speter      err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
3429251881Speter                             "Invalid 'depth' specified in get-locks request");
3430251881Speter      return log_fail_and_flush(err, b, conn, pool);
3431251881Speter    }
3432251881Speter
3433362181Sdim  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
3434362181Sdim                                        pool, pool));
3435289180Speter  full_path = svn_fspath__join(b->repository->fs_path->data,
3436362181Sdim                               canonical_path, pool);
3437251881Speter
3438251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
3439251881Speter
3440251881Speter  SVN_ERR(log_command(b, conn, pool, "get-locks %s",
3441251881Speter                      svn_path_uri_encode(full_path, pool)));
3442289180Speter  SVN_CMD_ERR(svn_repos_fs_get_locks2(&locks, b->repository->repos,
3443289180Speter                                      full_path, depth,
3444251881Speter                                      authz_check_access_cb_func(b), &ab,
3445251881Speter                                      pool));
3446251881Speter
3447251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success"));
3448251881Speter  for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi))
3449251881Speter    {
3450289180Speter      svn_lock_t *l = apr_hash_this_val(hi);
3451251881Speter
3452251881Speter      SVN_ERR(write_lock(conn, pool, l));
3453251881Speter    }
3454251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));
3455251881Speter
3456251881Speter  return SVN_NO_ERROR;
3457251881Speter}
3458251881Speter
3459251881Speterstatic svn_error_t *replay_one_revision(svn_ra_svn_conn_t *conn,
3460251881Speter                                        server_baton_t *b,
3461251881Speter                                        svn_revnum_t rev,
3462251881Speter                                        svn_revnum_t low_water_mark,
3463251881Speter                                        svn_boolean_t send_deltas,
3464251881Speter                                        apr_pool_t *pool)
3465251881Speter{
3466251881Speter  const svn_delta_editor_t *editor;
3467251881Speter  void *edit_baton;
3468251881Speter  svn_fs_root_t *root;
3469251881Speter  svn_error_t *err;
3470251881Speter  authz_baton_t ab;
3471251881Speter
3472251881Speter  ab.server = b;
3473251881Speter  ab.conn = conn;
3474251881Speter
3475251881Speter  SVN_ERR(log_command(b, conn, pool,
3476289180Speter                      svn_log__replay(b->repository->fs_path->data, rev,
3477289180Speter                                      pool)));
3478251881Speter
3479251881Speter  svn_ra_svn_get_editor(&editor, &edit_baton, conn, pool, NULL, NULL);
3480251881Speter
3481289180Speter  err = svn_fs_revision_root(&root, b->repository->fs, rev, pool);
3482251881Speter
3483251881Speter  if (! err)
3484289180Speter    err = svn_repos_replay2(root, b->repository->fs_path->data,
3485289180Speter                            low_water_mark, send_deltas, editor, edit_baton,
3486251881Speter                            authz_check_access_cb_func(b), &ab, pool);
3487251881Speter
3488251881Speter  if (err)
3489251881Speter    svn_error_clear(editor->abort_edit(edit_baton, pool));
3490251881Speter  SVN_CMD_ERR(err);
3491251881Speter
3492251881Speter  return svn_ra_svn__write_cmd_finish_replay(conn, pool);
3493251881Speter}
3494251881Speter
3495362181Sdimstatic svn_error_t *
3496362181Sdimreplay(svn_ra_svn_conn_t *conn,
3497362181Sdim       apr_pool_t *pool,
3498362181Sdim       svn_ra_svn__list_t *params,
3499362181Sdim       void *baton)
3500251881Speter{
3501251881Speter  svn_revnum_t rev, low_water_mark;
3502251881Speter  svn_boolean_t send_deltas;
3503251881Speter  server_baton_t *b = baton;
3504251881Speter
3505362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "rrb", &rev, &low_water_mark,
3506251881Speter                                 &send_deltas));
3507251881Speter
3508251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
3509251881Speter
3510251881Speter  SVN_ERR(replay_one_revision(conn, b, rev, low_water_mark,
3511251881Speter                              send_deltas, pool));
3512251881Speter
3513251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
3514251881Speter
3515251881Speter  return SVN_NO_ERROR;
3516251881Speter}
3517251881Speter
3518362181Sdimstatic svn_error_t *
3519362181Sdimreplay_range(svn_ra_svn_conn_t *conn,
3520362181Sdim             apr_pool_t *pool,
3521362181Sdim             svn_ra_svn__list_t *params,
3522362181Sdim             void *baton)
3523251881Speter{
3524251881Speter  svn_revnum_t start_rev, end_rev, rev, low_water_mark;
3525251881Speter  svn_boolean_t send_deltas;
3526251881Speter  server_baton_t *b = baton;
3527251881Speter  apr_pool_t *iterpool;
3528251881Speter  authz_baton_t ab;
3529251881Speter
3530251881Speter  ab.server = b;
3531251881Speter  ab.conn = conn;
3532251881Speter
3533362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "rrrb", &start_rev,
3534251881Speter                                 &end_rev, &low_water_mark,
3535251881Speter                                 &send_deltas));
3536251881Speter
3537251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
3538251881Speter
3539251881Speter  iterpool = svn_pool_create(pool);
3540251881Speter  for (rev = start_rev; rev <= end_rev; rev++)
3541251881Speter    {
3542251881Speter      apr_hash_t *props;
3543251881Speter
3544251881Speter      svn_pool_clear(iterpool);
3545251881Speter
3546289180Speter      SVN_CMD_ERR(svn_repos_fs_revision_proplist(&props,
3547289180Speter                                                 b->repository->repos, rev,
3548251881Speter                                                 authz_check_access_cb_func(b),
3549251881Speter                                                 &ab,
3550251881Speter                                                 iterpool));
3551251881Speter      SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "w(!", "revprops"));
3552251881Speter      SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, props));
3553251881Speter      SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!)"));
3554251881Speter
3555251881Speter      SVN_ERR(replay_one_revision(conn, b, rev, low_water_mark,
3556251881Speter                                  send_deltas, iterpool));
3557251881Speter
3558251881Speter    }
3559251881Speter  svn_pool_destroy(iterpool);
3560251881Speter
3561251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
3562251881Speter
3563251881Speter  return SVN_NO_ERROR;
3564251881Speter}
3565251881Speter
3566251881Speterstatic svn_error_t *
3567251881Speterget_deleted_rev(svn_ra_svn_conn_t *conn,
3568251881Speter                apr_pool_t *pool,
3569362181Sdim                svn_ra_svn__list_t *params,
3570251881Speter                void *baton)
3571251881Speter{
3572251881Speter  server_baton_t *b = baton;
3573362181Sdim  const char *path, *full_path, *canonical_path;
3574251881Speter  svn_revnum_t peg_revision;
3575251881Speter  svn_revnum_t end_revision;
3576251881Speter  svn_revnum_t revision_deleted;
3577251881Speter
3578362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "crr",
3579251881Speter                                 &path, &peg_revision, &end_revision));
3580362181Sdim  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
3581362181Sdim                                        pool, pool));
3582289180Speter  full_path = svn_fspath__join(b->repository->fs_path->data,
3583362181Sdim                               canonical_path, pool);
3584251881Speter  SVN_ERR(log_command(b, conn, pool, "get-deleted-rev"));
3585251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
3586362181Sdim  SVN_CMD_ERR(svn_repos_deleted_rev(b->repository->fs, full_path, peg_revision,
3587362181Sdim                                    end_revision, &revision_deleted, pool));
3588362181Sdim
3589362181Sdim  /* The protocol does not allow for a reply of SVN_INVALID_REVNUM directly.
3590362181Sdim     Instead, return SVN_ERR_ENTRY_MISSING_REVISION. A new enough client
3591362181Sdim     knows that this means the answer to the query is SVN_INVALID_REVNUM.
3592362181Sdim     (An older client reports this as an error.) */
3593362181Sdim  if (revision_deleted == SVN_INVALID_REVNUM)
3594362181Sdim    SVN_CMD_ERR(svn_error_createf(SVN_ERR_ENTRY_MISSING_REVISION, NULL,
3595362181Sdim                                  "svn protocol command 'get-deleted-rev': "
3596362181Sdim                                  "path '%s' was not deleted in r%ld-%ld; "
3597362181Sdim                                  "NOTE: newer clients handle this case "
3598362181Sdim                                  "and do not report it as an error",
3599362181Sdim                                  full_path, peg_revision, end_revision));
3600362181Sdim
3601251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", revision_deleted));
3602251881Speter  return SVN_NO_ERROR;
3603251881Speter}
3604251881Speter
3605251881Speterstatic svn_error_t *
3606251881Speterget_inherited_props(svn_ra_svn_conn_t *conn,
3607251881Speter                    apr_pool_t *pool,
3608362181Sdim                    svn_ra_svn__list_t *params,
3609251881Speter                    void *baton)
3610251881Speter{
3611251881Speter  server_baton_t *b = baton;
3612362181Sdim  const char *path, *full_path, *canonical_path;
3613251881Speter  svn_revnum_t rev;
3614251881Speter  svn_fs_root_t *root;
3615251881Speter  apr_array_header_t *inherited_props;
3616251881Speter  int i;
3617251881Speter  apr_pool_t *iterpool = svn_pool_create(pool);
3618251881Speter  authz_baton_t ab;
3619362181Sdim  svn_node_kind_t node_kind;
3620251881Speter
3621251881Speter  ab.server = b;
3622251881Speter  ab.conn = conn;
3623251881Speter
3624251881Speter  /* Parse arguments. */
3625362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)", &path, &rev));
3626251881Speter
3627362181Sdim  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
3628362181Sdim                                        iterpool, iterpool));
3629289180Speter  full_path = svn_fspath__join(b->repository->fs_path->data,
3630362181Sdim                               canonical_path, pool);
3631251881Speter
3632251881Speter  /* Check authorizations */
3633251881Speter  SVN_ERR(must_have_access(conn, iterpool, b, svn_authz_read,
3634251881Speter                           full_path, FALSE));
3635251881Speter
3636251881Speter  SVN_ERR(log_command(b, conn, pool, "%s",
3637251881Speter                      svn_log__get_inherited_props(full_path, rev,
3638251881Speter                                                   iterpool)));
3639251881Speter
3640251881Speter  /* Fetch the properties and a stream for the contents. */
3641289180Speter  SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, iterpool));
3642362181Sdim  SVN_CMD_ERR(svn_fs_check_path(&node_kind, root, full_path, pool));
3643362181Sdim  if (node_kind == svn_node_none)
3644362181Sdim    {
3645362181Sdim      SVN_CMD_ERR(svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
3646362181Sdim                                    _("'%s' path not found"), full_path));
3647362181Sdim    }
3648251881Speter  SVN_CMD_ERR(get_props(NULL, &inherited_props, &ab, root, full_path, pool));
3649251881Speter
3650251881Speter  /* Send successful command response with revision and props. */
3651251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "w(!", "success"));
3652251881Speter
3653251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(?!"));
3654251881Speter
3655251881Speter  for (i = 0; i < inherited_props->nelts; i++)
3656251881Speter    {
3657251881Speter      svn_prop_inherited_item_t *iprop =
3658251881Speter        APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *);
3659251881Speter
3660251881Speter      svn_pool_clear(iterpool);
3661251881Speter      SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!",
3662251881Speter                                      iprop->path_or_url));
3663251881Speter      SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash));
3664251881Speter      SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!",
3665251881Speter                                      iprop->path_or_url));
3666251881Speter    }
3667251881Speter
3668251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))"));
3669251881Speter  svn_pool_destroy(iterpool);
3670251881Speter  return SVN_NO_ERROR;
3671251881Speter}
3672251881Speter
3673362181Sdim/* Baton type to be used with list_receiver. */
3674362181Sdimtypedef struct list_receiver_baton_t
3675362181Sdim{
3676362181Sdim  /* Send the data through this connection. */
3677362181Sdim  svn_ra_svn_conn_t *conn;
3678362181Sdim
3679362181Sdim  /* Send the field selected by these flags. */
3680362181Sdim  apr_uint32_t dirent_fields;
3681362181Sdim} list_receiver_baton_t;
3682362181Sdim
3683362181Sdim/* Implements svn_repos_dirent_receiver_t, sending DIRENT and PATH to the
3684362181Sdim * client.  BATON must be a list_receiver_baton_t. */
3685362181Sdimstatic svn_error_t *
3686362181Sdimlist_receiver(const char *path,
3687362181Sdim              svn_dirent_t *dirent,
3688362181Sdim              void *baton,
3689362181Sdim              apr_pool_t *pool)
3690362181Sdim{
3691362181Sdim  list_receiver_baton_t *b = baton;
3692362181Sdim  return svn_error_trace(svn_ra_svn__write_dirent(b->conn, pool, path, dirent,
3693362181Sdim                                                  b->dirent_fields));
3694362181Sdim}
3695362181Sdim
3696362181Sdimstatic svn_error_t *
3697362181Sdimlist(svn_ra_svn_conn_t *conn,
3698362181Sdim     apr_pool_t *pool,
3699362181Sdim     svn_ra_svn__list_t *params,
3700362181Sdim     void *baton)
3701362181Sdim{
3702362181Sdim  server_baton_t *b = baton;
3703362181Sdim  const char *path, *full_path, *canonical_path;
3704362181Sdim  svn_revnum_t rev;
3705362181Sdim  svn_depth_t depth;
3706362181Sdim  apr_array_header_t *patterns = NULL;
3707362181Sdim  svn_fs_root_t *root;
3708362181Sdim  const char *depth_word;
3709362181Sdim  svn_boolean_t path_info_only;
3710362181Sdim  svn_ra_svn__list_t *dirent_fields_list = NULL;
3711362181Sdim  svn_ra_svn__list_t *patterns_list = NULL;
3712362181Sdim  int i;
3713362181Sdim  list_receiver_baton_t rb;
3714362181Sdim  svn_error_t *err, *write_err;
3715362181Sdim
3716362181Sdim  authz_baton_t ab;
3717362181Sdim  ab.server = b;
3718362181Sdim  ab.conn = conn;
3719362181Sdim
3720362181Sdim  /* Read the command parameters. */
3721362181Sdim  SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)w?l?l", &path, &rev,
3722362181Sdim                                  &depth_word, &dirent_fields_list,
3723362181Sdim                                  &patterns_list));
3724362181Sdim
3725362181Sdim  rb.conn = conn;
3726362181Sdim  SVN_ERR(parse_dirent_fields(&rb.dirent_fields, dirent_fields_list));
3727362181Sdim
3728362181Sdim  depth = svn_depth_from_word(depth_word);
3729362181Sdim  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
3730362181Sdim                                        pool, pool));
3731362181Sdim  full_path = svn_fspath__join(b->repository->fs_path->data,
3732362181Sdim                               canonical_path, pool);
3733362181Sdim
3734362181Sdim  /* Read the patterns list.  */
3735362181Sdim  if (patterns_list)
3736362181Sdim    {
3737362181Sdim      patterns = apr_array_make(pool, 0, sizeof(const char *));
3738362181Sdim      for (i = 0; i < patterns_list->nelts; ++i)
3739362181Sdim        {
3740362181Sdim          svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(patterns_list, i);
3741362181Sdim
3742362181Sdim          if (elt->kind != SVN_RA_SVN_STRING)
3743362181Sdim            return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
3744362181Sdim                                    "Pattern field not a string");
3745362181Sdim
3746362181Sdim          APR_ARRAY_PUSH(patterns, const char *) = elt->u.string.data;
3747362181Sdim        }
3748362181Sdim    }
3749362181Sdim
3750362181Sdim  /* Check authorizations */
3751362181Sdim  SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
3752362181Sdim                           full_path, FALSE));
3753362181Sdim
3754362181Sdim  if (!SVN_IS_VALID_REVNUM(rev))
3755362181Sdim    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
3756362181Sdim
3757362181Sdim  SVN_ERR(log_command(b, conn, pool, "%s",
3758362181Sdim                      svn_log__list(full_path, rev, patterns, depth,
3759362181Sdim                                    rb.dirent_fields, pool)));
3760362181Sdim
3761362181Sdim  /* Fetch the root of the appropriate revision. */
3762362181Sdim  SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool));
3763362181Sdim
3764362181Sdim  /* Fetch the directory entries if requested and send them immediately. */
3765362181Sdim  path_info_only = (rb.dirent_fields & ~SVN_DIRENT_KIND) == 0;
3766362181Sdim  err = svn_repos_list(root, full_path, patterns, depth, path_info_only,
3767362181Sdim                       authz_check_access_cb_func(b), &ab, list_receiver,
3768362181Sdim                       &rb, NULL, NULL, pool);
3769362181Sdim
3770362181Sdim
3771362181Sdim  /* Finish response. */
3772362181Sdim  write_err = svn_ra_svn__write_word(conn, pool, "done");
3773362181Sdim  if (write_err)
3774362181Sdim    {
3775362181Sdim      svn_error_clear(err);
3776362181Sdim      return write_err;
3777362181Sdim    }
3778362181Sdim  SVN_CMD_ERR(err);
3779362181Sdim
3780362181Sdim  return svn_error_trace(svn_ra_svn__write_cmd_response(conn, pool, ""));
3781362181Sdim}
3782362181Sdim
3783362181Sdimstatic const svn_ra_svn__cmd_entry_t main_commands[] = {
3784251881Speter  { "reparent",        reparent },
3785251881Speter  { "get-latest-rev",  get_latest_rev },
3786251881Speter  { "get-dated-rev",   get_dated_rev },
3787251881Speter  { "change-rev-prop", change_rev_prop },
3788251881Speter  { "change-rev-prop2",change_rev_prop2 },
3789251881Speter  { "rev-proplist",    rev_proplist },
3790251881Speter  { "rev-prop",        rev_prop },
3791251881Speter  { "commit",          commit },
3792251881Speter  { "get-file",        get_file },
3793251881Speter  { "get-dir",         get_dir },
3794251881Speter  { "update",          update },
3795251881Speter  { "switch",          switch_cmd },
3796251881Speter  { "status",          status },
3797251881Speter  { "diff",            diff },
3798251881Speter  { "get-mergeinfo",   get_mergeinfo },
3799251881Speter  { "log",             log_cmd },
3800251881Speter  { "check-path",      check_path },
3801251881Speter  { "stat",            stat_cmd },
3802251881Speter  { "get-locations",   get_locations },
3803251881Speter  { "get-location-segments",   get_location_segments },
3804251881Speter  { "get-file-revs",   get_file_revs },
3805251881Speter  { "lock",            lock },
3806251881Speter  { "lock-many",       lock_many },
3807251881Speter  { "unlock",          unlock },
3808251881Speter  { "unlock-many",     unlock_many },
3809251881Speter  { "get-lock",        get_lock },
3810251881Speter  { "get-locks",       get_locks },
3811251881Speter  { "replay",          replay },
3812251881Speter  { "replay-range",    replay_range },
3813251881Speter  { "get-deleted-rev", get_deleted_rev },
3814251881Speter  { "get-iprops",      get_inherited_props },
3815362181Sdim  { "list",            list },
3816251881Speter  { NULL }
3817251881Speter};
3818251881Speter
3819251881Speter/* Skip past the scheme part of a URL, including the tunnel specification
3820251881Speter * if present.  Return NULL if the scheme part is invalid for ra_svn. */
3821251881Speterstatic const char *skip_scheme_part(const char *url)
3822251881Speter{
3823251881Speter  if (strncmp(url, "svn", 3) != 0)
3824251881Speter    return NULL;
3825251881Speter  url += 3;
3826251881Speter  if (*url == '+')
3827251881Speter    url += strcspn(url, ":");
3828251881Speter  if (strncmp(url, "://", 3) != 0)
3829251881Speter    return NULL;
3830251881Speter  return url + 3;
3831251881Speter}
3832251881Speter
3833251881Speter/* Check that PATH is a valid repository path, meaning it doesn't contain any
3834251881Speter   '..' path segments.
3835251881Speter   NOTE: This is similar to svn_path_is_backpath_present, but that function
3836251881Speter   assumes the path separator is '/'.  This function also checks for
3837251881Speter   segments delimited by the local path separator. */
3838251881Speterstatic svn_boolean_t
3839251881Speterrepos_path_valid(const char *path)
3840251881Speter{
3841251881Speter  const char *s = path;
3842251881Speter
3843251881Speter  while (*s)
3844251881Speter    {
3845251881Speter      /* Scan for the end of the segment. */
3846251881Speter      while (*path && *path != '/' && *path != SVN_PATH_LOCAL_SEPARATOR)
3847251881Speter        ++path;
3848251881Speter
3849251881Speter      /* Check for '..'. */
3850251881Speter#ifdef WIN32
3851251881Speter      /* On Windows, don't allow sequences of more than one character
3852251881Speter         consisting of just dots and spaces.  Win32 functions treat
3853251881Speter         paths such as ".. " and "......." inconsistently.  Make sure
3854251881Speter         no one can escape out of the root. */
3855251881Speter      if (path - s >= 2 && strspn(s, ". ") == (size_t)(path - s))
3856251881Speter        return FALSE;
3857251881Speter#else  /* ! WIN32 */
3858251881Speter      if (path - s == 2 && s[0] == '.' && s[1] == '.')
3859251881Speter        return FALSE;
3860251881Speter#endif
3861251881Speter
3862251881Speter      /* Skip all separators. */
3863251881Speter      while (*path && (*path == '/' || *path == SVN_PATH_LOCAL_SEPARATOR))
3864251881Speter        ++path;
3865251881Speter      s = path;
3866251881Speter    }
3867251881Speter
3868251881Speter  return TRUE;
3869251881Speter}
3870251881Speter
3871251881Speter/* Look for the repository given by URL, using ROOT as the virtual
3872289180Speter * repository root.  If we find one, fill in the repos, fs, repos_url,
3873289180Speter * and fs_path fields of REPOSITORY.  VHOST and READ_ONLY flags are the
3874289180Speter * same as in the server baton.
3875289180Speter *
3876362181Sdim * CONFIG_POOL shall be used to load config objects.
3877289180Speter *
3878289180Speter * Use SCRATCH_POOL for temporary allocations.
3879289180Speter *
3880251881Speter */
3881289180Speterstatic svn_error_t *
3882289180Speterfind_repos(const char *url,
3883289180Speter           const char *root,
3884289180Speter           svn_boolean_t vhost,
3885289180Speter           svn_boolean_t read_only,
3886289180Speter           svn_config_t *cfg,
3887289180Speter           repository_t *repository,
3888289180Speter           svn_repos__config_pool_t *config_pool,
3889289180Speter           apr_hash_t *fs_config,
3890362181Sdim           svn_repos_authz_warning_func_t authz_warning_func,
3891362181Sdim           void *authz_warning_baton,
3892289180Speter           apr_pool_t *result_pool,
3893289180Speter           apr_pool_t *scratch_pool)
3894251881Speter{
3895362181Sdim  const char *path, *full_path, *fs_path, *hooks_env, *canonical_path;
3896362181Sdim  const char *canonical_root;
3897251881Speter  svn_stringbuf_t *url_buf;
3898362181Sdim  svn_boolean_t sasl_requested;
3899251881Speter
3900251881Speter  /* Skip past the scheme and authority part. */
3901251881Speter  path = skip_scheme_part(url);
3902251881Speter  if (path == NULL)
3903251881Speter    return svn_error_createf(SVN_ERR_BAD_URL, NULL,
3904251881Speter                             "Non-svn URL passed to svn server: '%s'", url);
3905251881Speter
3906289180Speter  if (! vhost)
3907251881Speter    {
3908251881Speter      path = strchr(path, '/');
3909251881Speter      if (path == NULL)
3910251881Speter        path = "";
3911251881Speter    }
3912362181Sdim  SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path,
3913362181Sdim                                        scratch_pool, scratch_pool));
3914362181Sdim  path = svn_path_uri_decode(canonical_path, scratch_pool);
3915251881Speter
3916251881Speter  /* Ensure that it isn't possible to escape the root by disallowing
3917251881Speter     '..' segments. */
3918251881Speter  if (!repos_path_valid(path))
3919251881Speter    return svn_error_create(SVN_ERR_BAD_FILENAME, NULL,
3920251881Speter                            "Couldn't determine repository path");
3921251881Speter
3922251881Speter  /* Join the server-configured root with the client path. */
3923362181Sdim  SVN_ERR(svn_dirent_canonicalize_safe(&canonical_root, NULL, root,
3924362181Sdim                                       scratch_pool, scratch_pool));
3925362181Sdim  full_path = svn_dirent_join(canonical_root, path, scratch_pool);
3926251881Speter
3927251881Speter  /* Search for a repository in the full path. */
3928289180Speter  repository->repos_root = svn_repos_find_root_path(full_path, result_pool);
3929289180Speter  if (!repository->repos_root)
3930251881Speter    return svn_error_createf(SVN_ERR_RA_SVN_REPOS_NOT_FOUND, NULL,
3931251881Speter                             "No repository found in '%s'", url);
3932251881Speter
3933251881Speter  /* Open the repository and fill in b with the resulting information. */
3934289180Speter  SVN_ERR(svn_repos_open3(&repository->repos, repository->repos_root,
3935289180Speter                          fs_config, result_pool, scratch_pool));
3936289180Speter  SVN_ERR(svn_repos_remember_client_capabilities(repository->repos,
3937289180Speter                                                 repository->capabilities));
3938289180Speter  repository->fs = svn_repos_fs(repository->repos);
3939289180Speter  fs_path = full_path + strlen(repository->repos_root);
3940289180Speter  repository->fs_path = svn_stringbuf_create(*fs_path ? fs_path : "/",
3941289180Speter                                             result_pool);
3942289180Speter  url_buf = svn_stringbuf_create(url, result_pool);
3943251881Speter  svn_path_remove_components(url_buf,
3944289180Speter                        svn_path_component_count(repository->fs_path->data));
3945289180Speter  repository->repos_url = url_buf->data;
3946362181Sdim  repository->authz_repos_name = svn_dirent_is_child(canonical_root,
3947289180Speter                                                     repository->repos_root,
3948289180Speter                                                     result_pool);
3949289180Speter  if (repository->authz_repos_name == NULL)
3950289180Speter    repository->repos_name = svn_dirent_basename(repository->repos_root,
3951289180Speter                                                 result_pool);
3952251881Speter  else
3953289180Speter    repository->repos_name = repository->authz_repos_name;
3954289180Speter  repository->repos_name = svn_path_uri_encode(repository->repos_name,
3955289180Speter                                               result_pool);
3956251881Speter
3957251881Speter  /* If the svnserve configuration has not been loaded then load it from the
3958251881Speter   * repository. */
3959289180Speter  if (NULL == cfg)
3960251881Speter    {
3961289180Speter      repository->base = svn_repos_conf_dir(repository->repos, result_pool);
3962251881Speter
3963362181Sdim      SVN_ERR(svn_repos__config_pool_get(&cfg, config_pool,
3964289180Speter                                         svn_repos_svnserve_conf
3965289180Speter                                            (repository->repos, result_pool),
3966362181Sdim                                         FALSE, repository->repos,
3967289180Speter                                         result_pool));
3968251881Speter    }
3969289180Speter
3970289180Speter  SVN_ERR(load_pwdb_config(repository, cfg, config_pool, result_pool));
3971289180Speter  SVN_ERR(load_authz_config(repository, repository->repos_root, cfg,
3972362181Sdim                            authz_warning_func, authz_warning_baton,
3973362181Sdim                            result_pool, scratch_pool));
3974289180Speter
3975362181Sdim  /* Should we use Cyrus SASL? */
3976362181Sdim  SVN_ERR(svn_config_get_bool(cfg, &sasl_requested,
3977362181Sdim                              SVN_CONFIG_SECTION_SASL,
3978362181Sdim                              SVN_CONFIG_OPTION_USE_SASL, FALSE));
3979362181Sdim  if (sasl_requested)
3980362181Sdim    {
3981289180Speter#ifdef SVN_HAVE_SASL
3982289180Speter      const char *val;
3983289180Speter
3984362181Sdim      repository->use_sasl = sasl_requested;
3985289180Speter
3986289180Speter      svn_config_get(cfg, &val, SVN_CONFIG_SECTION_SASL,
3987289180Speter                    SVN_CONFIG_OPTION_MIN_SSF, "0");
3988289180Speter      SVN_ERR(svn_cstring_atoui(&repository->min_ssf, val));
3989289180Speter
3990289180Speter      svn_config_get(cfg, &val, SVN_CONFIG_SECTION_SASL,
3991289180Speter                    SVN_CONFIG_OPTION_MAX_SSF, "256");
3992289180Speter      SVN_ERR(svn_cstring_atoui(&repository->max_ssf, val));
3993362181Sdim#else /* !SVN_HAVE_SASL */
3994362181Sdim      return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
3995362181Sdim                               _("SASL requested but not compiled in; "
3996362181Sdim                                 "set '%s' to 'false' or recompile "
3997362181Sdim                                 "svnserve with SASL support"),
3998362181Sdim                               SVN_CONFIG_OPTION_USE_SASL);
3999362181Sdim#endif /* SVN_HAVE_SASL */
4000251881Speter    }
4001362181Sdim  else
4002362181Sdim    {
4003362181Sdim      repository->use_sasl = FALSE;
4004362181Sdim    }
4005251881Speter
4006251881Speter  /* Use the repository UUID as the default realm. */
4007289180Speter  SVN_ERR(svn_fs_get_uuid(repository->fs, &repository->realm, scratch_pool));
4008289180Speter  svn_config_get(cfg, &repository->realm, SVN_CONFIG_SECTION_GENERAL,
4009289180Speter                 SVN_CONFIG_OPTION_REALM, repository->realm);
4010289180Speter  repository->realm = apr_pstrdup(result_pool, repository->realm);
4011251881Speter
4012251881Speter  /* Make sure it's possible for the client to authenticate.  Note
4013251881Speter     that this doesn't take into account any authz configuration read
4014251881Speter     above, because we can't know about access it grants until paths
4015251881Speter     are given by the client. */
4016289180Speter  set_access(repository, cfg, read_only);
4017251881Speter
4018251881Speter  /* Configure hook script environment variables. */
4019289180Speter  svn_config_get(cfg, &hooks_env, SVN_CONFIG_SECTION_GENERAL,
4020251881Speter                 SVN_CONFIG_OPTION_HOOKS_ENV, NULL);
4021251881Speter  if (hooks_env)
4022289180Speter    hooks_env = svn_dirent_internal_style(hooks_env, scratch_pool);
4023251881Speter
4024309511Speter  SVN_ERR(svn_repos_hooks_setenv(repository->repos, hooks_env, scratch_pool));
4025289180Speter  repository->hooks_env = apr_pstrdup(result_pool, hooks_env);
4026289180Speter
4027251881Speter  return SVN_NO_ERROR;
4028251881Speter}
4029251881Speter
4030251881Speter/* Compute the authentication name EXTERNAL should be able to get, if any. */
4031251881Speterstatic const char *get_tunnel_user(serve_params_t *params, apr_pool_t *pool)
4032251881Speter{
4033251881Speter  /* Only offer EXTERNAL for connections tunneled over a login agent. */
4034251881Speter  if (!params->tunnel)
4035251881Speter    return NULL;
4036251881Speter
4037251881Speter  /* If a tunnel user was provided on the command line, use that. */
4038251881Speter  if (params->tunnel_user)
4039251881Speter    return params->tunnel_user;
4040251881Speter
4041251881Speter  return svn_user_get_name(pool);
4042251881Speter}
4043251881Speter
4044251881Speterstatic void
4045251881Speterfs_warning_func(void *baton, svn_error_t *err)
4046251881Speter{
4047251881Speter  fs_warning_baton_t *b = baton;
4048289180Speter  log_error(err, b->server);
4049251881Speter}
4050251881Speter
4051251881Speter/* Return the normalized repository-relative path for the given PATH
4052251881Speter * (may be a URL, full path or relative path) and fs contained in the
4053251881Speter * server baton BATON. Allocate the result in POOL.
4054251881Speter */
4055251881Speterstatic const char *
4056251881Speterget_normalized_repo_rel_path(void *baton,
4057251881Speter                             const char *path,
4058251881Speter                             apr_pool_t *pool)
4059251881Speter{
4060251881Speter  server_baton_t *sb = baton;
4061251881Speter
4062251881Speter  if (svn_path_is_url(path))
4063251881Speter    {
4064251881Speter      /* This is a copyfrom URL. */
4065289180Speter      path = svn_uri_skip_ancestor(sb->repository->repos_url, path, pool);
4066251881Speter      path = svn_fspath__canonicalize(path, pool);
4067251881Speter    }
4068251881Speter  else
4069251881Speter    {
4070251881Speter      /* This is a base-relative path. */
4071251881Speter      if ((path)[0] != '/')
4072251881Speter        /* Get an absolute path for use in the FS. */
4073289180Speter        path = svn_fspath__join(sb->repository->fs_path->data, path, pool);
4074251881Speter    }
4075251881Speter
4076251881Speter  return path;
4077251881Speter}
4078251881Speter
4079251881Speter/* Get the revision root for REVISION in fs given by server baton BATON
4080251881Speter * and return it in *FS_ROOT. Use HEAD if REVISION is SVN_INVALID_REVNUM.
4081251881Speter * Use POOL for allocations.
4082251881Speter */
4083251881Speterstatic svn_error_t *
4084251881Speterget_revision_root(svn_fs_root_t **fs_root,
4085251881Speter                  void *baton,
4086251881Speter                  svn_revnum_t revision,
4087251881Speter                  apr_pool_t *pool)
4088251881Speter{
4089251881Speter  server_baton_t *sb = baton;
4090251881Speter
4091251881Speter  if (!SVN_IS_VALID_REVNUM(revision))
4092289180Speter    SVN_ERR(svn_fs_youngest_rev(&revision, sb->repository->fs, pool));
4093251881Speter
4094289180Speter  SVN_ERR(svn_fs_revision_root(fs_root, sb->repository->fs, revision, pool));
4095251881Speter
4096251881Speter  return SVN_NO_ERROR;
4097251881Speter}
4098251881Speter
4099251881Speterstatic svn_error_t *
4100251881Speterfetch_props_func(apr_hash_t **props,
4101251881Speter                 void *baton,
4102251881Speter                 const char *path,
4103251881Speter                 svn_revnum_t base_revision,
4104251881Speter                 apr_pool_t *result_pool,
4105251881Speter                 apr_pool_t *scratch_pool)
4106251881Speter{
4107251881Speter  svn_fs_root_t *fs_root;
4108251881Speter  svn_error_t *err;
4109251881Speter
4110251881Speter  path = get_normalized_repo_rel_path(baton, path, scratch_pool);
4111251881Speter  SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool));
4112251881Speter
4113251881Speter  err = svn_fs_node_proplist(props, fs_root, path, result_pool);
4114251881Speter  if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
4115251881Speter    {
4116251881Speter      svn_error_clear(err);
4117251881Speter      *props = apr_hash_make(result_pool);
4118251881Speter      return SVN_NO_ERROR;
4119251881Speter    }
4120251881Speter  else if (err)
4121251881Speter    return svn_error_trace(err);
4122251881Speter
4123251881Speter  return SVN_NO_ERROR;
4124251881Speter}
4125251881Speter
4126251881Speterstatic svn_error_t *
4127251881Speterfetch_kind_func(svn_node_kind_t *kind,
4128251881Speter                void *baton,
4129251881Speter                const char *path,
4130251881Speter                svn_revnum_t base_revision,
4131251881Speter                apr_pool_t *scratch_pool)
4132251881Speter{
4133251881Speter  svn_fs_root_t *fs_root;
4134251881Speter
4135251881Speter  path = get_normalized_repo_rel_path(baton, path, scratch_pool);
4136251881Speter  SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool));
4137251881Speter
4138251881Speter  SVN_ERR(svn_fs_check_path(kind, fs_root, path, scratch_pool));
4139251881Speter
4140251881Speter  return SVN_NO_ERROR;
4141251881Speter}
4142251881Speter
4143251881Speterstatic svn_error_t *
4144251881Speterfetch_base_func(const char **filename,
4145251881Speter                void *baton,
4146251881Speter                const char *path,
4147251881Speter                svn_revnum_t base_revision,
4148251881Speter                apr_pool_t *result_pool,
4149251881Speter                apr_pool_t *scratch_pool)
4150251881Speter{
4151251881Speter  svn_stream_t *contents;
4152251881Speter  svn_stream_t *file_stream;
4153251881Speter  const char *tmp_filename;
4154251881Speter  svn_fs_root_t *fs_root;
4155251881Speter  svn_error_t *err;
4156251881Speter
4157251881Speter  path = get_normalized_repo_rel_path(baton, path, scratch_pool);
4158251881Speter  SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool));
4159251881Speter
4160251881Speter  err = svn_fs_file_contents(&contents, fs_root, path, scratch_pool);
4161251881Speter  if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
4162251881Speter    {
4163251881Speter      svn_error_clear(err);
4164251881Speter      *filename = NULL;
4165251881Speter      return SVN_NO_ERROR;
4166251881Speter    }
4167251881Speter  else if (err)
4168251881Speter    return svn_error_trace(err);
4169251881Speter  SVN_ERR(svn_stream_open_unique(&file_stream, &tmp_filename, NULL,
4170251881Speter                                 svn_io_file_del_on_pool_cleanup,
4171251881Speter                                 scratch_pool, scratch_pool));
4172251881Speter  SVN_ERR(svn_stream_copy3(contents, file_stream, NULL, NULL, scratch_pool));
4173251881Speter
4174251881Speter  *filename = apr_pstrdup(result_pool, tmp_filename);
4175251881Speter
4176251881Speter  return SVN_NO_ERROR;
4177251881Speter}
4178251881Speter
4179289180Speterclient_info_t *
4180289180Speterget_client_info(svn_ra_svn_conn_t *conn,
4181289180Speter                serve_params_t *params,
4182289180Speter                apr_pool_t *pool)
4183251881Speter{
4184289180Speter  client_info_t *client_info = apr_pcalloc(pool, sizeof(*client_info));
4185289180Speter
4186289180Speter  client_info->tunnel = params->tunnel;
4187289180Speter  client_info->tunnel_user = get_tunnel_user(params, pool);
4188289180Speter  client_info->user = NULL;
4189289180Speter  client_info->authz_user = NULL;
4190289180Speter  client_info->remote_host = svn_ra_svn_conn_remote_host(conn);
4191289180Speter
4192289180Speter  return client_info;
4193289180Speter}
4194289180Speter
4195362181Sdimstatic void
4196362181Sdimhandle_authz_warning(void *baton,
4197362181Sdim                     const svn_error_t *err,
4198362181Sdim                     apr_pool_t *scratch_pool)
4199362181Sdim{
4200362181Sdim  server_baton_t *const server_baton = baton;
4201362181Sdim  log_warning(err, server_baton);
4202362181Sdim  SVN_UNUSED(scratch_pool);
4203362181Sdim}
4204362181Sdim
4205289180Speter/* Construct the server baton for CONN using PARAMS and return it in *BATON.
4206289180Speter * It's lifetime is the same as that of CONN.  SCRATCH_POOL
4207289180Speter */
4208289180Speterstatic svn_error_t *
4209289180Speterconstruct_server_baton(server_baton_t **baton,
4210289180Speter                       svn_ra_svn_conn_t *conn,
4211289180Speter                       serve_params_t *params,
4212289180Speter                       apr_pool_t *scratch_pool)
4213289180Speter{
4214362181Sdim  svn_error_t *err;
4215251881Speter  apr_uint64_t ver;
4216362181Sdim  const char *client_url, *ra_client_string, *client_string, *canonical_url;
4217362181Sdim  svn_ra_svn__list_t *caplist;
4218289180Speter  apr_pool_t *conn_pool = svn_ra_svn__get_pool(conn);
4219289180Speter  server_baton_t *b = apr_pcalloc(conn_pool, sizeof(*b));
4220289180Speter  fs_warning_baton_t *warn_baton;
4221289180Speter  svn_stringbuf_t *cap_log = svn_stringbuf_create_empty(scratch_pool);
4222251881Speter
4223289180Speter  b->repository = apr_pcalloc(conn_pool, sizeof(*b->repository));
4224289180Speter  b->repository->username_case = params->username_case;
4225289180Speter  b->repository->base = params->base;
4226289180Speter  b->repository->pwdb = NULL;
4227289180Speter  b->repository->authzdb = NULL;
4228289180Speter  b->repository->realm = NULL;
4229289180Speter  b->repository->use_sasl = FALSE;
4230251881Speter
4231289180Speter  b->read_only = params->read_only;
4232289180Speter  b->pool = conn_pool;
4233289180Speter  b->vhost = params->vhost;
4234251881Speter
4235289180Speter  b->logger = params->logger;
4236289180Speter  b->client_info = get_client_info(conn, params, conn_pool);
4237289180Speter
4238251881Speter  /* Send greeting.  We don't support version 1 any more, so we can
4239251881Speter   * send an empty mechlist. */
4240251881Speter  if (params->compression_level > 0)
4241289180Speter    SVN_ERR(svn_ra_svn__write_cmd_response(conn, scratch_pool,
4242362181Sdim                                           "nn()(wwwwwwwwwwwww)",
4243251881Speter                                           (apr_uint64_t) 2, (apr_uint64_t) 2,
4244251881Speter                                           SVN_RA_SVN_CAP_EDIT_PIPELINE,
4245251881Speter                                           SVN_RA_SVN_CAP_SVNDIFF1,
4246362181Sdim                                           SVN_RA_SVN_CAP_SVNDIFF2_ACCEPTED,
4247251881Speter                                           SVN_RA_SVN_CAP_ABSENT_ENTRIES,
4248251881Speter                                           SVN_RA_SVN_CAP_COMMIT_REVPROPS,
4249251881Speter                                           SVN_RA_SVN_CAP_DEPTH,
4250251881Speter                                           SVN_RA_SVN_CAP_LOG_REVPROPS,
4251251881Speter                                           SVN_RA_SVN_CAP_ATOMIC_REVPROPS,
4252251881Speter                                           SVN_RA_SVN_CAP_PARTIAL_REPLAY,
4253251881Speter                                           SVN_RA_SVN_CAP_INHERITED_PROPS,
4254251881Speter                                           SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS,
4255362181Sdim                                           SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE,
4256362181Sdim                                           SVN_RA_SVN_CAP_LIST
4257251881Speter                                           ));
4258251881Speter  else
4259289180Speter    SVN_ERR(svn_ra_svn__write_cmd_response(conn, scratch_pool,
4260362181Sdim                                           "nn()(wwwwwwwwwww)",
4261251881Speter                                           (apr_uint64_t) 2, (apr_uint64_t) 2,
4262251881Speter                                           SVN_RA_SVN_CAP_EDIT_PIPELINE,
4263251881Speter                                           SVN_RA_SVN_CAP_ABSENT_ENTRIES,
4264251881Speter                                           SVN_RA_SVN_CAP_COMMIT_REVPROPS,
4265251881Speter                                           SVN_RA_SVN_CAP_DEPTH,
4266251881Speter                                           SVN_RA_SVN_CAP_LOG_REVPROPS,
4267251881Speter                                           SVN_RA_SVN_CAP_ATOMIC_REVPROPS,
4268251881Speter                                           SVN_RA_SVN_CAP_PARTIAL_REPLAY,
4269251881Speter                                           SVN_RA_SVN_CAP_INHERITED_PROPS,
4270251881Speter                                           SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS,
4271362181Sdim                                           SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE,
4272362181Sdim                                           SVN_RA_SVN_CAP_LIST
4273251881Speter                                           ));
4274251881Speter
4275251881Speter  /* Read client response, which we assume to be in version 2 format:
4276251881Speter   * version, capability list, and client URL; then we do an auth
4277251881Speter   * request. */
4278289180Speter  SVN_ERR(svn_ra_svn__read_tuple(conn, scratch_pool, "nlc?c(?c)",
4279251881Speter                                 &ver, &caplist, &client_url,
4280251881Speter                                 &ra_client_string,
4281251881Speter                                 &client_string));
4282251881Speter  if (ver != 2)
4283362181Sdim    return svn_error_createf(SVN_ERR_RA_SVN_BAD_VERSION, NULL,
4284362181Sdim                             "Unsupported ra_svn protocol version"
4285362181Sdim                             " %"APR_UINT64_T_FMT
4286362181Sdim                             " (supported versions: [2])", ver);
4287251881Speter
4288362181Sdim  SVN_ERR(svn_uri_canonicalize_safe(&canonical_url, NULL, client_url,
4289362181Sdim                                    conn_pool, scratch_pool));
4290362181Sdim  client_url = canonical_url;
4291362181Sdim  SVN_ERR(svn_ra_svn__set_capabilities(conn, caplist));
4292251881Speter
4293251881Speter  /* All released versions of Subversion support edit-pipeline,
4294251881Speter   * so we do not accept connections from clients that do not. */
4295251881Speter  if (! svn_ra_svn_has_capability(conn, SVN_RA_SVN_CAP_EDIT_PIPELINE))
4296362181Sdim    return svn_error_create(SVN_ERR_RA_SVN_BAD_VERSION, NULL,
4297362181Sdim                            "Missing edit-pipeline capability");
4298251881Speter
4299251881Speter  /* find_repos needs the capabilities as a list of words (eventually
4300251881Speter     they get handed to the start-commit hook).  While we could add a
4301251881Speter     new interface to re-retrieve them from conn and convert the
4302251881Speter     result to a list, it's simpler to just convert caplist by hand
4303362181Sdim     here, since we already have it and turning 'svn_ra_svn__item_t's
4304251881Speter     into 'const char *'s is pretty easy.
4305251881Speter
4306251881Speter     We only record capabilities we care about.  The client may report
4307251881Speter     more (because it doesn't know what the server cares about). */
4308251881Speter  {
4309251881Speter    int i;
4310362181Sdim    svn_ra_svn__item_t *item;
4311251881Speter
4312289180Speter    b->repository->capabilities = apr_array_make(conn_pool, 1,
4313289180Speter                                                 sizeof(const char *));
4314251881Speter    for (i = 0; i < caplist->nelts; i++)
4315251881Speter      {
4316362181Sdim        static const svn_string_t str_cap_mergeinfo
4317362181Sdim          = SVN__STATIC_STRING(SVN_RA_SVN_CAP_MERGEINFO);
4318362181Sdim
4319362181Sdim        item = &SVN_RA_SVN__LIST_ITEM(caplist, i);
4320251881Speter        /* ra_svn_set_capabilities() already type-checked for us */
4321362181Sdim        if (svn_string_compare(&item->u.word, &str_cap_mergeinfo))
4322251881Speter          {
4323289180Speter            APR_ARRAY_PUSH(b->repository->capabilities, const char *)
4324251881Speter              = SVN_RA_CAPABILITY_MERGEINFO;
4325251881Speter          }
4326251881Speter        /* Save for operational log. */
4327251881Speter        if (cap_log->len > 0)
4328251881Speter          svn_stringbuf_appendcstr(cap_log, " ");
4329362181Sdim        svn_stringbuf_appendcstr(cap_log, item->u.word.data);
4330251881Speter      }
4331251881Speter  }
4332251881Speter
4333362181Sdim  /* (*b) has the logger, repository and client_info set, so it can
4334362181Sdim     be used as the authz_warning_baton that eventyally gets passed
4335362181Sdim     to log_warning(). */
4336289180Speter  err = handle_config_error(find_repos(client_url, params->root, b->vhost,
4337289180Speter                                       b->read_only, params->cfg,
4338289180Speter                                       b->repository, params->config_pool,
4339362181Sdim                                       params->fs_config,
4340362181Sdim                                       handle_authz_warning, b,
4341289180Speter                                       conn_pool, scratch_pool),
4342289180Speter                            b);
4343251881Speter  if (!err)
4344251881Speter    {
4345289180Speter      if (b->repository->anon_access == NO_ACCESS
4346289180Speter          && (b->repository->auth_access == NO_ACCESS
4347289180Speter              || (!b->client_info->tunnel_user && !b->repository->pwdb
4348289180Speter                  && !b->repository->use_sasl)))
4349251881Speter        err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
4350289180Speter                                   "No access allowed to this repository",
4351289180Speter                                   b);
4352251881Speter    }
4353289180Speter  if (!err)
4354289180Speter    {
4355289180Speter      SVN_ERR(auth_request(conn, scratch_pool, b, READ_ACCESS, FALSE));
4356289180Speter      if (current_access(b) == NO_ACCESS)
4357289180Speter        err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
4358289180Speter                                   "Not authorized for access", b);
4359289180Speter    }
4360251881Speter  if (err)
4361251881Speter    {
4362362181Sdim      /* Report these errors to the client before closing the connection. */
4363362181Sdim      err = svn_error_compose_create(err,
4364362181Sdim              svn_ra_svn__write_cmd_failure(conn, scratch_pool, err));
4365362181Sdim      err = svn_error_compose_create(err,
4366362181Sdim              svn_ra_svn__flush(conn, scratch_pool));
4367362181Sdim      return err;
4368251881Speter    }
4369251881Speter
4370289180Speter  SVN_ERR(svn_fs_get_uuid(b->repository->fs, &b->repository->uuid,
4371289180Speter                          conn_pool));
4372251881Speter
4373251881Speter  /* We can't claim mergeinfo capability until we know whether the
4374251881Speter     repository supports mergeinfo (i.e., is not a 1.4 repository),
4375251881Speter     but we don't get the repository url from the client until after
4376251881Speter     we've already sent the initial list of server capabilities.  So
4377251881Speter     we list repository capabilities here, in our first response after
4378251881Speter     the client has sent the url. */
4379251881Speter  {
4380251881Speter    svn_boolean_t supports_mergeinfo;
4381289180Speter    SVN_ERR(svn_repos_has_capability(b->repository->repos,
4382289180Speter                                     &supports_mergeinfo,
4383289180Speter                                     SVN_REPOS_CAPABILITY_MERGEINFO,
4384289180Speter                                     scratch_pool));
4385251881Speter
4386289180Speter    SVN_ERR(svn_ra_svn__write_tuple(conn, scratch_pool, "w(cc(!",
4387289180Speter                                    "success", b->repository->uuid,
4388289180Speter                                    b->repository->repos_url));
4389251881Speter    if (supports_mergeinfo)
4390289180Speter      SVN_ERR(svn_ra_svn__write_word(conn, scratch_pool,
4391289180Speter                                     SVN_RA_SVN_CAP_MERGEINFO));
4392289180Speter    SVN_ERR(svn_ra_svn__write_tuple(conn, scratch_pool, "!))"));
4393289180Speter    SVN_ERR(svn_ra_svn__flush(conn, scratch_pool));
4394251881Speter  }
4395251881Speter
4396289180Speter  /* Log the open. */
4397289180Speter  if (ra_client_string == NULL || ra_client_string[0] == '\0')
4398289180Speter    ra_client_string = "-";
4399289180Speter  else
4400289180Speter    ra_client_string = svn_path_uri_encode(ra_client_string, scratch_pool);
4401289180Speter  if (client_string == NULL || client_string[0] == '\0')
4402289180Speter    client_string = "-";
4403289180Speter  else
4404289180Speter    client_string = svn_path_uri_encode(client_string, scratch_pool);
4405289180Speter  SVN_ERR(log_command(b, conn, scratch_pool,
4406289180Speter                      "open %" APR_UINT64_T_FMT " cap=(%s) %s %s %s",
4407289180Speter                      ver, cap_log->data,
4408289180Speter                      svn_path_uri_encode(b->repository->fs_path->data,
4409289180Speter                                          scratch_pool),
4410289180Speter                      ra_client_string, client_string));
4411289180Speter
4412289180Speter  warn_baton = apr_pcalloc(conn_pool, sizeof(*warn_baton));
4413289180Speter  warn_baton->server = b;
4414289180Speter  warn_baton->conn = conn;
4415289180Speter  svn_fs_set_warning_func(b->repository->fs, fs_warning_func, warn_baton);
4416289180Speter
4417251881Speter  /* Set up editor shims. */
4418251881Speter  {
4419251881Speter    svn_delta_shim_callbacks_t *callbacks =
4420289180Speter                                svn_delta_shim_callbacks_default(conn_pool);
4421251881Speter
4422251881Speter    callbacks->fetch_base_func = fetch_base_func;
4423251881Speter    callbacks->fetch_props_func = fetch_props_func;
4424251881Speter    callbacks->fetch_kind_func = fetch_kind_func;
4425289180Speter    callbacks->fetch_baton = b;
4426251881Speter
4427251881Speter    SVN_ERR(svn_ra_svn__set_shim_callbacks(conn, callbacks));
4428251881Speter  }
4429251881Speter
4430289180Speter  *baton = b;
4431289180Speter
4432289180Speter  return SVN_NO_ERROR;
4433251881Speter}
4434289180Speter
4435289180Spetersvn_error_t *
4436289180Speterserve_interruptable(svn_boolean_t *terminate_p,
4437289180Speter                    connection_t *connection,
4438289180Speter                    svn_boolean_t (* is_busy)(connection_t *),
4439289180Speter                    apr_pool_t *pool)
4440289180Speter{
4441289180Speter  svn_boolean_t terminate = FALSE;
4442289180Speter  svn_error_t *err = NULL;
4443362181Sdim  const svn_ra_svn__cmd_entry_t *command;
4444289180Speter  apr_pool_t *iterpool = svn_pool_create(pool);
4445289180Speter
4446289180Speter  /* Prepare command parser. */
4447289180Speter  apr_hash_t *cmd_hash = apr_hash_make(pool);
4448289180Speter  for (command = main_commands; command->cmdname; command++)
4449289180Speter    svn_hash_sets(cmd_hash, command->cmdname, command);
4450289180Speter
4451289180Speter  /* Auto-initialize connection */
4452289180Speter  if (! connection->conn)
4453289180Speter    {
4454289180Speter      apr_status_t ar;
4455289180Speter
4456289180Speter      /* Enable TCP keep-alives on the socket so we time out when
4457289180Speter       * the connection breaks due to network-layer problems.
4458289180Speter       * If the peer has dropped the connection due to a network partition
4459289180Speter       * or a crash, or if the peer no longer considers the connection
4460289180Speter       * valid because we are behind a NAT and our public IP has changed,
4461289180Speter       * it will respond to the keep-alive probe with a RST instead of an
4462289180Speter       * acknowledgment segment, which will cause svn to abort the session
4463289180Speter       * even while it is currently blocked waiting for data from the peer. */
4464289180Speter      ar = apr_socket_opt_set(connection->usock, APR_SO_KEEPALIVE, 1);
4465289180Speter      if (ar)
4466289180Speter        {
4467289180Speter          /* It's not a fatal error if we cannot enable keep-alives. */
4468289180Speter        }
4469289180Speter
4470289180Speter      /* create the connection, configure ports etc. */
4471289180Speter      connection->conn
4472362181Sdim        = svn_ra_svn_create_conn5(connection->usock, NULL, NULL,
4473289180Speter                                  connection->params->compression_level,
4474289180Speter                                  connection->params->zero_copy_limit,
4475289180Speter                                  connection->params->error_check_interval,
4476362181Sdim                                  connection->params->max_request_size,
4477362181Sdim                                  connection->params->max_response_size,
4478289180Speter                                  connection->pool);
4479289180Speter
4480289180Speter      /* Construct server baton and open the repository for the first time. */
4481289180Speter      err = construct_server_baton(&connection->baton, connection->conn,
4482289180Speter                                   connection->params, pool);
4483289180Speter    }
4484289180Speter
4485289180Speter  /* If we can't access the repo for some reason, end this connection. */
4486289180Speter  if (err)
4487289180Speter    terminate = TRUE;
4488289180Speter
4489289180Speter  /* Process incoming commands. */
4490289180Speter  while (!terminate && !err)
4491289180Speter    {
4492289180Speter      svn_pool_clear(iterpool);
4493289180Speter      if (is_busy && is_busy(connection))
4494289180Speter        {
4495289180Speter          svn_boolean_t has_command;
4496289180Speter
4497289180Speter          /* If the server is busy, execute just one command and only if
4498289180Speter           * there is one currently waiting in our receive buffers.
4499289180Speter           */
4500289180Speter          err = svn_ra_svn__has_command(&has_command, &terminate,
4501289180Speter                                        connection->conn, iterpool);
4502289180Speter          if (!err && has_command)
4503289180Speter            err = svn_ra_svn__handle_command(&terminate, cmd_hash,
4504289180Speter                                             connection->baton,
4505289180Speter                                             connection->conn,
4506289180Speter                                             FALSE, iterpool);
4507289180Speter
4508289180Speter          break;
4509289180Speter        }
4510289180Speter      else
4511289180Speter        {
4512289180Speter          /* The server is not busy, thus let's serve whichever command
4513289180Speter           * comes in next and whenever it comes in.  This requires the
4514289180Speter           * busy() callback test to return TRUE while there are still some
4515289180Speter           * resources left.
4516289180Speter           */
4517289180Speter          err = svn_ra_svn__handle_command(&terminate, cmd_hash,
4518289180Speter                                           connection->baton,
4519289180Speter                                           connection->conn,
4520289180Speter                                           FALSE, iterpool);
4521289180Speter        }
4522289180Speter    }
4523289180Speter
4524289180Speter  /* error or normal end of session. Close the connection */
4525289180Speter  svn_pool_destroy(iterpool);
4526289180Speter  if (terminate_p)
4527289180Speter    *terminate_p = terminate;
4528289180Speter
4529289180Speter  return svn_error_trace(err);
4530289180Speter}
4531289180Speter
4532289180Spetersvn_error_t *serve(svn_ra_svn_conn_t *conn,
4533289180Speter                   serve_params_t *params,
4534289180Speter                   apr_pool_t *pool)
4535289180Speter{
4536289180Speter  server_baton_t *baton = NULL;
4537289180Speter
4538289180Speter  SVN_ERR(construct_server_baton(&baton, conn, params, pool));
4539289180Speter  return svn_ra_svn__handle_commands2(conn, pool, main_commands, baton, FALSE);
4540289180Speter}
4541