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"
64251881Speter
65251881Spetertypedef struct commit_callback_baton_t {
66251881Speter  apr_pool_t *pool;
67251881Speter  svn_revnum_t *new_rev;
68251881Speter  const char **date;
69251881Speter  const char **author;
70251881Speter  const char **post_commit_err;
71251881Speter} commit_callback_baton_t;
72251881Speter
73251881Spetertypedef struct report_driver_baton_t {
74251881Speter  server_baton_t *sb;
75251881Speter  const char *repos_url;  /* Decoded repository URL. */
76251881Speter  void *report_baton;
77251881Speter  svn_error_t *err;
78251881Speter  /* so update() can distinguish checkout from update in logging */
79251881Speter  int entry_counter;
80251881Speter  svn_boolean_t only_empty_entries;
81251881Speter  /* for diff() logging */
82251881Speter  svn_revnum_t *from_rev;
83251881Speter} report_driver_baton_t;
84251881Speter
85251881Spetertypedef struct log_baton_t {
86251881Speter  const char *fs_path;
87251881Speter  svn_ra_svn_conn_t *conn;
88251881Speter  int stack_depth;
89251881Speter} log_baton_t;
90251881Speter
91251881Spetertypedef struct file_revs_baton_t {
92251881Speter  svn_ra_svn_conn_t *conn;
93251881Speter  apr_pool_t *pool;  /* Pool provided in the handler call. */
94251881Speter} file_revs_baton_t;
95251881Speter
96251881Spetertypedef struct fs_warning_baton_t {
97251881Speter  server_baton_t *server;
98251881Speter  svn_ra_svn_conn_t *conn;
99251881Speter  apr_pool_t *pool;
100251881Speter} fs_warning_baton_t;
101251881Speter
102251881Spetertypedef struct authz_baton_t {
103251881Speter  server_baton_t *server;
104251881Speter  svn_ra_svn_conn_t *conn;
105251881Speter} authz_baton_t;
106251881Speter
107251881Speter/* Write LEN bytes of ERRSTR to LOG_FILE with svn_io_file_write(). */
108251881Speterstatic svn_error_t *
109251881Speterlog_write(apr_file_t *log_file, const char *errstr, apr_size_t len,
110251881Speter          apr_pool_t *pool)
111251881Speter{
112251881Speter  return svn_io_file_write(log_file, errstr, &len, pool);
113251881Speter}
114251881Speter
115251881Spetervoid
116251881Speterlog_error(svn_error_t *err, apr_file_t *log_file, const char *remote_host,
117251881Speter          const char *user, const char *repos, apr_pool_t *pool)
118251881Speter{
119251881Speter  const char *timestr, *continuation;
120251881Speter  char errbuf[256];
121251881Speter  /* 8192 from MAX_STRING_LEN in from httpd-2.2.4/include/httpd.h */
122251881Speter  char errstr[8192];
123251881Speter
124251881Speter  if (err == SVN_NO_ERROR)
125251881Speter    return;
126251881Speter
127251881Speter  if (log_file == NULL)
128251881Speter    return;
129251881Speter
130251881Speter  timestr = svn_time_to_cstring(apr_time_now(), pool);
131251881Speter  remote_host = (remote_host ? remote_host : "-");
132251881Speter  user = (user ? user : "-");
133251881Speter  repos = (repos ? repos : "-");
134251881Speter
135251881Speter  continuation = "";
136251881Speter  while (err != NULL)
137251881Speter    {
138251881Speter      const char *message = svn_err_best_message(err, errbuf, sizeof(errbuf));
139251881Speter      /* based on httpd-2.2.4/server/log.c:log_error_core */
140251881Speter      apr_size_t len = apr_snprintf(errstr, sizeof(errstr),
141251881Speter                                    "%" APR_PID_T_FMT
142251881Speter                                    " %s %s %s %s ERR%s %s %ld %d ",
143251881Speter                                    getpid(), timestr, remote_host, user,
144251881Speter                                    repos, continuation,
145251881Speter                                    err->file ? err->file : "-", err->line,
146251881Speter                                    err->apr_err);
147251881Speter
148251881Speter      len += escape_errorlog_item(errstr + len, message,
149251881Speter                                  sizeof(errstr) - len);
150251881Speter      /* Truncate for the terminator (as apr_snprintf does) */
151251881Speter      if (len > sizeof(errstr) - sizeof(APR_EOL_STR)) {
152251881Speter        len = sizeof(errstr) - sizeof(APR_EOL_STR);
153251881Speter      }
154251881Speter      strcpy(errstr + len, APR_EOL_STR);
155251881Speter      len += strlen(APR_EOL_STR);
156251881Speter      svn_error_clear(log_write(log_file, errstr, len, pool));
157251881Speter
158251881Speter      continuation = "-";
159251881Speter      err = err->child;
160251881Speter    }
161251881Speter}
162251881Speter
163251881Speter/* Call log_error with log_file, remote_host, user, and repos
164251881Speter   arguments from SERVER and CONN. */
165251881Speterstatic void
166251881Speterlog_server_error(svn_error_t *err, server_baton_t *server,
167251881Speter                 svn_ra_svn_conn_t *conn, apr_pool_t *pool)
168251881Speter{
169251881Speter  log_error(err, server->log_file, svn_ra_svn_conn_remote_host(conn),
170251881Speter            server->user, server->repos_name, pool);
171251881Speter}
172251881Speter
173251881Speter/* svn_error_create() a new error, log_server_error() it, and
174251881Speter   return it. */
175251881Speterstatic svn_error_t *
176251881Spetererror_create_and_log(apr_status_t apr_err, svn_error_t *child,
177251881Speter                     const char *message, server_baton_t *server,
178251881Speter                     svn_ra_svn_conn_t *conn, apr_pool_t *pool)
179251881Speter{
180251881Speter  svn_error_t *err = svn_error_create(apr_err, child, message);
181251881Speter  log_server_error(err, server, conn, pool);
182251881Speter  return err;
183251881Speter}
184251881Speter
185251881Speter/* Log a failure ERR, transmit ERR back to the client (as part of a
186251881Speter   "failure" notification), consume ERR, and flush the connection. */
187251881Speterstatic svn_error_t *
188251881Speterlog_fail_and_flush(svn_error_t *err, server_baton_t *server,
189251881Speter                   svn_ra_svn_conn_t *conn, apr_pool_t *pool)
190251881Speter{
191251881Speter  svn_error_t *io_err;
192251881Speter
193251881Speter  log_server_error(err, server, conn, pool);
194251881Speter  io_err = svn_ra_svn__write_cmd_failure(conn, pool, err);
195251881Speter  svn_error_clear(err);
196251881Speter  SVN_ERR(io_err);
197251881Speter  return svn_ra_svn__flush(conn, pool);
198251881Speter}
199251881Speter
200251881Speter/* Log a client command. */
201251881Speterstatic svn_error_t *log_command(server_baton_t *b,
202251881Speter                                svn_ra_svn_conn_t *conn,
203251881Speter                                apr_pool_t *pool,
204251881Speter                                const char *fmt, ...)
205251881Speter{
206251881Speter  const char *remote_host, *timestr, *log, *line;
207251881Speter  va_list ap;
208251881Speter  apr_size_t nbytes;
209251881Speter
210251881Speter  if (b->log_file == NULL)
211251881Speter    return SVN_NO_ERROR;
212251881Speter
213251881Speter  remote_host = svn_ra_svn_conn_remote_host(conn);
214251881Speter  timestr = svn_time_to_cstring(apr_time_now(), pool);
215251881Speter
216251881Speter  va_start(ap, fmt);
217251881Speter  log = apr_pvsprintf(pool, fmt, ap);
218251881Speter  va_end(ap);
219251881Speter
220251881Speter  line = apr_psprintf(pool, "%" APR_PID_T_FMT
221251881Speter                      " %s %s %s %s %s" APR_EOL_STR,
222251881Speter                      getpid(), timestr,
223251881Speter                      (remote_host ? remote_host : "-"),
224251881Speter                      (b->user ? b->user : "-"), b->repos_name, log);
225251881Speter  nbytes = strlen(line);
226251881Speter
227251881Speter  return log_write(b->log_file, line, nbytes, pool);
228251881Speter}
229251881Speter
230251881Speter/* Log an authz failure */
231251881Speterstatic svn_error_t *
232251881Speterlog_authz_denied(const char *path,
233251881Speter                 svn_repos_authz_access_t required,
234251881Speter                 server_baton_t *b,
235251881Speter                 svn_ra_svn_conn_t *conn,
236251881Speter                 apr_pool_t *pool)
237251881Speter{
238251881Speter  const char *timestr, *remote_host, *line;
239251881Speter
240251881Speter  if (b->log_file == NULL)
241251881Speter    return SVN_NO_ERROR;
242251881Speter
243251881Speter  if (!b->user)
244251881Speter    return SVN_NO_ERROR;
245251881Speter
246251881Speter  timestr = svn_time_to_cstring(apr_time_now(), pool);
247251881Speter  remote_host = svn_ra_svn_conn_remote_host(conn);
248251881Speter
249251881Speter  line = apr_psprintf(pool, "%" APR_PID_T_FMT
250251881Speter                      " %s %s %s %s Authorization Failed %s%s %s" APR_EOL_STR,
251251881Speter                      getpid(), timestr,
252251881Speter                      (remote_host ? remote_host : "-"),
253251881Speter                      (b->user ? b->user : "-"),
254251881Speter                      b->repos_name,
255251881Speter                      (required & svn_authz_recursive ? "recursive " : ""),
256251881Speter                      (required & svn_authz_write ? "write" : "read"),
257251881Speter                      (path && path[0] ? path : "/"));
258251881Speter
259251881Speter  return log_write(b->log_file, line, strlen(line), pool);
260251881Speter}
261251881Speter
262251881Speter
263251881Spetersvn_error_t *load_pwdb_config(server_baton_t *server,
264251881Speter                              svn_ra_svn_conn_t *conn,
265251881Speter                              apr_pool_t *pool)
266251881Speter{
267251881Speter  const char *pwdb_path;
268251881Speter  svn_error_t *err;
269251881Speter
270251881Speter  svn_config_get(server->cfg, &pwdb_path, SVN_CONFIG_SECTION_GENERAL,
271251881Speter                 SVN_CONFIG_OPTION_PASSWORD_DB, NULL);
272251881Speter
273251881Speter  server->pwdb = NULL;
274251881Speter  if (pwdb_path)
275251881Speter    {
276251881Speter      pwdb_path = svn_dirent_internal_style(pwdb_path, pool);
277251881Speter      pwdb_path = svn_dirent_join(server->base, pwdb_path, pool);
278251881Speter
279251881Speter      err = svn_config_read3(&server->pwdb, pwdb_path, TRUE,
280251881Speter                             FALSE, FALSE, pool);
281251881Speter      if (err)
282251881Speter        {
283251881Speter          log_server_error(err, server, conn, pool);
284251881Speter
285251881Speter          /* Because it may be possible to read the pwdb file with some
286251881Speter             access methods and not others, ignore errors reading the pwdb
287251881Speter             file and just don't present password authentication as an
288251881Speter             option.  Also, some authentications (e.g. --tunnel) can
289251881Speter             proceed without it anyway.
290251881Speter
291251881Speter             ### Not entirely sure why SVN_ERR_BAD_FILENAME is checked
292251881Speter             ### for here.  That seems to have been introduced in r856914,
293251881Speter             ### and only in r870942 was the APR_EACCES check introduced. */
294251881Speter          if (err->apr_err != SVN_ERR_BAD_FILENAME
295251881Speter              && ! APR_STATUS_IS_EACCES(err->apr_err))
296251881Speter            {
297251881Speter                /* Now that we've logged the error, clear it and return a
298251881Speter                 * nice, generic error to the user:
299251881Speter                 * http://subversion.tigris.org/issues/show_bug.cgi?id=2271 */
300251881Speter                svn_error_clear(err);
301251881Speter                return svn_error_create(SVN_ERR_AUTHN_FAILED, NULL, NULL);
302251881Speter            }
303251881Speter          else
304251881Speter            /* Ignore SVN_ERR_BAD_FILENAME and APR_EACCES and proceed. */
305251881Speter            svn_error_clear(err);
306251881Speter        }
307251881Speter    }
308251881Speter
309251881Speter  return SVN_NO_ERROR;
310251881Speter}
311251881Speter
312251881Speter/* Canonicalize *ACCESS_FILE based on the type of argument.  Results are
313251881Speter * placed in *ACCESS_FILE.  SERVER baton is used to convert relative paths to
314251881Speter * absolute paths rooted at the server root.  REPOS_ROOT is used to calculate
315251881Speter * an absolute URL for repos-relative URLs. */
316251881Speterstatic svn_error_t *
317251881Spetercanonicalize_access_file(const char **access_file, server_baton_t *server,
318251881Speter                         const char *repos_root, apr_pool_t *pool)
319251881Speter{
320251881Speter  if (svn_path_is_url(*access_file))
321251881Speter    {
322251881Speter      *access_file = svn_uri_canonicalize(*access_file, pool);
323251881Speter    }
324251881Speter  else if (svn_path_is_repos_relative_url(*access_file))
325251881Speter    {
326251881Speter      const char *repos_root_url;
327251881Speter
328251881Speter      SVN_ERR(svn_uri_get_file_url_from_dirent(&repos_root_url, repos_root,
329251881Speter                                               pool));
330251881Speter      SVN_ERR(svn_path_resolve_repos_relative_url(access_file, *access_file,
331251881Speter                                                  repos_root_url, pool));
332251881Speter      *access_file = svn_uri_canonicalize(*access_file, pool);
333251881Speter    }
334251881Speter  else
335251881Speter    {
336251881Speter      *access_file = svn_dirent_internal_style(*access_file, pool);
337251881Speter      *access_file = svn_dirent_join(server->base, *access_file, pool);
338251881Speter    }
339251881Speter
340251881Speter  return SVN_NO_ERROR;
341251881Speter}
342251881Speter
343251881Spetersvn_error_t *load_authz_config(server_baton_t *server,
344251881Speter                               svn_ra_svn_conn_t *conn,
345251881Speter                               const char *repos_root,
346251881Speter                               apr_pool_t *pool)
347251881Speter{
348251881Speter  const char *authzdb_path;
349251881Speter  const char *groupsdb_path;
350251881Speter  svn_error_t *err;
351251881Speter
352251881Speter  /* Read authz configuration. */
353251881Speter  svn_config_get(server->cfg, &authzdb_path, SVN_CONFIG_SECTION_GENERAL,
354251881Speter                 SVN_CONFIG_OPTION_AUTHZ_DB, NULL);
355251881Speter
356251881Speter  svn_config_get(server->cfg, &groupsdb_path, SVN_CONFIG_SECTION_GENERAL,
357251881Speter                 SVN_CONFIG_OPTION_GROUPS_DB, NULL);
358251881Speter
359251881Speter  if (authzdb_path)
360251881Speter    {
361251881Speter      const char *case_force_val;
362251881Speter
363251881Speter      /* Canonicalize and add the base onto the authzdb_path (if needed). */
364251881Speter      err = canonicalize_access_file(&authzdb_path, server,
365251881Speter                                     repos_root, pool);
366251881Speter
367251881Speter      /* Same for the groupsdb_path if it is present. */
368251881Speter      if (groupsdb_path && !err)
369251881Speter        err = canonicalize_access_file(&groupsdb_path, server,
370251881Speter                                       repos_root, pool);
371251881Speter
372251881Speter      if (!err)
373251881Speter        err = svn_repos_authz_read2(&server->authzdb, authzdb_path,
374251881Speter                                    groupsdb_path, TRUE, pool);
375251881Speter
376251881Speter      if (err)
377251881Speter        {
378251881Speter          log_server_error(err, server, conn, pool);
379251881Speter          svn_error_clear(err);
380251881Speter          return svn_error_create(SVN_ERR_AUTHZ_INVALID_CONFIG, NULL, NULL);
381251881Speter        }
382251881Speter
383251881Speter      /* Are we going to be case-normalizing usernames when we consult
384251881Speter       * this authz file? */
385251881Speter      svn_config_get(server->cfg, &case_force_val, SVN_CONFIG_SECTION_GENERAL,
386251881Speter                     SVN_CONFIG_OPTION_FORCE_USERNAME_CASE, NULL);
387251881Speter      if (case_force_val)
388251881Speter        {
389251881Speter          if (strcmp(case_force_val, "upper") == 0)
390251881Speter            server->username_case = CASE_FORCE_UPPER;
391251881Speter          else if (strcmp(case_force_val, "lower") == 0)
392251881Speter            server->username_case = CASE_FORCE_LOWER;
393251881Speter          else
394251881Speter            server->username_case = CASE_ASIS;
395251881Speter        }
396251881Speter    }
397251881Speter  else
398251881Speter    {
399251881Speter      server->authzdb = NULL;
400251881Speter      server->username_case = CASE_ASIS;
401251881Speter    }
402251881Speter
403251881Speter  return SVN_NO_ERROR;
404251881Speter}
405251881Speter
406251881Speter/* Set *FS_PATH to the portion of URL that is the path within the
407251881Speter   repository, if URL is inside REPOS_URL (if URL is not inside
408251881Speter   REPOS_URL, then error, with the effect on *FS_PATH undefined).
409251881Speter
410251881Speter   If the resultant fs path would be the empty string (i.e., URL and
411251881Speter   REPOS_URL are the same), then set *FS_PATH to "/".
412251881Speter
413251881Speter   Assume that REPOS_URL and URL are already URI-decoded. */
414251881Speterstatic svn_error_t *get_fs_path(const char *repos_url, const char *url,
415251881Speter                                const char **fs_path)
416251881Speter{
417251881Speter  apr_size_t len;
418251881Speter
419251881Speter  len = strlen(repos_url);
420251881Speter  if (strncmp(url, repos_url, len) != 0)
421251881Speter    return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
422251881Speter                             "'%s' is not the same repository as '%s'",
423251881Speter                             url, repos_url);
424251881Speter  *fs_path = url + len;
425251881Speter  if (! **fs_path)
426251881Speter    *fs_path = "/";
427251881Speter
428251881Speter  return SVN_NO_ERROR;
429251881Speter}
430251881Speter
431251881Speter/* --- AUTHENTICATION AND AUTHORIZATION FUNCTIONS --- */
432251881Speter
433251881Speter/* Convert TEXT to upper case if TO_UPPERCASE is TRUE, else
434251881Speter   converts it to lower case. */
435251881Speterstatic void convert_case(char *text, svn_boolean_t to_uppercase)
436251881Speter{
437251881Speter  char *c = text;
438251881Speter  while (*c)
439251881Speter    {
440251881Speter      *c = (char)(to_uppercase ? apr_toupper(*c) : apr_tolower(*c));
441251881Speter      ++c;
442251881Speter    }
443251881Speter}
444251881Speter
445251881Speter/* Set *ALLOWED to TRUE if PATH is accessible in the REQUIRED mode to
446251881Speter   the user described in BATON according to the authz rules in BATON.
447251881Speter   Use POOL for temporary allocations only.  If no authz rules are
448251881Speter   present in BATON, grant access by default. */
449251881Speterstatic svn_error_t *authz_check_access(svn_boolean_t *allowed,
450251881Speter                                       const char *path,
451251881Speter                                       svn_repos_authz_access_t required,
452251881Speter                                       server_baton_t *b,
453251881Speter                                       svn_ra_svn_conn_t *conn,
454251881Speter                                       apr_pool_t *pool)
455251881Speter{
456251881Speter  /* If authz cannot be performed, grant access.  This is NOT the same
457251881Speter     as the default policy when authz is performed on a path with no
458251881Speter     rules.  In the latter case, the default is to deny access, and is
459251881Speter     set by svn_repos_authz_check_access. */
460251881Speter  if (!b->authzdb)
461251881Speter    {
462251881Speter      *allowed = TRUE;
463251881Speter      return SVN_NO_ERROR;
464251881Speter    }
465251881Speter
466251881Speter  /* If the authz request is for the empty path (ie. ""), replace it
467251881Speter     with the root path.  This happens because of stripping done at
468251881Speter     various levels in svnserve that remove the leading / on an
469251881Speter     absolute path. Passing such a malformed path to the authz
470251881Speter     routines throws them into an infinite loop and makes them miss
471251881Speter     ACLs. */
472251881Speter  if (path)
473251881Speter    path = svn_fspath__canonicalize(path, pool);
474251881Speter
475251881Speter  /* If we have a username, and we've not yet used it + any username
476251881Speter     case normalization that might be requested to determine "the
477251881Speter     username we used for authz purposes", do so now. */
478251881Speter  if (b->user && (! b->authz_user))
479251881Speter    {
480251881Speter      char *authz_user = apr_pstrdup(b->pool, b->user);
481251881Speter      if (b->username_case == CASE_FORCE_UPPER)
482251881Speter        convert_case(authz_user, TRUE);
483251881Speter      else if (b->username_case == CASE_FORCE_LOWER)
484251881Speter        convert_case(authz_user, FALSE);
485251881Speter      b->authz_user = authz_user;
486251881Speter    }
487251881Speter
488251881Speter  SVN_ERR(svn_repos_authz_check_access(b->authzdb, b->authz_repos_name,
489251881Speter                                       path, b->authz_user, required,
490251881Speter                                       allowed, pool));
491251881Speter  if (!*allowed)
492251881Speter    SVN_ERR(log_authz_denied(path, required, b, conn, pool));
493251881Speter
494251881Speter  return SVN_NO_ERROR;
495251881Speter}
496251881Speter
497251881Speter/* Set *ALLOWED to TRUE if PATH is readable by the user described in
498251881Speter * BATON.  Use POOL for temporary allocations only.  ROOT is not used.
499251881Speter * Implements the svn_repos_authz_func_t interface.
500251881Speter */
501251881Speterstatic svn_error_t *authz_check_access_cb(svn_boolean_t *allowed,
502251881Speter                                          svn_fs_root_t *root,
503251881Speter                                          const char *path,
504251881Speter                                          void *baton,
505251881Speter                                          apr_pool_t *pool)
506251881Speter{
507251881Speter  authz_baton_t *sb = baton;
508251881Speter
509251881Speter  return authz_check_access(allowed, path, svn_authz_read,
510251881Speter                            sb->server, sb->conn, pool);
511251881Speter}
512251881Speter
513251881Speter/* If authz is enabled in the specified BATON, return a read authorization
514251881Speter   function. Otherwise, return NULL. */
515251881Speterstatic svn_repos_authz_func_t authz_check_access_cb_func(server_baton_t *baton)
516251881Speter{
517251881Speter  if (baton->authzdb)
518251881Speter     return authz_check_access_cb;
519251881Speter  return NULL;
520251881Speter}
521251881Speter
522251881Speter/* Set *ALLOWED to TRUE if the REQUIRED access to PATH is granted,
523251881Speter * according to the state in BATON.  Use POOL for temporary
524251881Speter * allocations only.  ROOT is not used.  Implements the
525251881Speter * svn_repos_authz_callback_t interface.
526251881Speter */
527251881Speterstatic svn_error_t *authz_commit_cb(svn_repos_authz_access_t required,
528251881Speter                                    svn_boolean_t *allowed,
529251881Speter                                    svn_fs_root_t *root,
530251881Speter                                    const char *path,
531251881Speter                                    void *baton,
532251881Speter                                    apr_pool_t *pool)
533251881Speter{
534251881Speter  authz_baton_t *sb = baton;
535251881Speter
536251881Speter  return authz_check_access(allowed, path, required,
537251881Speter                            sb->server, sb->conn, pool);
538251881Speter}
539251881Speter
540251881Speter
541251881Speterenum access_type get_access(server_baton_t *b, enum authn_type auth)
542251881Speter{
543251881Speter  const char *var = (auth == AUTHENTICATED) ? SVN_CONFIG_OPTION_AUTH_ACCESS :
544251881Speter    SVN_CONFIG_OPTION_ANON_ACCESS;
545251881Speter  const char *val, *def = (auth == AUTHENTICATED) ? "write" : "read";
546251881Speter  enum access_type result;
547251881Speter
548251881Speter  svn_config_get(b->cfg, &val, SVN_CONFIG_SECTION_GENERAL, var, def);
549251881Speter  result = (strcmp(val, "write") == 0 ? WRITE_ACCESS :
550251881Speter            strcmp(val, "read") == 0 ? READ_ACCESS : NO_ACCESS);
551251881Speter  return (result == WRITE_ACCESS && b->read_only) ? READ_ACCESS : result;
552251881Speter}
553251881Speter
554251881Speterstatic enum access_type current_access(server_baton_t *b)
555251881Speter{
556251881Speter  return get_access(b, (b->user) ? AUTHENTICATED : UNAUTHENTICATED);
557251881Speter}
558251881Speter
559251881Speter/* Send authentication mechs for ACCESS_TYPE to the client.  If NEEDS_USERNAME
560251881Speter   is true, don't send anonymous mech even if that would give the desired
561251881Speter   access. */
562251881Speterstatic svn_error_t *send_mechs(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
563251881Speter                               server_baton_t *b, enum access_type required,
564251881Speter                               svn_boolean_t needs_username)
565251881Speter{
566251881Speter  if (!needs_username && get_access(b, UNAUTHENTICATED) >= required)
567251881Speter    SVN_ERR(svn_ra_svn__write_word(conn, pool, "ANONYMOUS"));
568251881Speter  if (b->tunnel_user && get_access(b, AUTHENTICATED) >= required)
569251881Speter    SVN_ERR(svn_ra_svn__write_word(conn, pool, "EXTERNAL"));
570251881Speter  if (b->pwdb && get_access(b, AUTHENTICATED) >= required)
571251881Speter    SVN_ERR(svn_ra_svn__write_word(conn, pool, "CRAM-MD5"));
572251881Speter  return SVN_NO_ERROR;
573251881Speter}
574251881Speter
575251881Speter/* Context for cleanup handler. */
576251881Speterstruct cleanup_fs_access_baton
577251881Speter{
578251881Speter  svn_fs_t *fs;
579251881Speter  apr_pool_t *pool;
580251881Speter};
581251881Speter
582251881Speter/* Pool cleanup handler.  Make sure fs's access_t points to NULL when
583251881Speter   the command pool is destroyed. */
584251881Speterstatic apr_status_t cleanup_fs_access(void *data)
585251881Speter{
586251881Speter  svn_error_t *serr;
587251881Speter  struct cleanup_fs_access_baton *baton = data;
588251881Speter
589251881Speter  serr = svn_fs_set_access(baton->fs, NULL);
590251881Speter  if (serr)
591251881Speter    {
592251881Speter      apr_status_t apr_err = serr->apr_err;
593251881Speter      svn_error_clear(serr);
594251881Speter      return apr_err;
595251881Speter    }
596251881Speter
597251881Speter  return APR_SUCCESS;
598251881Speter}
599251881Speter
600251881Speter
601251881Speter/* Create an svn_fs_access_t in POOL for USER and associate it with
602251881Speter   B's filesystem.  Also, register a cleanup handler with POOL which
603251881Speter   de-associates the svn_fs_access_t from B's filesystem. */
604251881Speterstatic svn_error_t *
605251881Spetercreate_fs_access(server_baton_t *b, apr_pool_t *pool)
606251881Speter{
607251881Speter  svn_fs_access_t *fs_access;
608251881Speter  struct cleanup_fs_access_baton *cleanup_baton;
609251881Speter
610251881Speter  if (!b->user)
611251881Speter    return SVN_NO_ERROR;
612251881Speter
613251881Speter  SVN_ERR(svn_fs_create_access(&fs_access, b->user, pool));
614251881Speter  SVN_ERR(svn_fs_set_access(b->fs, fs_access));
615251881Speter
616251881Speter  cleanup_baton = apr_pcalloc(pool, sizeof(*cleanup_baton));
617251881Speter  cleanup_baton->pool = pool;
618251881Speter  cleanup_baton->fs = b->fs;
619251881Speter  apr_pool_cleanup_register(pool, cleanup_baton, cleanup_fs_access,
620251881Speter                            apr_pool_cleanup_null);
621251881Speter
622251881Speter  return SVN_NO_ERROR;
623251881Speter}
624251881Speter
625251881Speter/* Authenticate, once the client has chosen a mechanism and possibly
626251881Speter * sent an initial mechanism token.  On success, set *success to true
627251881Speter * and b->user to the authenticated username (or NULL for anonymous).
628251881Speter * On authentication failure, report failure to the client and set
629251881Speter * *success to FALSE.  On communications failure, return an error.
630251881Speter * If NEEDS_USERNAME is TRUE, don't allow anonymous authentication. */
631251881Speterstatic svn_error_t *auth(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
632251881Speter                         const char *mech, const char *mecharg,
633251881Speter                         server_baton_t *b, enum access_type required,
634251881Speter                         svn_boolean_t needs_username,
635251881Speter                         svn_boolean_t *success)
636251881Speter{
637251881Speter  const char *user;
638251881Speter  *success = FALSE;
639251881Speter
640251881Speter  if (get_access(b, AUTHENTICATED) >= required
641251881Speter      && b->tunnel_user && strcmp(mech, "EXTERNAL") == 0)
642251881Speter    {
643251881Speter      if (*mecharg && strcmp(mecharg, b->tunnel_user) != 0)
644251881Speter        return svn_ra_svn__write_tuple(conn, pool, "w(c)", "failure",
645251881Speter                                       "Requested username does not match");
646251881Speter      b->user = b->tunnel_user;
647251881Speter      SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w()", "success"));
648251881Speter      *success = TRUE;
649251881Speter      return SVN_NO_ERROR;
650251881Speter    }
651251881Speter
652251881Speter  if (get_access(b, UNAUTHENTICATED) >= required
653251881Speter      && strcmp(mech, "ANONYMOUS") == 0 && ! needs_username)
654251881Speter    {
655251881Speter      SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w()", "success"));
656251881Speter      *success = TRUE;
657251881Speter      return SVN_NO_ERROR;
658251881Speter    }
659251881Speter
660251881Speter  if (get_access(b, AUTHENTICATED) >= required
661251881Speter      && b->pwdb && strcmp(mech, "CRAM-MD5") == 0)
662251881Speter    {
663251881Speter      SVN_ERR(svn_ra_svn_cram_server(conn, pool, b->pwdb, &user, success));
664251881Speter      b->user = apr_pstrdup(b->pool, user);
665251881Speter      return SVN_NO_ERROR;
666251881Speter    }
667251881Speter
668251881Speter  return svn_ra_svn__write_tuple(conn, pool, "w(c)", "failure",
669251881Speter                                "Must authenticate with listed mechanism");
670251881Speter}
671251881Speter
672251881Speter/* Perform an authentication request using the built-in SASL implementation. */
673251881Speterstatic svn_error_t *
674251881Speterinternal_auth_request(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
675251881Speter                      server_baton_t *b, enum access_type required,
676251881Speter                      svn_boolean_t needs_username)
677251881Speter{
678251881Speter  svn_boolean_t success;
679251881Speter  const char *mech, *mecharg;
680251881Speter
681251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success"));
682251881Speter  SVN_ERR(send_mechs(conn, pool, b, required, needs_username));
683251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)c)", b->realm));
684251881Speter  do
685251881Speter    {
686251881Speter      SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "w(?c)", &mech, &mecharg));
687251881Speter      if (!*mech)
688251881Speter        break;
689251881Speter      SVN_ERR(auth(conn, pool, mech, mecharg, b, required, needs_username,
690251881Speter                   &success));
691251881Speter    }
692251881Speter  while (!success);
693251881Speter  return SVN_NO_ERROR;
694251881Speter}
695251881Speter
696251881Speter/* Perform an authentication request in order to get an access level of
697251881Speter * REQUIRED or higher.  Since the client may escape the authentication
698251881Speter * exchange, the caller should check current_access(b) to see if
699251881Speter * authentication succeeded. */
700251881Speterstatic svn_error_t *auth_request(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
701251881Speter                                 server_baton_t *b, enum access_type required,
702251881Speter                                 svn_boolean_t needs_username)
703251881Speter{
704251881Speter#ifdef SVN_HAVE_SASL
705251881Speter  if (b->use_sasl)
706251881Speter    return cyrus_auth_request(conn, pool, b, required, needs_username);
707251881Speter#endif
708251881Speter
709251881Speter  return internal_auth_request(conn, pool, b, required, needs_username);
710251881Speter}
711251881Speter
712251881Speter/* Send a trivial auth notification on CONN which lists no mechanisms,
713251881Speter * indicating that authentication is unnecessary.  Usually called in
714251881Speter * response to invocation of a svnserve command.
715251881Speter */
716251881Speterstatic svn_error_t *trivial_auth_request(svn_ra_svn_conn_t *conn,
717251881Speter                                         apr_pool_t *pool, server_baton_t *b)
718251881Speter{
719251881Speter  return svn_ra_svn__write_cmd_response(conn, pool, "()c", "");
720251881Speter}
721251881Speter
722251881Speter/* Ensure that the client has the REQUIRED access by checking the
723251881Speter * access directives (both blanket and per-directory) in BATON.  If
724251881Speter * PATH is NULL, then only the blanket access configuration will
725251881Speter * impact the result.
726251881Speter *
727251881Speter * If NEEDS_USERNAME is TRUE, then a lookup is only successful if the
728251881Speter * user described in BATON is authenticated and, well, has a username
729251881Speter * assigned to him.
730251881Speter *
731251881Speter * Use POOL for temporary allocations only.
732251881Speter */
733251881Speterstatic svn_boolean_t lookup_access(apr_pool_t *pool,
734251881Speter                                   server_baton_t *baton,
735251881Speter                                   svn_ra_svn_conn_t *conn,
736251881Speter                                   svn_repos_authz_access_t required,
737251881Speter                                   const char *path,
738251881Speter                                   svn_boolean_t needs_username)
739251881Speter{
740251881Speter  enum access_type req = (required & svn_authz_write) ?
741251881Speter    WRITE_ACCESS : READ_ACCESS;
742251881Speter  svn_boolean_t authorized;
743251881Speter  svn_error_t *err;
744251881Speter
745251881Speter  /* Get authz's opinion on the access. */
746251881Speter  err = authz_check_access(&authorized, path, required, baton, conn, pool);
747251881Speter
748251881Speter  /* If an error made lookup fail, deny access. */
749251881Speter  if (err)
750251881Speter    {
751251881Speter      log_server_error(err, baton, conn, pool);
752251881Speter      svn_error_clear(err);
753251881Speter      return FALSE;
754251881Speter    }
755251881Speter
756251881Speter  /* If the required access is blanket-granted AND granted by authz
757251881Speter     AND we already have a username if one is required, then the
758251881Speter     lookup has succeeded. */
759251881Speter  if (current_access(baton) >= req
760251881Speter      && authorized
761251881Speter      && (! needs_username || baton->user))
762251881Speter    return TRUE;
763251881Speter
764251881Speter  return FALSE;
765251881Speter}
766251881Speter
767251881Speter/* Check that the client has the REQUIRED access by consulting the
768251881Speter * authentication and authorization states stored in BATON.  If the
769251881Speter * client does not have the required access credentials, attempt to
770251881Speter * authenticate the client to get that access, using CONN for
771251881Speter * communication.
772251881Speter *
773251881Speter * This function is supposed to be called to handle the authentication
774251881Speter * half of a standard svn protocol reply.  If an error is returned, it
775251881Speter * probably means that the server can terminate the client connection
776251881Speter * with an apologetic error, as it implies an authentication failure.
777251881Speter *
778251881Speter * PATH and NEEDS_USERNAME are passed along to lookup_access, their
779251881Speter * behaviour is documented there.
780251881Speter */
781251881Speterstatic svn_error_t *must_have_access(svn_ra_svn_conn_t *conn,
782251881Speter                                     apr_pool_t *pool,
783251881Speter                                     server_baton_t *b,
784251881Speter                                     svn_repos_authz_access_t required,
785251881Speter                                     const char *path,
786251881Speter                                     svn_boolean_t needs_username)
787251881Speter{
788251881Speter  enum access_type req = (required & svn_authz_write) ?
789251881Speter    WRITE_ACCESS : READ_ACCESS;
790251881Speter
791251881Speter  /* See whether the user already has the required access.  If so,
792251881Speter     nothing needs to be done.  Create the FS access and send a
793251881Speter     trivial auth request. */
794251881Speter  if (lookup_access(pool, b, conn, required, path, needs_username))
795251881Speter    {
796251881Speter      SVN_ERR(create_fs_access(b, pool));
797251881Speter      return trivial_auth_request(conn, pool, b);
798251881Speter    }
799251881Speter
800251881Speter  /* If the required blanket access can be obtained by authenticating,
801251881Speter     try that.  Unfortunately, we can't tell until after
802251881Speter     authentication whether authz will work or not.  We force
803251881Speter     requiring a username because we need one to be able to check
804251881Speter     authz configuration again with a different user credentials than
805251881Speter     the first time round. */
806251881Speter  if (b->user == NULL
807251881Speter      && get_access(b, AUTHENTICATED) >= req
808251881Speter      && (b->tunnel_user || b->pwdb || b->use_sasl))
809251881Speter    SVN_ERR(auth_request(conn, pool, b, req, TRUE));
810251881Speter
811251881Speter  /* Now that an authentication has been done get the new take of
812251881Speter     authz on the request. */
813251881Speter  if (! lookup_access(pool, b, conn, required, path, needs_username))
814251881Speter    return svn_error_create(SVN_ERR_RA_SVN_CMD_ERR,
815251881Speter                            error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED,
816251881Speter                                                 NULL, NULL, b, conn, pool),
817251881Speter                            NULL);
818251881Speter
819251881Speter  /* Else, access is granted, and there is much rejoicing. */
820251881Speter  SVN_ERR(create_fs_access(b, pool));
821251881Speter
822251881Speter  return SVN_NO_ERROR;
823251881Speter}
824251881Speter
825251881Speter/* --- REPORTER COMMAND SET --- */
826251881Speter
827251881Speter/* To allow for pipelining, reporter commands have no reponses.  If we
828251881Speter * get an error, we ignore all subsequent reporter commands and return
829251881Speter * the error finish_report, to be handled by the calling command.
830251881Speter */
831251881Speter
832251881Speterstatic svn_error_t *set_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
833251881Speter                             apr_array_header_t *params, void *baton)
834251881Speter{
835251881Speter  report_driver_baton_t *b = baton;
836251881Speter  const char *path, *lock_token, *depth_word;
837251881Speter  svn_revnum_t rev;
838251881Speter  /* Default to infinity, for old clients that don't send depth. */
839251881Speter  svn_depth_t depth = svn_depth_infinity;
840251881Speter  svn_boolean_t start_empty;
841251881Speter
842251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "crb?(?c)?w",
843251881Speter                                 &path, &rev, &start_empty, &lock_token,
844251881Speter                                 &depth_word));
845251881Speter  if (depth_word)
846251881Speter    depth = svn_depth_from_word(depth_word);
847251881Speter  path = svn_relpath_canonicalize(path, pool);
848251881Speter  if (b->from_rev && strcmp(path, "") == 0)
849251881Speter    *b->from_rev = rev;
850251881Speter  if (!b->err)
851251881Speter    b->err = svn_repos_set_path3(b->report_baton, path, rev, depth,
852251881Speter                                 start_empty, lock_token, pool);
853251881Speter  b->entry_counter++;
854251881Speter  if (!start_empty)
855251881Speter    b->only_empty_entries = FALSE;
856251881Speter  return SVN_NO_ERROR;
857251881Speter}
858251881Speter
859251881Speterstatic svn_error_t *delete_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
860251881Speter                                apr_array_header_t *params, void *baton)
861251881Speter{
862251881Speter  report_driver_baton_t *b = baton;
863251881Speter  const char *path;
864251881Speter
865251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &path));
866251881Speter  path = svn_relpath_canonicalize(path, pool);
867251881Speter  if (!b->err)
868251881Speter    b->err = svn_repos_delete_path(b->report_baton, path, pool);
869251881Speter  return SVN_NO_ERROR;
870251881Speter}
871251881Speter
872251881Speterstatic svn_error_t *link_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
873251881Speter                              apr_array_header_t *params, void *baton)
874251881Speter{
875251881Speter  report_driver_baton_t *b = baton;
876251881Speter  const char *path, *url, *lock_token, *fs_path, *depth_word;
877251881Speter  svn_revnum_t rev;
878251881Speter  svn_boolean_t start_empty;
879251881Speter  /* Default to infinity, for old clients that don't send depth. */
880251881Speter  svn_depth_t depth = svn_depth_infinity;
881251881Speter
882251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "ccrb?(?c)?w",
883251881Speter                                 &path, &url, &rev, &start_empty,
884251881Speter                                 &lock_token, &depth_word));
885251881Speter
886251881Speter  /* ### WHAT?!  The link path is an absolute URL?!  Didn't see that
887251881Speter     coming...   -- cmpilato  */
888251881Speter  path = svn_relpath_canonicalize(path, pool);
889251881Speter  url = svn_uri_canonicalize(url, pool);
890251881Speter  if (depth_word)
891251881Speter    depth = svn_depth_from_word(depth_word);
892251881Speter  if (!b->err)
893251881Speter    b->err = get_fs_path(svn_path_uri_decode(b->repos_url, pool),
894251881Speter                         svn_path_uri_decode(url, pool),
895251881Speter                         &fs_path);
896251881Speter  if (!b->err)
897251881Speter    b->err = svn_repos_link_path3(b->report_baton, path, fs_path, rev,
898251881Speter                                  depth, start_empty, lock_token, pool);
899251881Speter  b->entry_counter++;
900251881Speter  return SVN_NO_ERROR;
901251881Speter}
902251881Speter
903251881Speterstatic svn_error_t *finish_report(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
904251881Speter                                  apr_array_header_t *params, void *baton)
905251881Speter{
906251881Speter  report_driver_baton_t *b = baton;
907251881Speter
908251881Speter  /* No arguments to parse. */
909251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b->sb));
910251881Speter  if (!b->err)
911251881Speter    b->err = svn_repos_finish_report(b->report_baton, pool);
912251881Speter  return SVN_NO_ERROR;
913251881Speter}
914251881Speter
915251881Speterstatic svn_error_t *abort_report(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
916251881Speter                                 apr_array_header_t *params, void *baton)
917251881Speter{
918251881Speter  report_driver_baton_t *b = baton;
919251881Speter
920251881Speter  /* No arguments to parse. */
921251881Speter  svn_error_clear(svn_repos_abort_report(b->report_baton, pool));
922251881Speter  return SVN_NO_ERROR;
923251881Speter}
924251881Speter
925251881Speterstatic const svn_ra_svn_cmd_entry_t report_commands[] = {
926251881Speter  { "set-path",      set_path },
927251881Speter  { "delete-path",   delete_path },
928251881Speter  { "link-path",     link_path },
929251881Speter  { "finish-report", finish_report, TRUE },
930251881Speter  { "abort-report",  abort_report,  TRUE },
931251881Speter  { NULL }
932251881Speter};
933251881Speter
934251881Speter/* Accept a report from the client, drive the network editor with the
935251881Speter * result, and then write an empty command response.  If there is a
936251881Speter * non-protocol failure, accept_report will abort the edit and return
937251881Speter * a command error to be reported by handle_commands().
938251881Speter *
939251881Speter * If only_empty_entry is not NULL and the report contains only one
940251881Speter * item, and that item is empty, set *only_empty_entry to TRUE, else
941251881Speter * set it to FALSE.
942251881Speter *
943251881Speter * If from_rev is not NULL, set *from_rev to the revision number from
944251881Speter * the set-path on ""; if somehow set-path "" never happens, set
945251881Speter * *from_rev to SVN_INVALID_REVNUM.
946251881Speter */
947251881Speterstatic svn_error_t *accept_report(svn_boolean_t *only_empty_entry,
948251881Speter                                  svn_revnum_t *from_rev,
949251881Speter                                  svn_ra_svn_conn_t *conn, apr_pool_t *pool,
950251881Speter                                  server_baton_t *b, svn_revnum_t rev,
951251881Speter                                  const char *target, const char *tgt_path,
952251881Speter                                  svn_boolean_t text_deltas,
953251881Speter                                  svn_depth_t depth,
954251881Speter                                  svn_boolean_t send_copyfrom_args,
955251881Speter                                  svn_boolean_t ignore_ancestry)
956251881Speter{
957251881Speter  const svn_delta_editor_t *editor;
958251881Speter  void *edit_baton, *report_baton;
959251881Speter  report_driver_baton_t rb;
960251881Speter  svn_error_t *err;
961251881Speter  authz_baton_t ab;
962251881Speter
963251881Speter  ab.server = b;
964251881Speter  ab.conn = conn;
965251881Speter
966251881Speter  /* Make an svn_repos report baton.  Tell it to drive the network editor
967251881Speter   * when the report is complete. */
968251881Speter  svn_ra_svn_get_editor(&editor, &edit_baton, conn, pool, NULL, NULL);
969251881Speter  SVN_CMD_ERR(svn_repos_begin_report3(&report_baton, rev, b->repos,
970251881Speter                                      b->fs_path->data, target, tgt_path,
971251881Speter                                      text_deltas, depth, ignore_ancestry,
972251881Speter                                      send_copyfrom_args,
973251881Speter                                      editor, edit_baton,
974251881Speter                                      authz_check_access_cb_func(b),
975251881Speter                                      &ab, svn_ra_svn_zero_copy_limit(conn),
976251881Speter                                      pool));
977251881Speter
978251881Speter  rb.sb = b;
979251881Speter  rb.repos_url = svn_path_uri_decode(b->repos_url, pool);
980251881Speter  rb.report_baton = report_baton;
981251881Speter  rb.err = NULL;
982251881Speter  rb.entry_counter = 0;
983251881Speter  rb.only_empty_entries = TRUE;
984251881Speter  rb.from_rev = from_rev;
985251881Speter  if (from_rev)
986251881Speter    *from_rev = SVN_INVALID_REVNUM;
987251881Speter  err = svn_ra_svn__handle_commands2(conn, pool, report_commands, &rb, TRUE);
988251881Speter  if (err)
989251881Speter    {
990251881Speter      /* Network or protocol error while handling commands. */
991251881Speter      svn_error_clear(rb.err);
992251881Speter      return err;
993251881Speter    }
994251881Speter  else if (rb.err)
995251881Speter    {
996251881Speter      /* Some failure during the reporting or editing operations. */
997251881Speter      SVN_CMD_ERR(rb.err);
998251881Speter    }
999251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
1000251881Speter
1001251881Speter  if (only_empty_entry)
1002251881Speter    *only_empty_entry = rb.entry_counter == 1 && rb.only_empty_entries;
1003251881Speter
1004251881Speter  return SVN_NO_ERROR;
1005251881Speter}
1006251881Speter
1007251881Speter/* --- MAIN COMMAND SET --- */
1008251881Speter
1009251881Speter/* Write out a list of property diffs.  PROPDIFFS is an array of svn_prop_t
1010251881Speter * values. */
1011251881Speterstatic svn_error_t *write_prop_diffs(svn_ra_svn_conn_t *conn,
1012251881Speter                                     apr_pool_t *pool,
1013251881Speter                                     const apr_array_header_t *propdiffs)
1014251881Speter{
1015251881Speter  int i;
1016251881Speter
1017251881Speter  for (i = 0; i < propdiffs->nelts; ++i)
1018251881Speter    {
1019251881Speter      const svn_prop_t *prop = &APR_ARRAY_IDX(propdiffs, i, svn_prop_t);
1020251881Speter
1021251881Speter      SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "c(?s)",
1022251881Speter                                      prop->name, prop->value));
1023251881Speter    }
1024251881Speter
1025251881Speter  return SVN_NO_ERROR;
1026251881Speter}
1027251881Speter
1028251881Speter/* Write out a lock to the client. */
1029251881Speterstatic svn_error_t *write_lock(svn_ra_svn_conn_t *conn,
1030251881Speter                               apr_pool_t *pool,
1031251881Speter                               svn_lock_t *lock)
1032251881Speter{
1033251881Speter  const char *cdate, *edate;
1034251881Speter
1035251881Speter  cdate = svn_time_to_cstring(lock->creation_date, pool);
1036251881Speter  edate = lock->expiration_date
1037251881Speter    ? svn_time_to_cstring(lock->expiration_date, pool) : NULL;
1038251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "ccc(?c)c(?c)", lock->path,
1039251881Speter                                  lock->token, lock->owner, lock->comment,
1040251881Speter                                  cdate, edate));
1041251881Speter
1042251881Speter  return SVN_NO_ERROR;
1043251881Speter}
1044251881Speter
1045251881Speter/* ### This really belongs in libsvn_repos. */
1046251881Speter/* Get the explicit properties and/or inherited properties for a PATH in
1047251881Speter   ROOT, with hardcoded committed-info values. */
1048251881Speterstatic svn_error_t *
1049251881Speterget_props(apr_hash_t **props,
1050251881Speter          apr_array_header_t **iprops,
1051251881Speter          authz_baton_t *b,
1052251881Speter          svn_fs_root_t *root,
1053251881Speter          const char *path,
1054251881Speter          apr_pool_t *pool)
1055251881Speter{
1056251881Speter  /* Get the explicit properties. */
1057251881Speter  if (props)
1058251881Speter    {
1059251881Speter      svn_string_t *str;
1060251881Speter      svn_revnum_t crev;
1061251881Speter      const char *cdate, *cauthor, *uuid;
1062251881Speter
1063251881Speter      SVN_ERR(svn_fs_node_proplist(props, root, path, pool));
1064251881Speter
1065251881Speter      /* Hardcode the values for the committed revision, date, and author. */
1066251881Speter      SVN_ERR(svn_repos_get_committed_info(&crev, &cdate, &cauthor, root,
1067251881Speter                                           path, pool));
1068251881Speter      str = svn_string_create(apr_psprintf(pool, "%ld", crev),
1069251881Speter                              pool);
1070251881Speter      svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_REV, str);
1071251881Speter      str = (cdate) ? svn_string_create(cdate, pool) : NULL;
1072251881Speter      svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_DATE, str);
1073251881Speter      str = (cauthor) ? svn_string_create(cauthor, pool) : NULL;
1074251881Speter      svn_hash_sets(*props, SVN_PROP_ENTRY_LAST_AUTHOR, str);
1075251881Speter
1076251881Speter      /* Hardcode the values for the UUID. */
1077251881Speter      SVN_ERR(svn_fs_get_uuid(svn_fs_root_fs(root), &uuid, pool));
1078251881Speter      str = (uuid) ? svn_string_create(uuid, pool) : NULL;
1079251881Speter      svn_hash_sets(*props, SVN_PROP_ENTRY_UUID, str);
1080251881Speter    }
1081251881Speter
1082251881Speter  /* Get any inherited properties the user is authorized to. */
1083251881Speter  if (iprops)
1084251881Speter    {
1085251881Speter      SVN_ERR(svn_repos_fs_get_inherited_props(
1086251881Speter                iprops, root, path, NULL,
1087251881Speter                authz_check_access_cb_func(b->server),
1088251881Speter                b, pool, pool));
1089251881Speter    }
1090251881Speter
1091251881Speter  return SVN_NO_ERROR;
1092251881Speter}
1093251881Speter
1094251881Speter/* Set BATON->FS_PATH for the repository URL found in PARAMS. */
1095251881Speterstatic svn_error_t *reparent(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1096251881Speter                             apr_array_header_t *params, void *baton)
1097251881Speter{
1098251881Speter  server_baton_t *b = baton;
1099251881Speter  const char *url;
1100251881Speter  const char *fs_path;
1101251881Speter
1102251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &url));
1103251881Speter  url = svn_uri_canonicalize(url, pool);
1104251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
1105251881Speter  SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repos_url, pool),
1106251881Speter                          svn_path_uri_decode(url, pool),
1107251881Speter                          &fs_path));
1108251881Speter  SVN_ERR(log_command(b, conn, pool, "%s", svn_log__reparent(fs_path, pool)));
1109251881Speter  svn_stringbuf_set(b->fs_path, fs_path);
1110251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
1111251881Speter  return SVN_NO_ERROR;
1112251881Speter}
1113251881Speter
1114251881Speterstatic svn_error_t *get_latest_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1115251881Speter                                   apr_array_header_t *params, void *baton)
1116251881Speter{
1117251881Speter  server_baton_t *b = baton;
1118251881Speter  svn_revnum_t rev;
1119251881Speter
1120251881Speter  SVN_ERR(log_command(b, conn, pool, "get-latest-rev"));
1121251881Speter
1122251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
1123251881Speter  SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
1124251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", rev));
1125251881Speter  return SVN_NO_ERROR;
1126251881Speter}
1127251881Speter
1128251881Speterstatic svn_error_t *get_dated_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1129251881Speter                                  apr_array_header_t *params, void *baton)
1130251881Speter{
1131251881Speter  server_baton_t *b = baton;
1132251881Speter  svn_revnum_t rev;
1133251881Speter  apr_time_t tm;
1134251881Speter  const char *timestr;
1135251881Speter
1136251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &timestr));
1137251881Speter  SVN_ERR(log_command(b, conn, pool, "get-dated-rev %s", timestr));
1138251881Speter
1139251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
1140251881Speter  SVN_CMD_ERR(svn_time_from_cstring(&tm, timestr, pool));
1141251881Speter  SVN_CMD_ERR(svn_repos_dated_revision(&rev, b->repos, tm, pool));
1142251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", rev));
1143251881Speter  return SVN_NO_ERROR;
1144251881Speter}
1145251881Speter
1146251881Speter/* Common logic for change_rev_prop() and change_rev_prop2(). */
1147251881Speterstatic svn_error_t *do_change_rev_prop(svn_ra_svn_conn_t *conn,
1148251881Speter                                       server_baton_t *b,
1149251881Speter                                       svn_revnum_t rev,
1150251881Speter                                       const char *name,
1151251881Speter                                       const svn_string_t *const *old_value_p,
1152251881Speter                                       const svn_string_t *value,
1153251881Speter                                       apr_pool_t *pool)
1154251881Speter{
1155251881Speter  authz_baton_t ab;
1156251881Speter
1157251881Speter  ab.server = b;
1158251881Speter  ab.conn = conn;
1159251881Speter
1160251881Speter  SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, FALSE));
1161251881Speter  SVN_ERR(log_command(b, conn, pool, "%s",
1162251881Speter                      svn_log__change_rev_prop(rev, name, pool)));
1163251881Speter  SVN_CMD_ERR(svn_repos_fs_change_rev_prop4(b->repos, rev, b->user,
1164251881Speter                                            name, old_value_p, value,
1165251881Speter                                            TRUE, TRUE,
1166251881Speter                                            authz_check_access_cb_func(b), &ab,
1167251881Speter                                            pool));
1168251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
1169251881Speter
1170251881Speter  return SVN_NO_ERROR;
1171251881Speter}
1172251881Speter
1173251881Speterstatic svn_error_t *change_rev_prop2(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1174251881Speter                                     apr_array_header_t *params, void *baton)
1175251881Speter{
1176251881Speter  server_baton_t *b = baton;
1177251881Speter  svn_revnum_t rev;
1178251881Speter  const char *name;
1179251881Speter  svn_string_t *value;
1180251881Speter  const svn_string_t *const *old_value_p;
1181251881Speter  svn_string_t *old_value;
1182251881Speter  svn_boolean_t dont_care;
1183251881Speter
1184251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rc(?s)(b?s)",
1185251881Speter                                  &rev, &name, &value,
1186251881Speter                                  &dont_care, &old_value));
1187251881Speter
1188251881Speter  /* Argument parsing. */
1189251881Speter  if (dont_care)
1190251881Speter    old_value_p = NULL;
1191251881Speter  else
1192251881Speter    old_value_p = (const svn_string_t *const *)&old_value;
1193251881Speter
1194251881Speter  /* Input validation. */
1195251881Speter  if (dont_care && old_value)
1196251881Speter    {
1197251881Speter      svn_error_t *err;
1198251881Speter      err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
1199251881Speter                             "'previous-value' and 'dont-care' cannot both be "
1200251881Speter                             "set in 'change-rev-prop2' request");
1201251881Speter      return log_fail_and_flush(err, b, conn, pool);
1202251881Speter    }
1203251881Speter
1204251881Speter  /* Do it. */
1205251881Speter  SVN_ERR(do_change_rev_prop(conn, b, rev, name, old_value_p, value, pool));
1206251881Speter
1207251881Speter  return SVN_NO_ERROR;
1208251881Speter}
1209251881Speter
1210251881Speterstatic svn_error_t *change_rev_prop(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1211251881Speter                                    apr_array_header_t *params, void *baton)
1212251881Speter{
1213251881Speter  server_baton_t *b = baton;
1214251881Speter  svn_revnum_t rev;
1215251881Speter  const char *name;
1216251881Speter  svn_string_t *value;
1217251881Speter
1218251881Speter  /* Because the revprop value was at one time mandatory, the usual
1219251881Speter     optional element pattern "(?s)" isn't used. */
1220251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rc?s", &rev, &name, &value));
1221251881Speter
1222251881Speter  SVN_ERR(do_change_rev_prop(conn, b, rev, name, NULL, value, pool));
1223251881Speter
1224251881Speter  return SVN_NO_ERROR;
1225251881Speter}
1226251881Speter
1227251881Speterstatic svn_error_t *rev_proplist(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1228251881Speter                                 apr_array_header_t *params, void *baton)
1229251881Speter{
1230251881Speter  server_baton_t *b = baton;
1231251881Speter  svn_revnum_t rev;
1232251881Speter  apr_hash_t *props;
1233251881Speter  authz_baton_t ab;
1234251881Speter
1235251881Speter  ab.server = b;
1236251881Speter  ab.conn = conn;
1237251881Speter
1238251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "r", &rev));
1239251881Speter  SVN_ERR(log_command(b, conn, pool, "%s", svn_log__rev_proplist(rev, pool)));
1240251881Speter
1241251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
1242251881Speter  SVN_CMD_ERR(svn_repos_fs_revision_proplist(&props, b->repos, rev,
1243251881Speter                                             authz_check_access_cb_func(b), &ab,
1244251881Speter                                             pool));
1245251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success"));
1246251881Speter  SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props));
1247251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));
1248251881Speter  return SVN_NO_ERROR;
1249251881Speter}
1250251881Speter
1251251881Speterstatic svn_error_t *rev_prop(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1252251881Speter                             apr_array_header_t *params, void *baton)
1253251881Speter{
1254251881Speter  server_baton_t *b = baton;
1255251881Speter  svn_revnum_t rev;
1256251881Speter  const char *name;
1257251881Speter  svn_string_t *value;
1258251881Speter  authz_baton_t ab;
1259251881Speter
1260251881Speter  ab.server = b;
1261251881Speter  ab.conn = conn;
1262251881Speter
1263251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rc", &rev, &name));
1264251881Speter  SVN_ERR(log_command(b, conn, pool, "%s",
1265251881Speter                      svn_log__rev_prop(rev, name, pool)));
1266251881Speter
1267251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
1268251881Speter  SVN_CMD_ERR(svn_repos_fs_revision_prop(&value, b->repos, rev, name,
1269251881Speter                                         authz_check_access_cb_func(b), &ab,
1270251881Speter                                         pool));
1271251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "(?s)", value));
1272251881Speter  return SVN_NO_ERROR;
1273251881Speter}
1274251881Speter
1275251881Speterstatic svn_error_t *commit_done(const svn_commit_info_t *commit_info,
1276251881Speter                                void *baton, apr_pool_t *pool)
1277251881Speter{
1278251881Speter  commit_callback_baton_t *ccb = baton;
1279251881Speter
1280251881Speter  *ccb->new_rev = commit_info->revision;
1281251881Speter  *ccb->date = commit_info->date
1282251881Speter    ? apr_pstrdup(ccb->pool, commit_info->date): NULL;
1283251881Speter  *ccb->author = commit_info->author
1284251881Speter    ? apr_pstrdup(ccb->pool, commit_info->author) : NULL;
1285251881Speter  *ccb->post_commit_err = commit_info->post_commit_err
1286251881Speter    ? apr_pstrdup(ccb->pool, commit_info->post_commit_err) : NULL;
1287251881Speter  return SVN_NO_ERROR;
1288251881Speter}
1289251881Speter
1290251881Speter/* Add the LOCK_TOKENS (if any) to the filesystem access context,
1291251881Speter * checking path authorizations using the state in SB as we go.
1292251881Speter * LOCK_TOKENS is an array of svn_ra_svn_item_t structs.  Return a
1293251881Speter * client error if LOCK_TOKENS is not a list of lists.  If a lock
1294251881Speter * violates the authz configuration, return SVN_ERR_RA_NOT_AUTHORIZED
1295251881Speter * to the client.  Use POOL for temporary allocations only.
1296251881Speter */
1297251881Speterstatic svn_error_t *add_lock_tokens(svn_ra_svn_conn_t *conn,
1298251881Speter                                    const apr_array_header_t *lock_tokens,
1299251881Speter                                    server_baton_t *sb,
1300251881Speter                                    apr_pool_t *pool)
1301251881Speter{
1302251881Speter  int i;
1303251881Speter  svn_fs_access_t *fs_access;
1304251881Speter
1305251881Speter  SVN_ERR(svn_fs_get_access(&fs_access, sb->fs));
1306251881Speter
1307251881Speter  /* If there is no access context, nowhere to add the tokens. */
1308251881Speter  if (! fs_access)
1309251881Speter    return SVN_NO_ERROR;
1310251881Speter
1311251881Speter  for (i = 0; i < lock_tokens->nelts; ++i)
1312251881Speter    {
1313251881Speter      const char *path, *token, *full_path;
1314251881Speter      svn_ra_svn_item_t *path_item, *token_item;
1315251881Speter      svn_ra_svn_item_t *item = &APR_ARRAY_IDX(lock_tokens, i,
1316251881Speter                                               svn_ra_svn_item_t);
1317251881Speter      if (item->kind != SVN_RA_SVN_LIST)
1318251881Speter        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1319251881Speter                                "Lock tokens aren't a list of lists");
1320251881Speter
1321251881Speter      path_item = &APR_ARRAY_IDX(item->u.list, 0, svn_ra_svn_item_t);
1322251881Speter      if (path_item->kind != SVN_RA_SVN_STRING)
1323251881Speter        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1324251881Speter                                "Lock path isn't a string");
1325251881Speter
1326251881Speter      token_item = &APR_ARRAY_IDX(item->u.list, 1, svn_ra_svn_item_t);
1327251881Speter      if (token_item->kind != SVN_RA_SVN_STRING)
1328251881Speter        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1329251881Speter                                "Lock token isn't a string");
1330251881Speter
1331251881Speter      path = path_item->u.string->data;
1332251881Speter      full_path = svn_fspath__join(sb->fs_path->data,
1333251881Speter                                   svn_relpath_canonicalize(path, pool),
1334251881Speter                                   pool);
1335251881Speter
1336251881Speter      if (! lookup_access(pool, sb, conn, svn_authz_write,
1337251881Speter                          full_path, TRUE))
1338251881Speter        return error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, NULL,
1339251881Speter                                    sb, conn, pool);
1340251881Speter
1341251881Speter      token = token_item->u.string->data;
1342251881Speter      SVN_ERR(svn_fs_access_add_lock_token2(fs_access, path, token));
1343251881Speter    }
1344251881Speter
1345251881Speter  return SVN_NO_ERROR;
1346251881Speter}
1347251881Speter
1348251881Speter/* Unlock the paths with lock tokens in LOCK_TOKENS, ignoring any errors.
1349251881Speter   LOCK_TOKENS contains svn_ra_svn_item_t elements, assumed to be lists. */
1350251881Speterstatic svn_error_t *unlock_paths(const apr_array_header_t *lock_tokens,
1351251881Speter                                 server_baton_t *sb,
1352251881Speter                                 svn_ra_svn_conn_t *conn,
1353251881Speter                                 apr_pool_t *pool)
1354251881Speter{
1355251881Speter  int i;
1356251881Speter  apr_pool_t *iterpool;
1357251881Speter
1358251881Speter  iterpool = svn_pool_create(pool);
1359251881Speter
1360251881Speter  for (i = 0; i < lock_tokens->nelts; ++i)
1361251881Speter    {
1362251881Speter      svn_ra_svn_item_t *item, *path_item, *token_item;
1363251881Speter      const char *path, *token, *full_path;
1364251881Speter      svn_error_t *err;
1365251881Speter      svn_pool_clear(iterpool);
1366251881Speter
1367251881Speter      item = &APR_ARRAY_IDX(lock_tokens, i, svn_ra_svn_item_t);
1368251881Speter      path_item = &APR_ARRAY_IDX(item->u.list, 0, svn_ra_svn_item_t);
1369251881Speter      token_item = &APR_ARRAY_IDX(item->u.list, 1, svn_ra_svn_item_t);
1370251881Speter
1371251881Speter      path = path_item->u.string->data;
1372251881Speter      token = token_item->u.string->data;
1373251881Speter
1374251881Speter      full_path = svn_fspath__join(sb->fs_path->data,
1375251881Speter                                   svn_relpath_canonicalize(path, iterpool),
1376251881Speter                                   iterpool);
1377251881Speter
1378251881Speter      /* The lock may have become defunct after the commit, so ignore such
1379251881Speter         errors. */
1380251881Speter      err = svn_repos_fs_unlock(sb->repos, full_path, token,
1381251881Speter                                FALSE, iterpool);
1382251881Speter      log_server_error(err, sb, conn, iterpool);
1383251881Speter      svn_error_clear(err);
1384251881Speter    }
1385251881Speter
1386251881Speter  svn_pool_destroy(iterpool);
1387251881Speter
1388251881Speter  return SVN_NO_ERROR;
1389251881Speter}
1390251881Speter
1391251881Speterstatic svn_error_t *commit(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1392251881Speter                           apr_array_header_t *params, void *baton)
1393251881Speter{
1394251881Speter  server_baton_t *b = baton;
1395251881Speter  const char *log_msg = NULL,
1396251881Speter             *date = NULL,
1397251881Speter             *author = NULL,
1398251881Speter             *post_commit_err = NULL;
1399251881Speter  apr_array_header_t *lock_tokens;
1400251881Speter  svn_boolean_t keep_locks;
1401251881Speter  apr_array_header_t *revprop_list = NULL;
1402251881Speter  apr_hash_t *revprop_table;
1403251881Speter  const svn_delta_editor_t *editor;
1404251881Speter  void *edit_baton;
1405251881Speter  svn_boolean_t aborted;
1406251881Speter  commit_callback_baton_t ccb;
1407251881Speter  svn_revnum_t new_rev;
1408251881Speter  authz_baton_t ab;
1409251881Speter
1410251881Speter  ab.server = b;
1411251881Speter  ab.conn = conn;
1412251881Speter
1413251881Speter  if (params->nelts == 1)
1414251881Speter    {
1415251881Speter      /* Clients before 1.2 don't send lock-tokens, keep-locks,
1416251881Speter         and rev-props fields. */
1417251881Speter      SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &log_msg));
1418251881Speter      lock_tokens = NULL;
1419251881Speter      keep_locks = TRUE;
1420251881Speter      revprop_list = NULL;
1421251881Speter    }
1422251881Speter  else
1423251881Speter    {
1424251881Speter      /* Clients before 1.5 don't send the rev-props field. */
1425251881Speter      SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "clb?l", &log_msg,
1426251881Speter                                      &lock_tokens, &keep_locks,
1427251881Speter                                      &revprop_list));
1428251881Speter    }
1429251881Speter
1430251881Speter  /* The handling for locks is a little problematic, because the
1431251881Speter     protocol won't let us send several auth requests once one has
1432251881Speter     succeeded.  So we request write access and a username before
1433251881Speter     adding tokens (if we have any), and subsequently fail if a lock
1434251881Speter     violates authz. */
1435251881Speter  SVN_ERR(must_have_access(conn, pool, b, svn_authz_write,
1436251881Speter                           NULL,
1437251881Speter                           (lock_tokens && lock_tokens->nelts)));
1438251881Speter
1439251881Speter  /* Authorize the lock tokens and give them to the FS if we got
1440251881Speter     any. */
1441251881Speter  if (lock_tokens && lock_tokens->nelts)
1442251881Speter    SVN_CMD_ERR(add_lock_tokens(conn, lock_tokens, b, pool));
1443251881Speter
1444253734Speter  /* Ignore LOG_MSG, per the protocol.  See ra_svn_commit(). */
1445251881Speter  if (revprop_list)
1446251881Speter    SVN_ERR(svn_ra_svn__parse_proplist(revprop_list, pool, &revprop_table));
1447251881Speter  else
1448251881Speter    {
1449251881Speter      revprop_table = apr_hash_make(pool);
1450251881Speter      svn_hash_sets(revprop_table, SVN_PROP_REVISION_LOG,
1451251881Speter                    svn_string_create(log_msg, pool));
1452251881Speter    }
1453251881Speter
1454251881Speter  /* Get author from the baton, making sure clients can't circumvent
1455251881Speter     the authentication via the revision props. */
1456251881Speter  svn_hash_sets(revprop_table, SVN_PROP_REVISION_AUTHOR,
1457251881Speter                b->user ? svn_string_create(b->user, pool) : NULL);
1458251881Speter
1459251881Speter  ccb.pool = pool;
1460251881Speter  ccb.new_rev = &new_rev;
1461251881Speter  ccb.date = &date;
1462251881Speter  ccb.author = &author;
1463251881Speter  ccb.post_commit_err = &post_commit_err;
1464251881Speter  /* ### Note that svn_repos_get_commit_editor5 actually wants a decoded URL. */
1465251881Speter  SVN_CMD_ERR(svn_repos_get_commit_editor5
1466251881Speter              (&editor, &edit_baton, b->repos, NULL,
1467251881Speter               svn_path_uri_decode(b->repos_url, pool),
1468251881Speter               b->fs_path->data, revprop_table,
1469251881Speter               commit_done, &ccb,
1470251881Speter               authz_commit_cb, &ab, pool));
1471251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
1472251881Speter  SVN_ERR(svn_ra_svn_drive_editor2(conn, pool, editor, edit_baton,
1473251881Speter                                   &aborted, FALSE));
1474251881Speter  if (!aborted)
1475251881Speter    {
1476251881Speter      SVN_ERR(log_command(b, conn, pool, "%s",
1477251881Speter                          svn_log__commit(new_rev, pool)));
1478251881Speter      SVN_ERR(trivial_auth_request(conn, pool, b));
1479251881Speter
1480251881Speter      /* In tunnel mode, deltify before answering the client, because
1481251881Speter         answering may cause the client to terminate the connection
1482251881Speter         and thus kill the server.  But otherwise, deltify after
1483251881Speter         answering the client, to avoid user-visible delay. */
1484251881Speter
1485251881Speter      if (b->tunnel)
1486251881Speter        SVN_ERR(svn_fs_deltify_revision(b->fs, new_rev, pool));
1487251881Speter
1488251881Speter      /* Unlock the paths. */
1489251881Speter      if (! keep_locks && lock_tokens && lock_tokens->nelts)
1490251881Speter        SVN_ERR(unlock_paths(lock_tokens, b, conn, pool));
1491251881Speter
1492251881Speter      SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "r(?c)(?c)(?c)",
1493251881Speter                                      new_rev, date, author, post_commit_err));
1494251881Speter
1495251881Speter      if (! b->tunnel)
1496251881Speter        SVN_ERR(svn_fs_deltify_revision(b->fs, new_rev, pool));
1497251881Speter    }
1498251881Speter  return SVN_NO_ERROR;
1499251881Speter}
1500251881Speter
1501251881Speterstatic svn_error_t *get_file(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1502251881Speter                             apr_array_header_t *params, void *baton)
1503251881Speter{
1504251881Speter  server_baton_t *b = baton;
1505251881Speter  const char *path, *full_path, *hex_digest;
1506251881Speter  svn_revnum_t rev;
1507251881Speter  svn_fs_root_t *root;
1508251881Speter  svn_stream_t *contents;
1509251881Speter  apr_hash_t *props = NULL;
1510251881Speter  apr_array_header_t *inherited_props;
1511251881Speter  svn_string_t write_str;
1512251881Speter  char buf[4096];
1513251881Speter  apr_size_t len;
1514251881Speter  svn_boolean_t want_props, want_contents;
1515251881Speter  apr_uint64_t wants_inherited_props;
1516251881Speter  svn_checksum_t *checksum;
1517251881Speter  svn_error_t *err, *write_err;
1518251881Speter  int i;
1519251881Speter  authz_baton_t ab;
1520251881Speter
1521251881Speter  ab.server = b;
1522251881Speter  ab.conn = conn;
1523251881Speter
1524251881Speter  /* Parse arguments. */
1525251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)bb?B", &path, &rev,
1526251881Speter                                  &want_props, &want_contents,
1527251881Speter                                  &wants_inherited_props));
1528251881Speter
1529269847Speter  if (wants_inherited_props == SVN_RA_SVN_UNSPECIFIED_NUMBER)
1530269847Speter    wants_inherited_props = FALSE;
1531269847Speter
1532251881Speter  full_path = svn_fspath__join(b->fs_path->data,
1533251881Speter                               svn_relpath_canonicalize(path, pool), pool);
1534251881Speter
1535251881Speter  /* Check authorizations */
1536251881Speter  SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
1537251881Speter                           full_path, FALSE));
1538251881Speter
1539251881Speter  if (!SVN_IS_VALID_REVNUM(rev))
1540251881Speter    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
1541251881Speter
1542251881Speter  SVN_ERR(log_command(b, conn, pool, "%s",
1543251881Speter                      svn_log__get_file(full_path, rev,
1544251881Speter                                        want_contents, want_props, pool)));
1545251881Speter
1546251881Speter  /* Fetch the properties and a stream for the contents. */
1547251881Speter  SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool));
1548251881Speter  SVN_CMD_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, root,
1549251881Speter                                   full_path, TRUE, pool));
1550251881Speter  hex_digest = svn_checksum_to_cstring_display(checksum, pool);
1551269847Speter
1552269847Speter  /* Fetch the file's explicit and/or inherited properties if
1553269847Speter     requested.  Although the wants-iprops boolean was added to the
1554269847Speter     protocol in 1.8 a standard 1.8 client never requests iprops. */
1555251881Speter  if (want_props || wants_inherited_props)
1556269847Speter    SVN_CMD_ERR(get_props(want_props ? &props : NULL,
1557269847Speter                          wants_inherited_props ? &inherited_props : NULL,
1558269847Speter                          &ab, root, full_path,
1559251881Speter                          pool));
1560251881Speter  if (want_contents)
1561251881Speter    SVN_CMD_ERR(svn_fs_file_contents(&contents, root, full_path, pool));
1562251881Speter
1563251881Speter  /* Send successful command response with revision and props. */
1564251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((?c)r(!", "success",
1565251881Speter                                  hex_digest, rev));
1566251881Speter  SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props));
1567251881Speter
1568251881Speter  if (wants_inherited_props)
1569251881Speter    {
1570251881Speter      apr_pool_t *iterpool = svn_pool_create(pool);
1571251881Speter
1572251881Speter      SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(?!"));
1573251881Speter      for (i = 0; i < inherited_props->nelts; i++)
1574251881Speter        {
1575251881Speter          svn_prop_inherited_item_t *iprop =
1576251881Speter            APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *);
1577251881Speter
1578251881Speter          svn_pool_clear(iterpool);
1579251881Speter          SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!",
1580251881Speter                                          iprop->path_or_url));
1581251881Speter          SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash));
1582251881Speter          SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!",
1583251881Speter                                          iprop->path_or_url));
1584251881Speter        }
1585251881Speter      svn_pool_destroy(iterpool);
1586251881Speter    }
1587251881Speter
1588251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));
1589251881Speter
1590251881Speter  /* Now send the file's contents. */
1591251881Speter  if (want_contents)
1592251881Speter    {
1593251881Speter      err = SVN_NO_ERROR;
1594251881Speter      while (1)
1595251881Speter        {
1596251881Speter          len = sizeof(buf);
1597251881Speter          err = svn_stream_read(contents, buf, &len);
1598251881Speter          if (err)
1599251881Speter            break;
1600251881Speter          if (len > 0)
1601251881Speter            {
1602251881Speter              write_str.data = buf;
1603251881Speter              write_str.len = len;
1604251881Speter              SVN_ERR(svn_ra_svn__write_string(conn, pool, &write_str));
1605251881Speter            }
1606251881Speter          if (len < sizeof(buf))
1607251881Speter            {
1608251881Speter              err = svn_stream_close(contents);
1609251881Speter              break;
1610251881Speter            }
1611251881Speter        }
1612251881Speter      write_err = svn_ra_svn__write_cstring(conn, pool, "");
1613251881Speter      if (write_err)
1614251881Speter        {
1615251881Speter          svn_error_clear(err);
1616251881Speter          return write_err;
1617251881Speter        }
1618251881Speter      SVN_CMD_ERR(err);
1619251881Speter      SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
1620251881Speter    }
1621251881Speter
1622251881Speter  return SVN_NO_ERROR;
1623251881Speter}
1624251881Speter
1625251881Speterstatic svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1626251881Speter                            apr_array_header_t *params, void *baton)
1627251881Speter{
1628251881Speter  server_baton_t *b = baton;
1629251881Speter  const char *path, *full_path;
1630251881Speter  svn_revnum_t rev;
1631251881Speter  apr_hash_t *entries, *props = NULL;
1632251881Speter  apr_array_header_t *inherited_props;
1633251881Speter  apr_hash_index_t *hi;
1634251881Speter  svn_fs_root_t *root;
1635251881Speter  apr_pool_t *subpool;
1636251881Speter  svn_boolean_t want_props, want_contents;
1637251881Speter  apr_uint64_t wants_inherited_props;
1638251881Speter  apr_uint64_t dirent_fields;
1639251881Speter  apr_array_header_t *dirent_fields_list = NULL;
1640251881Speter  svn_ra_svn_item_t *elt;
1641251881Speter  int i;
1642251881Speter  authz_baton_t ab;
1643251881Speter
1644251881Speter  ab.server = b;
1645251881Speter  ab.conn = conn;
1646251881Speter
1647251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)bb?l?B", &path, &rev,
1648251881Speter                                  &want_props, &want_contents,
1649251881Speter                                  &dirent_fields_list,
1650251881Speter                                  &wants_inherited_props));
1651251881Speter
1652269847Speter  if (wants_inherited_props == SVN_RA_SVN_UNSPECIFIED_NUMBER)
1653269847Speter    wants_inherited_props = FALSE;
1654269847Speter
1655251881Speter  if (! dirent_fields_list)
1656251881Speter    {
1657251881Speter      dirent_fields = SVN_DIRENT_ALL;
1658251881Speter    }
1659251881Speter  else
1660251881Speter    {
1661251881Speter      dirent_fields = 0;
1662251881Speter
1663251881Speter      for (i = 0; i < dirent_fields_list->nelts; ++i)
1664251881Speter        {
1665251881Speter          elt = &APR_ARRAY_IDX(dirent_fields_list, i, svn_ra_svn_item_t);
1666251881Speter
1667251881Speter          if (elt->kind != SVN_RA_SVN_WORD)
1668251881Speter            return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1669251881Speter                                    "Dirent field not a string");
1670251881Speter
1671251881Speter          if (strcmp(SVN_RA_SVN_DIRENT_KIND, elt->u.word) == 0)
1672251881Speter            dirent_fields |= SVN_DIRENT_KIND;
1673251881Speter          else if (strcmp(SVN_RA_SVN_DIRENT_SIZE, elt->u.word) == 0)
1674251881Speter            dirent_fields |= SVN_DIRENT_SIZE;
1675251881Speter          else if (strcmp(SVN_RA_SVN_DIRENT_HAS_PROPS, elt->u.word) == 0)
1676251881Speter            dirent_fields |= SVN_DIRENT_HAS_PROPS;
1677251881Speter          else if (strcmp(SVN_RA_SVN_DIRENT_CREATED_REV, elt->u.word) == 0)
1678251881Speter            dirent_fields |= SVN_DIRENT_CREATED_REV;
1679251881Speter          else if (strcmp(SVN_RA_SVN_DIRENT_TIME, elt->u.word) == 0)
1680251881Speter            dirent_fields |= SVN_DIRENT_TIME;
1681251881Speter          else if (strcmp(SVN_RA_SVN_DIRENT_LAST_AUTHOR, elt->u.word) == 0)
1682251881Speter            dirent_fields |= SVN_DIRENT_LAST_AUTHOR;
1683251881Speter        }
1684251881Speter    }
1685251881Speter
1686251881Speter  full_path = svn_fspath__join(b->fs_path->data,
1687251881Speter                               svn_relpath_canonicalize(path, pool), pool);
1688251881Speter
1689251881Speter  /* Check authorizations */
1690251881Speter  SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
1691251881Speter                           full_path, FALSE));
1692251881Speter
1693251881Speter  if (!SVN_IS_VALID_REVNUM(rev))
1694251881Speter    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
1695251881Speter
1696251881Speter  SVN_ERR(log_command(b, conn, pool, "%s",
1697251881Speter                      svn_log__get_dir(full_path, rev,
1698251881Speter                                       want_contents, want_props,
1699251881Speter                                       dirent_fields, pool)));
1700251881Speter
1701251881Speter  /* Fetch the root of the appropriate revision. */
1702251881Speter  SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool));
1703251881Speter
1704269847Speter  /* Fetch the directory's explicit and/or inherited properties if
1705269847Speter     requested.  Although the wants-iprops boolean was added to the
1706269847Speter     protocol in 1.8 a standard 1.8 client never requests iprops. */
1707251881Speter  if (want_props || wants_inherited_props)
1708269847Speter    SVN_CMD_ERR(get_props(want_props ? &props : NULL,
1709269847Speter                          wants_inherited_props ? &inherited_props : NULL,
1710269847Speter                          &ab, root, full_path,
1711251881Speter                          pool));
1712251881Speter
1713251881Speter  /* Begin response ... */
1714251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(r(!", "success", rev));
1715251881Speter  SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props));
1716251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(!"));
1717251881Speter
1718251881Speter  /* Fetch the directory entries if requested and send them immediately. */
1719251881Speter  if (want_contents)
1720251881Speter    {
1721251881Speter      /* Use epoch for a placeholder for a missing date.  */
1722251881Speter      const char *missing_date = svn_time_to_cstring(0, pool);
1723251881Speter
1724251881Speter      SVN_CMD_ERR(svn_fs_dir_entries(&entries, root, full_path, pool));
1725251881Speter
1726251881Speter      /* Transform the hash table's FS entries into dirents.  This probably
1727251881Speter       * belongs in libsvn_repos. */
1728251881Speter      subpool = svn_pool_create(pool);
1729251881Speter      for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
1730251881Speter        {
1731251881Speter          const char *name = svn__apr_hash_index_key(hi);
1732251881Speter          svn_fs_dirent_t *fsent = svn__apr_hash_index_val(hi);
1733251881Speter          const char *file_path;
1734251881Speter
1735251881Speter          /* The fields in the entry tuple.  */
1736251881Speter          svn_node_kind_t entry_kind = svn_node_none;
1737251881Speter          svn_filesize_t entry_size = 0;
1738251881Speter          svn_boolean_t has_props = FALSE;
1739251881Speter          /* If 'created rev' was not requested, send 0.  We can't use
1740251881Speter           * SVN_INVALID_REVNUM as the tuple field is not optional.
1741251881Speter           * See the email thread on dev@, 2012-03-28, subject
1742251881Speter           * "buildbot failure in ASF Buildbot on svn-slik-w2k3-x64-ra",
1743251881Speter           * <http://svn.haxx.se/dev/archive-2012-03/0655.shtml>. */
1744251881Speter          svn_revnum_t created_rev = 0;
1745251881Speter          const char *cdate = NULL;
1746251881Speter          const char *last_author = NULL;
1747251881Speter
1748251881Speter          svn_pool_clear(subpool);
1749251881Speter
1750251881Speter          file_path = svn_fspath__join(full_path, name, subpool);
1751251881Speter          if (! lookup_access(subpool, b, conn, svn_authz_read,
1752251881Speter                              file_path, FALSE))
1753251881Speter            continue;
1754251881Speter
1755251881Speter          if (dirent_fields & SVN_DIRENT_KIND)
1756251881Speter              entry_kind = fsent->kind;
1757251881Speter
1758251881Speter          if (dirent_fields & SVN_DIRENT_SIZE)
1759251881Speter              if (entry_kind != svn_node_dir)
1760251881Speter                SVN_CMD_ERR(svn_fs_file_length(&entry_size, root, file_path,
1761251881Speter                                               subpool));
1762251881Speter
1763251881Speter          if (dirent_fields & SVN_DIRENT_HAS_PROPS)
1764251881Speter            {
1765251881Speter              apr_hash_t *file_props;
1766251881Speter
1767251881Speter              /* has_props */
1768251881Speter              SVN_CMD_ERR(svn_fs_node_proplist(&file_props, root, file_path,
1769251881Speter                                               subpool));
1770251881Speter              has_props = (apr_hash_count(file_props) > 0);
1771251881Speter            }
1772251881Speter
1773251881Speter          if ((dirent_fields & SVN_DIRENT_LAST_AUTHOR)
1774251881Speter              || (dirent_fields & SVN_DIRENT_TIME)
1775251881Speter              || (dirent_fields & SVN_DIRENT_CREATED_REV))
1776251881Speter            {
1777251881Speter              /* created_rev, last_author, time */
1778251881Speter              SVN_CMD_ERR(svn_repos_get_committed_info(&created_rev,
1779251881Speter                                                       &cdate,
1780251881Speter                                                       &last_author,
1781251881Speter                                                       root,
1782251881Speter                                                       file_path,
1783251881Speter                                                       subpool));
1784251881Speter            }
1785251881Speter
1786251881Speter          /* The client does not properly handle a missing CDATE. For
1787251881Speter             interoperability purposes, we must fill in some junk.
1788251881Speter
1789251881Speter             See libsvn_ra_svn/client.c:ra_svn_get_dir()  */
1790251881Speter          if (cdate == NULL)
1791251881Speter            cdate = missing_date;
1792251881Speter
1793251881Speter          /* Send the entry. */
1794251881Speter          SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "cwnbr(?c)(?c)", name,
1795251881Speter                                          svn_node_kind_to_word(entry_kind),
1796251881Speter                                          (apr_uint64_t) entry_size,
1797251881Speter                                          has_props, created_rev,
1798251881Speter                                          cdate, last_author));
1799251881Speter        }
1800251881Speter      svn_pool_destroy(subpool);
1801251881Speter    }
1802251881Speter
1803251881Speter  if (wants_inherited_props)
1804251881Speter    {
1805251881Speter      apr_pool_t *iterpool = svn_pool_create(pool);
1806251881Speter
1807251881Speter      SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(?!"));
1808251881Speter      for (i = 0; i < inherited_props->nelts; i++)
1809251881Speter        {
1810251881Speter          svn_prop_inherited_item_t *iprop =
1811251881Speter            APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *);
1812251881Speter
1813251881Speter          svn_pool_clear(iterpool);
1814251881Speter          SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!",
1815251881Speter                                          iprop->path_or_url));
1816251881Speter          SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash));
1817251881Speter          SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!",
1818251881Speter                                          iprop->path_or_url));
1819251881Speter        }
1820251881Speter      svn_pool_destroy(iterpool);
1821251881Speter    }
1822251881Speter
1823251881Speter  /* Finish response. */
1824251881Speter  return svn_ra_svn__write_tuple(conn, pool, "!))");
1825251881Speter}
1826251881Speter
1827251881Speterstatic svn_error_t *update(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1828251881Speter                           apr_array_header_t *params, void *baton)
1829251881Speter{
1830251881Speter  server_baton_t *b = baton;
1831251881Speter  svn_revnum_t rev;
1832251881Speter  const char *target, *full_path, *depth_word;
1833251881Speter  svn_boolean_t recurse;
1834251881Speter  apr_uint64_t send_copyfrom_args; /* Optional; default FALSE */
1835251881Speter  apr_uint64_t ignore_ancestry; /* Optional; default FALSE */
1836251881Speter  /* Default to unknown.  Old clients won't send depth, but we'll
1837251881Speter     handle that by converting recurse if necessary. */
1838251881Speter  svn_depth_t depth = svn_depth_unknown;
1839251881Speter  svn_boolean_t is_checkout;
1840251881Speter
1841251881Speter  /* Parse the arguments. */
1842251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cb?wB?B", &rev, &target,
1843251881Speter                                  &recurse, &depth_word,
1844251881Speter                                  &send_copyfrom_args, &ignore_ancestry));
1845251881Speter  target = svn_relpath_canonicalize(target, pool);
1846251881Speter
1847251881Speter  if (depth_word)
1848251881Speter    depth = svn_depth_from_word(depth_word);
1849251881Speter  else
1850251881Speter    depth = SVN_DEPTH_INFINITY_OR_FILES(recurse);
1851251881Speter
1852251881Speter  full_path = svn_fspath__join(b->fs_path->data, target, pool);
1853251881Speter  /* Check authorization and authenticate the user if necessary. */
1854251881Speter  SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, full_path, FALSE));
1855251881Speter
1856251881Speter  if (!SVN_IS_VALID_REVNUM(rev))
1857251881Speter    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
1858251881Speter
1859251881Speter  SVN_ERR(accept_report(&is_checkout, NULL,
1860251881Speter                        conn, pool, b, rev, target, NULL, TRUE,
1861251881Speter                        depth,
1862251881Speter                        (send_copyfrom_args == TRUE) /* send_copyfrom_args */,
1863251881Speter                        (ignore_ancestry == TRUE) /* ignore_ancestry */));
1864251881Speter  if (is_checkout)
1865251881Speter    {
1866251881Speter      SVN_ERR(log_command(b, conn, pool, "%s",
1867251881Speter                          svn_log__checkout(full_path, rev,
1868251881Speter                                            depth, pool)));
1869251881Speter    }
1870251881Speter  else
1871251881Speter    {
1872251881Speter      SVN_ERR(log_command(b, conn, pool, "%s",
1873251881Speter                          svn_log__update(full_path, rev, depth,
1874251881Speter                                          send_copyfrom_args, pool)));
1875251881Speter    }
1876251881Speter
1877251881Speter  return SVN_NO_ERROR;
1878251881Speter}
1879251881Speter
1880251881Speterstatic svn_error_t *switch_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1881251881Speter                               apr_array_header_t *params, void *baton)
1882251881Speter{
1883251881Speter  server_baton_t *b = baton;
1884251881Speter  svn_revnum_t rev;
1885251881Speter  const char *target, *depth_word;
1886251881Speter  const char *switch_url, *switch_path;
1887251881Speter  svn_boolean_t recurse;
1888251881Speter  /* Default to unknown.  Old clients won't send depth, but we'll
1889251881Speter     handle that by converting recurse if necessary. */
1890251881Speter  svn_depth_t depth = svn_depth_unknown;
1891251881Speter  apr_uint64_t send_copyfrom_args; /* Optional; default FALSE */
1892251881Speter  apr_uint64_t ignore_ancestry; /* Optional; default TRUE */
1893251881Speter
1894251881Speter  /* Parse the arguments. */
1895251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbc?w?BB", &rev, &target,
1896251881Speter                                  &recurse, &switch_url, &depth_word,
1897251881Speter                                  &send_copyfrom_args, &ignore_ancestry));
1898251881Speter  target = svn_relpath_canonicalize(target, pool);
1899251881Speter  switch_url = svn_uri_canonicalize(switch_url, pool);
1900251881Speter
1901251881Speter  if (depth_word)
1902251881Speter    depth = svn_depth_from_word(depth_word);
1903251881Speter  else
1904251881Speter    depth = SVN_DEPTH_INFINITY_OR_FILES(recurse);
1905251881Speter
1906251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
1907251881Speter  if (!SVN_IS_VALID_REVNUM(rev))
1908251881Speter    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
1909251881Speter
1910251881Speter  SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repos_url, pool),
1911251881Speter                          svn_path_uri_decode(switch_url, pool),
1912251881Speter                          &switch_path));
1913251881Speter
1914251881Speter  {
1915251881Speter    const char *full_path = svn_fspath__join(b->fs_path->data, target, pool);
1916251881Speter    SVN_ERR(log_command(b, conn, pool, "%s",
1917251881Speter                        svn_log__switch(full_path, switch_path, rev,
1918251881Speter                                        depth, pool)));
1919251881Speter  }
1920251881Speter
1921251881Speter  return accept_report(NULL, NULL,
1922251881Speter                       conn, pool, b, rev, target, switch_path, TRUE,
1923251881Speter                       depth,
1924251881Speter                       (send_copyfrom_args == TRUE) /* send_copyfrom_args */,
1925251881Speter                       (ignore_ancestry != FALSE) /* ignore_ancestry */);
1926251881Speter}
1927251881Speter
1928251881Speterstatic svn_error_t *status(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1929251881Speter                           apr_array_header_t *params, void *baton)
1930251881Speter{
1931251881Speter  server_baton_t *b = baton;
1932251881Speter  svn_revnum_t rev;
1933251881Speter  const char *target, *depth_word;
1934251881Speter  svn_boolean_t recurse;
1935251881Speter  /* Default to unknown.  Old clients won't send depth, but we'll
1936251881Speter     handle that by converting recurse if necessary. */
1937251881Speter  svn_depth_t depth = svn_depth_unknown;
1938251881Speter
1939251881Speter  /* Parse the arguments. */
1940251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "cb?(?r)?w",
1941251881Speter                                  &target, &recurse, &rev, &depth_word));
1942251881Speter  target = svn_relpath_canonicalize(target, pool);
1943251881Speter
1944251881Speter  if (depth_word)
1945251881Speter    depth = svn_depth_from_word(depth_word);
1946251881Speter  else
1947251881Speter    depth = SVN_DEPTH_INFINITY_OR_EMPTY(recurse);
1948251881Speter
1949251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
1950251881Speter  if (!SVN_IS_VALID_REVNUM(rev))
1951251881Speter    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
1952251881Speter
1953251881Speter  {
1954251881Speter    const char *full_path = svn_fspath__join(b->fs_path->data, target, pool);
1955251881Speter    SVN_ERR(log_command(b, conn, pool, "%s",
1956251881Speter                        svn_log__status(full_path, rev, depth, pool)));
1957251881Speter  }
1958251881Speter
1959251881Speter  return accept_report(NULL, NULL, conn, pool, b, rev, target, NULL, FALSE,
1960251881Speter                       depth, FALSE, FALSE);
1961251881Speter}
1962251881Speter
1963251881Speterstatic svn_error_t *diff(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1964251881Speter                         apr_array_header_t *params, void *baton)
1965251881Speter{
1966251881Speter  server_baton_t *b = baton;
1967251881Speter  svn_revnum_t rev;
1968251881Speter  const char *target, *versus_url, *versus_path, *depth_word;
1969251881Speter  svn_boolean_t recurse, ignore_ancestry;
1970251881Speter  svn_boolean_t text_deltas;
1971251881Speter  /* Default to unknown.  Old clients won't send depth, but we'll
1972251881Speter     handle that by converting recurse if necessary. */
1973251881Speter  svn_depth_t depth = svn_depth_unknown;
1974251881Speter
1975251881Speter  /* Parse the arguments. */
1976251881Speter  if (params->nelts == 5)
1977251881Speter    {
1978251881Speter      /* Clients before 1.4 don't send the text_deltas boolean or depth. */
1979251881Speter      SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbbc", &rev, &target,
1980251881Speter                                      &recurse, &ignore_ancestry, &versus_url));
1981251881Speter      text_deltas = TRUE;
1982251881Speter      depth_word = NULL;
1983251881Speter    }
1984251881Speter  else
1985251881Speter    {
1986251881Speter      SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbbcb?w",
1987251881Speter                                      &rev, &target, &recurse,
1988251881Speter                                      &ignore_ancestry, &versus_url,
1989251881Speter                                      &text_deltas, &depth_word));
1990251881Speter    }
1991251881Speter  target = svn_relpath_canonicalize(target, pool);
1992251881Speter  versus_url = svn_uri_canonicalize(versus_url, pool);
1993251881Speter
1994251881Speter  if (depth_word)
1995251881Speter    depth = svn_depth_from_word(depth_word);
1996251881Speter  else
1997251881Speter    depth = SVN_DEPTH_INFINITY_OR_FILES(recurse);
1998251881Speter
1999251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
2000251881Speter
2001251881Speter  if (!SVN_IS_VALID_REVNUM(rev))
2002251881Speter    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
2003251881Speter  SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repos_url, pool),
2004251881Speter                          svn_path_uri_decode(versus_url, pool),
2005251881Speter                          &versus_path));
2006251881Speter
2007251881Speter  {
2008251881Speter    const char *full_path = svn_fspath__join(b->fs_path->data, target, pool);
2009251881Speter    svn_revnum_t from_rev;
2010251881Speter    SVN_ERR(accept_report(NULL, &from_rev,
2011251881Speter                          conn, pool, b, rev, target, versus_path,
2012251881Speter                          text_deltas, depth, FALSE, ignore_ancestry));
2013251881Speter    SVN_ERR(log_command(b, conn, pool, "%s",
2014251881Speter                        svn_log__diff(full_path, from_rev, versus_path,
2015251881Speter                                      rev, depth, ignore_ancestry,
2016251881Speter                                      pool)));
2017251881Speter  }
2018251881Speter  return SVN_NO_ERROR;
2019251881Speter}
2020251881Speter
2021251881Speter/* Regardless of whether a client's capabilities indicate an
2022251881Speter   understanding of this command (by way of SVN_RA_SVN_CAP_MERGEINFO),
2023251881Speter   we provide a response.
2024251881Speter
2025251881Speter   ASSUMPTION: When performing a 'merge' with two URLs at different
2026251881Speter   revisions, the client will call this command more than once. */
2027251881Speterstatic svn_error_t *get_mergeinfo(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
2028251881Speter                                  apr_array_header_t *params, void *baton)
2029251881Speter{
2030251881Speter  server_baton_t *b = baton;
2031251881Speter  svn_revnum_t rev;
2032251881Speter  apr_array_header_t *paths, *canonical_paths;
2033251881Speter  svn_mergeinfo_catalog_t mergeinfo;
2034251881Speter  int i;
2035251881Speter  apr_hash_index_t *hi;
2036251881Speter  const char *inherit_word;
2037251881Speter  svn_mergeinfo_inheritance_t inherit;
2038251881Speter  svn_boolean_t include_descendants;
2039251881Speter  apr_pool_t *iterpool;
2040251881Speter  authz_baton_t ab;
2041251881Speter
2042251881Speter  ab.server = b;
2043251881Speter  ab.conn = conn;
2044251881Speter
2045251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "l(?r)wb", &paths, &rev,
2046251881Speter                                  &inherit_word, &include_descendants));
2047251881Speter  inherit = svn_inheritance_from_word(inherit_word);
2048251881Speter
2049251881Speter  /* Canonicalize the paths which mergeinfo has been requested for. */
2050251881Speter  canonical_paths = apr_array_make(pool, paths->nelts, sizeof(const char *));
2051251881Speter  for (i = 0; i < paths->nelts; i++)
2052251881Speter     {
2053251881Speter        svn_ra_svn_item_t *item = &APR_ARRAY_IDX(paths, i, svn_ra_svn_item_t);
2054251881Speter        const char *full_path;
2055251881Speter
2056251881Speter        if (item->kind != SVN_RA_SVN_STRING)
2057251881Speter          return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
2058251881Speter                                  _("Path is not a string"));
2059251881Speter        full_path = svn_relpath_canonicalize(item->u.string->data, pool);
2060251881Speter        full_path = svn_fspath__join(b->fs_path->data, full_path, pool);
2061251881Speter        APR_ARRAY_PUSH(canonical_paths, const char *) = full_path;
2062251881Speter     }
2063251881Speter
2064251881Speter  SVN_ERR(log_command(b, conn, pool, "%s",
2065251881Speter                      svn_log__get_mergeinfo(canonical_paths, inherit,
2066251881Speter                                             include_descendants,
2067251881Speter                                             pool)));
2068251881Speter
2069251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
2070251881Speter  SVN_CMD_ERR(svn_repos_fs_get_mergeinfo(&mergeinfo, b->repos,
2071251881Speter                                         canonical_paths, rev,
2072251881Speter                                         inherit,
2073251881Speter                                         include_descendants,
2074251881Speter                                         authz_check_access_cb_func(b), &ab,
2075251881Speter                                         pool));
2076251881Speter  SVN_ERR(svn_mergeinfo__remove_prefix_from_catalog(&mergeinfo, mergeinfo,
2077251881Speter                                                    b->fs_path->data, pool));
2078251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success"));
2079251881Speter  iterpool = svn_pool_create(pool);
2080251881Speter  for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
2081251881Speter    {
2082251881Speter      const char *key = svn__apr_hash_index_key(hi);
2083251881Speter      svn_mergeinfo_t value = svn__apr_hash_index_val(hi);
2084251881Speter      svn_string_t *mergeinfo_string;
2085251881Speter
2086251881Speter      svn_pool_clear(iterpool);
2087251881Speter
2088251881Speter      SVN_ERR(svn_mergeinfo_to_string(&mergeinfo_string, value, iterpool));
2089251881Speter      SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "cs", key,
2090251881Speter                                      mergeinfo_string));
2091251881Speter    }
2092251881Speter  svn_pool_destroy(iterpool);
2093251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));
2094251881Speter
2095251881Speter  return SVN_NO_ERROR;
2096251881Speter}
2097251881Speter
2098251881Speter/* Send a log entry to the client. */
2099251881Speterstatic svn_error_t *log_receiver(void *baton,
2100251881Speter                                 svn_log_entry_t *log_entry,
2101251881Speter                                 apr_pool_t *pool)
2102251881Speter{
2103251881Speter  log_baton_t *b = baton;
2104251881Speter  svn_ra_svn_conn_t *conn = b->conn;
2105251881Speter  apr_hash_index_t *h;
2106251881Speter  svn_boolean_t invalid_revnum = FALSE;
2107251881Speter  char action[2];
2108251881Speter  const char *author, *date, *message;
2109251881Speter  apr_uint64_t revprop_count;
2110251881Speter
2111251881Speter  if (log_entry->revision == SVN_INVALID_REVNUM)
2112251881Speter    {
2113251881Speter      /* If the stack depth is zero, we've seen the last revision, so don't
2114251881Speter         send it, just return. */
2115251881Speter      if (b->stack_depth == 0)
2116251881Speter        return SVN_NO_ERROR;
2117251881Speter
2118251881Speter      /* Because the svn protocol won't let us send an invalid revnum, we have
2119251881Speter         to fudge here and send an additional flag. */
2120251881Speter      log_entry->revision = 0;
2121251881Speter      invalid_revnum = TRUE;
2122251881Speter      b->stack_depth--;
2123251881Speter    }
2124251881Speter
2125251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "(!"));
2126251881Speter  if (log_entry->changed_paths2)
2127251881Speter    {
2128251881Speter      for (h = apr_hash_first(pool, log_entry->changed_paths2); h;
2129251881Speter                                                        h = apr_hash_next(h))
2130251881Speter        {
2131251881Speter          const char *path = svn__apr_hash_index_key(h);
2132251881Speter          svn_log_changed_path2_t *change = svn__apr_hash_index_val(h);
2133251881Speter
2134251881Speter          action[0] = change->action;
2135251881Speter          action[1] = '\0';
2136251881Speter          SVN_ERR(svn_ra_svn__write_tuple(
2137251881Speter                      conn, pool, "cw(?cr)(cbb)",
2138251881Speter                      path,
2139251881Speter                      action,
2140251881Speter                      change->copyfrom_path,
2141251881Speter                      change->copyfrom_rev,
2142251881Speter                      svn_node_kind_to_word(change->node_kind),
2143251881Speter                      /* text_modified and props_modified are never unknown */
2144251881Speter                      change->text_modified  == svn_tristate_true,
2145251881Speter                      change->props_modified == svn_tristate_true));
2146251881Speter        }
2147251881Speter    }
2148251881Speter  svn_compat_log_revprops_out(&author, &date, &message, log_entry->revprops);
2149251881Speter  svn_compat_log_revprops_clear(log_entry->revprops);
2150251881Speter  if (log_entry->revprops)
2151251881Speter    revprop_count = apr_hash_count(log_entry->revprops);
2152251881Speter  else
2153251881Speter    revprop_count = 0;
2154251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)r(?c)(?c)(?c)bbn(!",
2155251881Speter                                  log_entry->revision,
2156251881Speter                                  author, date, message,
2157251881Speter                                  log_entry->has_children,
2158251881Speter                                  invalid_revnum, revprop_count));
2159251881Speter  SVN_ERR(svn_ra_svn__write_proplist(conn, pool, log_entry->revprops));
2160251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)b",
2161251881Speter                                  log_entry->subtractive_merge));
2162251881Speter
2163251881Speter  if (log_entry->has_children)
2164251881Speter    b->stack_depth++;
2165251881Speter
2166251881Speter  return SVN_NO_ERROR;
2167251881Speter}
2168251881Speter
2169251881Speterstatic svn_error_t *log_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
2170251881Speter                            apr_array_header_t *params, void *baton)
2171251881Speter{
2172251881Speter  svn_error_t *err, *write_err;
2173251881Speter  server_baton_t *b = baton;
2174251881Speter  svn_revnum_t start_rev, end_rev;
2175251881Speter  const char *full_path;
2176251881Speter  svn_boolean_t send_changed_paths, strict_node, include_merged_revisions;
2177251881Speter  apr_array_header_t *paths, *full_paths, *revprop_items, *revprops;
2178251881Speter  char *revprop_word;
2179251881Speter  svn_ra_svn_item_t *elt;
2180251881Speter  int i;
2181251881Speter  apr_uint64_t limit, include_merged_revs_param;
2182251881Speter  log_baton_t lb;
2183251881Speter  authz_baton_t ab;
2184251881Speter
2185251881Speter  ab.server = b;
2186251881Speter  ab.conn = conn;
2187251881Speter
2188251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "l(?r)(?r)bb?n?Bwl", &paths,
2189251881Speter                                  &start_rev, &end_rev, &send_changed_paths,
2190251881Speter                                  &strict_node, &limit,
2191251881Speter                                  &include_merged_revs_param,
2192251881Speter                                  &revprop_word, &revprop_items));
2193251881Speter
2194251881Speter  if (include_merged_revs_param == SVN_RA_SVN_UNSPECIFIED_NUMBER)
2195251881Speter    include_merged_revisions = FALSE;
2196251881Speter  else
2197251881Speter    include_merged_revisions = (svn_boolean_t) include_merged_revs_param;
2198251881Speter
2199251881Speter  if (revprop_word == NULL)
2200251881Speter    /* pre-1.5 client */
2201251881Speter    revprops = svn_compat_log_revprops_in(pool);
2202251881Speter  else if (strcmp(revprop_word, "all-revprops") == 0)
2203251881Speter    revprops = NULL;
2204251881Speter  else if (strcmp(revprop_word, "revprops") == 0)
2205251881Speter    {
2206251881Speter      SVN_ERR_ASSERT(revprop_items);
2207251881Speter
2208251881Speter      revprops = apr_array_make(pool, revprop_items->nelts,
2209251881Speter                                sizeof(char *));
2210251881Speter      for (i = 0; i < revprop_items->nelts; i++)
2211251881Speter        {
2212251881Speter          elt = &APR_ARRAY_IDX(revprop_items, i, svn_ra_svn_item_t);
2213251881Speter          if (elt->kind != SVN_RA_SVN_STRING)
2214251881Speter            return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
2215251881Speter                                    _("Log revprop entry not a string"));
2216251881Speter          APR_ARRAY_PUSH(revprops, const char *) = elt->u.string->data;
2217251881Speter        }
2218251881Speter    }
2219251881Speter  else
2220251881Speter    return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
2221251881Speter                             _("Unknown revprop word '%s' in log command"),
2222251881Speter                             revprop_word);
2223251881Speter
2224251881Speter  /* If we got an unspecified number then the user didn't send us anything,
2225251881Speter     so we assume no limit.  If it's larger than INT_MAX then someone is
2226251881Speter     messing with us, since we know the svn client libraries will never send
2227251881Speter     us anything that big, so play it safe and default to no limit. */
2228251881Speter  if (limit == SVN_RA_SVN_UNSPECIFIED_NUMBER || limit > INT_MAX)
2229251881Speter    limit = 0;
2230251881Speter
2231251881Speter  full_paths = apr_array_make(pool, paths->nelts, sizeof(const char *));
2232251881Speter  for (i = 0; i < paths->nelts; i++)
2233251881Speter    {
2234251881Speter      elt = &APR_ARRAY_IDX(paths, i, svn_ra_svn_item_t);
2235251881Speter      if (elt->kind != SVN_RA_SVN_STRING)
2236251881Speter        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
2237251881Speter                                _("Log path entry not a string"));
2238251881Speter      full_path = svn_relpath_canonicalize(elt->u.string->data, pool),
2239251881Speter      full_path = svn_fspath__join(b->fs_path->data, full_path, pool);
2240251881Speter      APR_ARRAY_PUSH(full_paths, const char *) = full_path;
2241251881Speter    }
2242251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
2243251881Speter
2244251881Speter  SVN_ERR(log_command(b, conn, pool, "%s",
2245251881Speter                      svn_log__log(full_paths, start_rev, end_rev,
2246251881Speter                                   (int) limit, send_changed_paths,
2247251881Speter                                   strict_node, include_merged_revisions,
2248251881Speter                                   revprops, pool)));
2249251881Speter
2250251881Speter  /* Get logs.  (Can't report errors back to the client at this point.) */
2251251881Speter  lb.fs_path = b->fs_path->data;
2252251881Speter  lb.conn = conn;
2253251881Speter  lb.stack_depth = 0;
2254251881Speter  err = svn_repos_get_logs4(b->repos, full_paths, start_rev, end_rev,
2255251881Speter                            (int) limit, send_changed_paths, strict_node,
2256251881Speter                            include_merged_revisions, revprops,
2257251881Speter                            authz_check_access_cb_func(b), &ab, log_receiver,
2258251881Speter                            &lb, pool);
2259251881Speter
2260251881Speter  write_err = svn_ra_svn__write_word(conn, pool, "done");
2261251881Speter  if (write_err)
2262251881Speter    {
2263251881Speter      svn_error_clear(err);
2264251881Speter      return write_err;
2265251881Speter    }
2266251881Speter  SVN_CMD_ERR(err);
2267251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
2268251881Speter  return SVN_NO_ERROR;
2269251881Speter}
2270251881Speter
2271251881Speterstatic svn_error_t *check_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
2272251881Speter                               apr_array_header_t *params, void *baton)
2273251881Speter{
2274251881Speter  server_baton_t *b = baton;
2275251881Speter  svn_revnum_t rev;
2276251881Speter  const char *path, *full_path;
2277251881Speter  svn_fs_root_t *root;
2278251881Speter  svn_node_kind_t kind;
2279251881Speter
2280251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)", &path, &rev));
2281251881Speter  full_path = svn_fspath__join(b->fs_path->data,
2282251881Speter                               svn_relpath_canonicalize(path, pool), pool);
2283251881Speter
2284251881Speter  /* Check authorizations */
2285251881Speter  SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
2286251881Speter                           full_path, FALSE));
2287251881Speter
2288251881Speter  if (!SVN_IS_VALID_REVNUM(rev))
2289251881Speter    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
2290251881Speter
2291251881Speter  SVN_ERR(log_command(b, conn, pool, "check-path %s@%d",
2292251881Speter                      svn_path_uri_encode(full_path, pool), rev));
2293251881Speter
2294251881Speter  SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool));
2295251881Speter  SVN_CMD_ERR(svn_fs_check_path(&kind, root, full_path, pool));
2296251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "w",
2297251881Speter                                         svn_node_kind_to_word(kind)));
2298251881Speter  return SVN_NO_ERROR;
2299251881Speter}
2300251881Speter
2301251881Speterstatic svn_error_t *stat_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
2302251881Speter                             apr_array_header_t *params, void *baton)
2303251881Speter{
2304251881Speter  server_baton_t *b = baton;
2305251881Speter  svn_revnum_t rev;
2306251881Speter  const char *path, *full_path, *cdate;
2307251881Speter  svn_fs_root_t *root;
2308251881Speter  svn_dirent_t *dirent;
2309251881Speter
2310251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)", &path, &rev));
2311251881Speter  full_path = svn_fspath__join(b->fs_path->data,
2312251881Speter                               svn_relpath_canonicalize(path, pool), pool);
2313251881Speter
2314251881Speter  /* Check authorizations */
2315251881Speter  SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
2316251881Speter                           full_path, FALSE));
2317251881Speter
2318251881Speter  if (!SVN_IS_VALID_REVNUM(rev))
2319251881Speter    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
2320251881Speter
2321251881Speter  SVN_ERR(log_command(b, conn, pool, "stat %s@%d",
2322251881Speter                      svn_path_uri_encode(full_path, pool), rev));
2323251881Speter
2324251881Speter  SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool));
2325251881Speter  SVN_CMD_ERR(svn_repos_stat(&dirent, root, full_path, pool));
2326251881Speter
2327251881Speter  /* Need to return the equivalent of "(?l)", since that's what the
2328251881Speter     client is reading.  */
2329251881Speter
2330251881Speter  if (dirent == NULL)
2331251881Speter    {
2332251881Speter      SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "()"));
2333251881Speter      return SVN_NO_ERROR;
2334251881Speter    }
2335251881Speter
2336251881Speter  cdate = (dirent->time == (time_t) -1) ? NULL
2337251881Speter    : svn_time_to_cstring(dirent->time, pool);
2338251881Speter
2339251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "((wnbr(?c)(?c)))",
2340251881Speter                                         svn_node_kind_to_word(dirent->kind),
2341251881Speter                                         (apr_uint64_t) dirent->size,
2342251881Speter                                         dirent->has_props, dirent->created_rev,
2343251881Speter                                         cdate, dirent->last_author));
2344251881Speter
2345251881Speter  return SVN_NO_ERROR;
2346251881Speter}
2347251881Speter
2348251881Speterstatic svn_error_t *get_locations(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
2349251881Speter                                  apr_array_header_t *params, void *baton)
2350251881Speter{
2351251881Speter  svn_error_t *err, *write_err;
2352251881Speter  server_baton_t *b = baton;
2353251881Speter  svn_revnum_t revision;
2354251881Speter  apr_array_header_t *location_revisions, *loc_revs_proto;
2355251881Speter  svn_ra_svn_item_t *elt;
2356251881Speter  int i;
2357251881Speter  const char *relative_path;
2358251881Speter  svn_revnum_t peg_revision;
2359251881Speter  apr_hash_t *fs_locations;
2360251881Speter  const char *abs_path;
2361251881Speter  authz_baton_t ab;
2362251881Speter
2363251881Speter  ab.server = b;
2364251881Speter  ab.conn = conn;
2365251881Speter
2366251881Speter  /* Parse the arguments. */
2367251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "crl", &relative_path,
2368251881Speter                                  &peg_revision,
2369251881Speter                                  &loc_revs_proto));
2370251881Speter  relative_path = svn_relpath_canonicalize(relative_path, pool);
2371251881Speter
2372251881Speter  abs_path = svn_fspath__join(b->fs_path->data, relative_path, pool);
2373251881Speter
2374251881Speter  location_revisions = apr_array_make(pool, loc_revs_proto->nelts,
2375251881Speter                                      sizeof(svn_revnum_t));
2376251881Speter  for (i = 0; i < loc_revs_proto->nelts; i++)
2377251881Speter    {
2378251881Speter      elt = &APR_ARRAY_IDX(loc_revs_proto, i, svn_ra_svn_item_t);
2379251881Speter      if (elt->kind != SVN_RA_SVN_NUMBER)
2380251881Speter        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
2381251881Speter                                "Get-locations location revisions entry "
2382251881Speter                                "not a revision number");
2383251881Speter      revision = (svn_revnum_t)(elt->u.number);
2384251881Speter      APR_ARRAY_PUSH(location_revisions, svn_revnum_t) = revision;
2385251881Speter    }
2386251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
2387251881Speter  SVN_ERR(log_command(b, conn, pool, "%s",
2388251881Speter                      svn_log__get_locations(abs_path, peg_revision,
2389251881Speter                                             location_revisions, pool)));
2390251881Speter
2391251881Speter  /* All the parameters are fine - let's perform the query against the
2392251881Speter   * repository. */
2393251881Speter
2394251881Speter  /* We store both err and write_err here, so the client will get
2395251881Speter   * the "done" even if there was an error in fetching the results. */
2396251881Speter
2397251881Speter  err = svn_repos_trace_node_locations(b->fs, &fs_locations, abs_path,
2398251881Speter                                       peg_revision, location_revisions,
2399251881Speter                                       authz_check_access_cb_func(b), &ab,
2400251881Speter                                       pool);
2401251881Speter
2402251881Speter  /* Now, write the results to the connection. */
2403251881Speter  if (!err)
2404251881Speter    {
2405251881Speter      if (fs_locations)
2406251881Speter        {
2407251881Speter          apr_hash_index_t *iter;
2408251881Speter
2409251881Speter          for (iter = apr_hash_first(pool, fs_locations); iter;
2410251881Speter              iter = apr_hash_next(iter))
2411251881Speter            {
2412251881Speter              const svn_revnum_t *iter_key = svn__apr_hash_index_key(iter);
2413251881Speter              const char *iter_value = svn__apr_hash_index_val(iter);
2414251881Speter
2415251881Speter              SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "rc",
2416251881Speter                                              *iter_key, iter_value));
2417251881Speter            }
2418251881Speter        }
2419251881Speter    }
2420251881Speter
2421251881Speter  write_err = svn_ra_svn__write_word(conn, pool, "done");
2422251881Speter  if (write_err)
2423251881Speter    {
2424251881Speter      svn_error_clear(err);
2425251881Speter      return write_err;
2426251881Speter    }
2427251881Speter  SVN_CMD_ERR(err);
2428251881Speter
2429251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
2430251881Speter
2431251881Speter  return SVN_NO_ERROR;
2432251881Speter}
2433251881Speter
2434251881Speterstatic svn_error_t *gls_receiver(svn_location_segment_t *segment,
2435251881Speter                                 void *baton,
2436251881Speter                                 apr_pool_t *pool)
2437251881Speter{
2438251881Speter  svn_ra_svn_conn_t *conn = baton;
2439251881Speter  return svn_ra_svn__write_tuple(conn, pool, "rr(?c)",
2440251881Speter                                 segment->range_start,
2441251881Speter                                 segment->range_end,
2442251881Speter                                 segment->path);
2443251881Speter}
2444251881Speter
2445251881Speterstatic svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn,
2446251881Speter                                          apr_pool_t *pool,
2447251881Speter                                          apr_array_header_t *params,
2448251881Speter                                          void *baton)
2449251881Speter{
2450251881Speter  svn_error_t *err, *write_err;
2451251881Speter  server_baton_t *b = baton;
2452251881Speter  svn_revnum_t peg_revision, start_rev, end_rev;
2453251881Speter  const char *relative_path;
2454251881Speter  const char *abs_path;
2455251881Speter  authz_baton_t ab;
2456251881Speter
2457251881Speter  ab.server = b;
2458251881Speter  ab.conn = conn;
2459251881Speter
2460251881Speter  /* Parse the arguments. */
2461251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)(?r)(?r)",
2462251881Speter                                  &relative_path, &peg_revision,
2463251881Speter                                  &start_rev, &end_rev));
2464251881Speter  relative_path = svn_relpath_canonicalize(relative_path, pool);
2465251881Speter
2466251881Speter  abs_path = svn_fspath__join(b->fs_path->data, relative_path, pool);
2467251881Speter
2468251881Speter  if (SVN_IS_VALID_REVNUM(start_rev)
2469251881Speter      && SVN_IS_VALID_REVNUM(end_rev)
2470251881Speter      && (end_rev > start_rev))
2471251881Speter    {
2472251881Speter      err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
2473251881Speter                              "Get-location-segments end revision must not be "
2474251881Speter                              "younger than start revision");
2475251881Speter      return log_fail_and_flush(err, b, conn, pool);
2476251881Speter    }
2477251881Speter
2478251881Speter  if (SVN_IS_VALID_REVNUM(peg_revision)
2479251881Speter      && SVN_IS_VALID_REVNUM(start_rev)
2480251881Speter      && (start_rev > peg_revision))
2481251881Speter    {
2482251881Speter      err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
2483251881Speter                              "Get-location-segments start revision must not "
2484251881Speter                              "be younger than peg revision");
2485251881Speter      return log_fail_and_flush(err, b, conn, pool);
2486251881Speter    }
2487251881Speter
2488251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
2489251881Speter  SVN_ERR(log_command(baton, conn, pool, "%s",
2490251881Speter                      svn_log__get_location_segments(abs_path, peg_revision,
2491251881Speter                                                     start_rev, end_rev,
2492251881Speter                                                     pool)));
2493251881Speter
2494251881Speter  /* All the parameters are fine - let's perform the query against the
2495251881Speter   * repository. */
2496251881Speter
2497251881Speter  /* We store both err and write_err here, so the client will get
2498251881Speter   * the "done" even if there was an error in fetching the results. */
2499251881Speter
2500251881Speter  err = svn_repos_node_location_segments(b->repos, abs_path,
2501251881Speter                                         peg_revision, start_rev, end_rev,
2502251881Speter                                         gls_receiver, (void *)conn,
2503251881Speter                                         authz_check_access_cb_func(b), &ab,
2504251881Speter                                         pool);
2505251881Speter  write_err = svn_ra_svn__write_word(conn, pool, "done");
2506251881Speter  if (write_err)
2507251881Speter    {
2508251881Speter      svn_error_clear(err);
2509251881Speter      return write_err;
2510251881Speter    }
2511251881Speter  SVN_CMD_ERR(err);
2512251881Speter
2513251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
2514251881Speter
2515251881Speter  return SVN_NO_ERROR;
2516251881Speter}
2517251881Speter
2518251881Speter/* This implements svn_write_fn_t.  Write LEN bytes starting at DATA to the
2519251881Speter   client as a string. */
2520251881Speterstatic svn_error_t *svndiff_handler(void *baton, const char *data,
2521251881Speter                                    apr_size_t *len)
2522251881Speter{
2523251881Speter  file_revs_baton_t *b = baton;
2524251881Speter  svn_string_t str;
2525251881Speter
2526251881Speter  str.data = data;
2527251881Speter  str.len = *len;
2528251881Speter  return svn_ra_svn__write_string(b->conn, b->pool, &str);
2529251881Speter}
2530251881Speter
2531251881Speter/* This implements svn_close_fn_t.  Mark the end of the data by writing an
2532251881Speter   empty string to the client. */
2533251881Speterstatic svn_error_t *svndiff_close_handler(void *baton)
2534251881Speter{
2535251881Speter  file_revs_baton_t *b = baton;
2536251881Speter
2537251881Speter  SVN_ERR(svn_ra_svn__write_cstring(b->conn, b->pool, ""));
2538251881Speter  return SVN_NO_ERROR;
2539251881Speter}
2540251881Speter
2541251881Speter/* This implements the svn_repos_file_rev_handler_t interface. */
2542251881Speterstatic svn_error_t *file_rev_handler(void *baton, const char *path,
2543251881Speter                                     svn_revnum_t rev, apr_hash_t *rev_props,
2544251881Speter                                     svn_boolean_t merged_revision,
2545251881Speter                                     svn_txdelta_window_handler_t *d_handler,
2546251881Speter                                     void **d_baton,
2547251881Speter                                     apr_array_header_t *prop_diffs,
2548251881Speter                                     apr_pool_t *pool)
2549251881Speter{
2550251881Speter  file_revs_baton_t *frb = baton;
2551251881Speter  svn_stream_t *stream;
2552251881Speter
2553251881Speter  SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "cr(!",
2554251881Speter                                  path, rev));
2555251881Speter  SVN_ERR(svn_ra_svn__write_proplist(frb->conn, pool, rev_props));
2556251881Speter  SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "!)(!"));
2557251881Speter  SVN_ERR(write_prop_diffs(frb->conn, pool, prop_diffs));
2558251881Speter  SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "!)b", merged_revision));
2559251881Speter
2560251881Speter  /* Store the pool for the delta stream. */
2561251881Speter  frb->pool = pool;
2562251881Speter
2563251881Speter  /* Prepare for the delta or just write an empty string. */
2564251881Speter  if (d_handler)
2565251881Speter    {
2566251881Speter      stream = svn_stream_create(baton, pool);
2567251881Speter      svn_stream_set_write(stream, svndiff_handler);
2568251881Speter      svn_stream_set_close(stream, svndiff_close_handler);
2569251881Speter
2570251881Speter      /* If the connection does not support SVNDIFF1 or if we don't want to use
2571251881Speter       * compression, use the non-compressing "version 0" implementation */
2572251881Speter      if (   svn_ra_svn_compression_level(frb->conn) > 0
2573251881Speter          && svn_ra_svn_has_capability(frb->conn, SVN_RA_SVN_CAP_SVNDIFF1))
2574251881Speter        svn_txdelta_to_svndiff3(d_handler, d_baton, stream, 1,
2575251881Speter                                svn_ra_svn_compression_level(frb->conn), pool);
2576251881Speter      else
2577251881Speter        svn_txdelta_to_svndiff3(d_handler, d_baton, stream, 0,
2578251881Speter                                svn_ra_svn_compression_level(frb->conn), pool);
2579251881Speter    }
2580251881Speter  else
2581251881Speter    SVN_ERR(svn_ra_svn__write_cstring(frb->conn, pool, ""));
2582251881Speter
2583251881Speter  return SVN_NO_ERROR;
2584251881Speter}
2585251881Speter
2586251881Speterstatic svn_error_t *get_file_revs(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
2587251881Speter                                  apr_array_header_t *params, void *baton)
2588251881Speter{
2589251881Speter  server_baton_t *b = baton;
2590251881Speter  svn_error_t *err, *write_err;
2591251881Speter  file_revs_baton_t frb;
2592251881Speter  svn_revnum_t start_rev, end_rev;
2593251881Speter  const char *path;
2594251881Speter  const char *full_path;
2595251881Speter  apr_uint64_t include_merged_revs_param;
2596251881Speter  svn_boolean_t include_merged_revisions;
2597251881Speter  authz_baton_t ab;
2598251881Speter
2599251881Speter  ab.server = b;
2600251881Speter  ab.conn = conn;
2601251881Speter
2602251881Speter  /* Parse arguments. */
2603251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)(?r)?B",
2604251881Speter                                  &path, &start_rev, &end_rev,
2605251881Speter                                  &include_merged_revs_param));
2606251881Speter  path = svn_relpath_canonicalize(path, pool);
2607251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
2608251881Speter  full_path = svn_fspath__join(b->fs_path->data, path, pool);
2609251881Speter
2610251881Speter  if (include_merged_revs_param == SVN_RA_SVN_UNSPECIFIED_NUMBER)
2611251881Speter    include_merged_revisions = FALSE;
2612251881Speter  else
2613251881Speter    include_merged_revisions = (svn_boolean_t) include_merged_revs_param;
2614251881Speter
2615251881Speter  SVN_ERR(log_command(b, conn, pool, "%s",
2616251881Speter                      svn_log__get_file_revs(full_path, start_rev, end_rev,
2617251881Speter                                             include_merged_revisions,
2618251881Speter                                             pool)));
2619251881Speter
2620251881Speter  frb.conn = conn;
2621251881Speter  frb.pool = NULL;
2622251881Speter
2623251881Speter  err = svn_repos_get_file_revs2(b->repos, full_path, start_rev, end_rev,
2624251881Speter                                 include_merged_revisions,
2625251881Speter                                 authz_check_access_cb_func(b), &ab,
2626251881Speter                                 file_rev_handler, &frb, pool);
2627251881Speter  write_err = svn_ra_svn__write_word(conn, pool, "done");
2628251881Speter  if (write_err)
2629251881Speter    {
2630251881Speter      svn_error_clear(err);
2631251881Speter      return write_err;
2632251881Speter    }
2633251881Speter  SVN_CMD_ERR(err);
2634251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
2635251881Speter
2636251881Speter  return SVN_NO_ERROR;
2637251881Speter}
2638251881Speter
2639251881Speterstatic svn_error_t *lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
2640251881Speter                         apr_array_header_t *params, void *baton)
2641251881Speter{
2642251881Speter  server_baton_t *b = baton;
2643251881Speter  const char *path;
2644251881Speter  const char *comment;
2645251881Speter  const char *full_path;
2646251881Speter  svn_boolean_t steal_lock;
2647251881Speter  svn_revnum_t current_rev;
2648251881Speter  svn_lock_t *l;
2649251881Speter
2650251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?c)b(?r)", &path, &comment,
2651251881Speter                                  &steal_lock, &current_rev));
2652251881Speter  full_path = svn_fspath__join(b->fs_path->data,
2653251881Speter                               svn_relpath_canonicalize(path, pool), pool);
2654251881Speter
2655251881Speter  SVN_ERR(must_have_access(conn, pool, b, svn_authz_write,
2656251881Speter                           full_path, TRUE));
2657251881Speter  SVN_ERR(log_command(b, conn, pool, "%s",
2658251881Speter                      svn_log__lock_one_path(full_path, steal_lock, pool)));
2659251881Speter
2660251881Speter  SVN_CMD_ERR(svn_repos_fs_lock(&l, b->repos, full_path, NULL, comment, 0,
2661251881Speter                                0, /* No expiration time. */
2662251881Speter                                current_rev, steal_lock, pool));
2663251881Speter
2664251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(!", "success"));
2665251881Speter  SVN_ERR(write_lock(conn, pool, l));
2666251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)"));
2667251881Speter
2668251881Speter  return SVN_NO_ERROR;
2669251881Speter}
2670251881Speter
2671251881Speterstatic svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
2672251881Speter                              apr_array_header_t *params, void *baton)
2673251881Speter{
2674251881Speter  server_baton_t *b = baton;
2675251881Speter  apr_array_header_t *path_revs;
2676251881Speter  const char *comment;
2677251881Speter  svn_boolean_t steal_lock;
2678251881Speter  int i;
2679251881Speter  apr_pool_t *subpool;
2680251881Speter  const char *path;
2681251881Speter  const char *full_path;
2682251881Speter  svn_revnum_t current_rev;
2683251881Speter  apr_array_header_t *log_paths;
2684251881Speter  svn_lock_t *l;
2685251881Speter  svn_error_t *err = SVN_NO_ERROR, *write_err;
2686251881Speter
2687251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?c)bl", &comment, &steal_lock,
2688251881Speter                                  &path_revs));
2689251881Speter
2690251881Speter  subpool = svn_pool_create(pool);
2691251881Speter
2692251881Speter  /* Because we can only send a single auth reply per request, we send
2693251881Speter     a reply before parsing the lock commands.  This means an authz
2694251881Speter     access denial will abort the processing of the locks and return
2695251881Speter     an error. */
2696251881Speter  SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, TRUE));
2697251881Speter
2698251881Speter  /* Loop through the lock requests. */
2699251881Speter  log_paths = apr_array_make(pool, path_revs->nelts, sizeof(full_path));
2700251881Speter  for (i = 0; i < path_revs->nelts; ++i)
2701251881Speter    {
2702251881Speter      svn_ra_svn_item_t *item = &APR_ARRAY_IDX(path_revs, i,
2703251881Speter                                               svn_ra_svn_item_t);
2704251881Speter
2705251881Speter      svn_pool_clear(subpool);
2706251881Speter
2707251881Speter      if (item->kind != SVN_RA_SVN_LIST)
2708251881Speter        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
2709251881Speter                                "Lock requests should be list of lists");
2710251881Speter
2711251881Speter      SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, pool, "c(?r)", &path,
2712251881Speter                                      &current_rev));
2713251881Speter
2714251881Speter      /* Allocate the full_path out of pool so it will survive for use
2715251881Speter       * by operational logging, after this loop. */
2716251881Speter      full_path = svn_fspath__join(b->fs_path->data,
2717251881Speter                                   svn_relpath_canonicalize(path, subpool),
2718251881Speter                                   pool);
2719251881Speter      APR_ARRAY_PUSH(log_paths, const char *) = full_path;
2720251881Speter
2721251881Speter      if (! lookup_access(pool, b, conn, svn_authz_write, full_path, TRUE))
2722251881Speter        {
2723251881Speter          err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, NULL,
2724251881Speter                                     b, conn, pool);
2725251881Speter          break;
2726251881Speter        }
2727251881Speter
2728251881Speter      err = svn_repos_fs_lock(&l, b->repos, full_path,
2729251881Speter                              NULL, comment, FALSE,
2730251881Speter                              0, /* No expiration time. */
2731251881Speter                              current_rev,
2732251881Speter                              steal_lock, subpool);
2733251881Speter
2734251881Speter      if (err)
2735251881Speter        {
2736251881Speter          if (SVN_ERR_IS_LOCK_ERROR(err))
2737251881Speter            {
2738251881Speter              write_err = svn_ra_svn__write_cmd_failure(conn, pool, err);
2739251881Speter              svn_error_clear(err);
2740251881Speter              err = NULL;
2741251881Speter              SVN_ERR(write_err);
2742251881Speter            }
2743251881Speter          else
2744251881Speter            break;
2745251881Speter        }
2746251881Speter      else
2747251881Speter        {
2748251881Speter          SVN_ERR(svn_ra_svn__write_tuple(conn, subpool, "w!", "success"));
2749251881Speter          SVN_ERR(write_lock(conn, subpool, l));
2750251881Speter          SVN_ERR(svn_ra_svn__write_tuple(conn, subpool, "!"));
2751251881Speter        }
2752251881Speter    }
2753251881Speter
2754251881Speter  svn_pool_destroy(subpool);
2755251881Speter
2756251881Speter  SVN_ERR(log_command(b, conn, pool, "%s",
2757251881Speter                      svn_log__lock(log_paths, steal_lock, pool)));
2758251881Speter
2759251881Speter  /* NOTE: err might contain a fatal locking error from the loop above. */
2760251881Speter  write_err = svn_ra_svn__write_word(conn, pool, "done");
2761251881Speter  if (!write_err)
2762251881Speter    SVN_CMD_ERR(err);
2763251881Speter  svn_error_clear(err);
2764251881Speter  SVN_ERR(write_err);
2765251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
2766251881Speter
2767251881Speter  return SVN_NO_ERROR;
2768251881Speter}
2769251881Speter
2770251881Speterstatic svn_error_t *unlock(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
2771251881Speter                           apr_array_header_t *params, void *baton)
2772251881Speter{
2773251881Speter  server_baton_t *b = baton;
2774251881Speter  const char *path, *token, *full_path;
2775251881Speter  svn_boolean_t break_lock;
2776251881Speter
2777251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?c)b", &path, &token,
2778251881Speter                                 &break_lock));
2779251881Speter
2780251881Speter  full_path = svn_fspath__join(b->fs_path->data,
2781251881Speter                               svn_relpath_canonicalize(path, pool), pool);
2782251881Speter
2783251881Speter  /* Username required unless break_lock was specified. */
2784251881Speter  SVN_ERR(must_have_access(conn, pool, b, svn_authz_write,
2785251881Speter                           full_path, ! break_lock));
2786251881Speter  SVN_ERR(log_command(b, conn, pool, "%s",
2787251881Speter                      svn_log__unlock_one_path(full_path, break_lock, pool)));
2788251881Speter
2789251881Speter  SVN_CMD_ERR(svn_repos_fs_unlock(b->repos, full_path, token, break_lock,
2790251881Speter                                  pool));
2791251881Speter
2792251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
2793251881Speter
2794251881Speter  return SVN_NO_ERROR;
2795251881Speter}
2796251881Speter
2797251881Speterstatic svn_error_t *unlock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
2798251881Speter                                apr_array_header_t *params, void *baton)
2799251881Speter{
2800251881Speter  server_baton_t *b = baton;
2801251881Speter  svn_boolean_t break_lock;
2802251881Speter  apr_array_header_t *unlock_tokens;
2803251881Speter  int i;
2804251881Speter  apr_pool_t *subpool;
2805251881Speter  const char *path;
2806251881Speter  const char *full_path;
2807251881Speter  apr_array_header_t *log_paths;
2808251881Speter  const char *token;
2809251881Speter  svn_error_t *err = SVN_NO_ERROR, *write_err;
2810251881Speter
2811251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "bl", &break_lock,
2812251881Speter                                  &unlock_tokens));
2813251881Speter
2814251881Speter  /* Username required unless break_lock was specified. */
2815251881Speter  SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, ! break_lock));
2816251881Speter
2817251881Speter  subpool = svn_pool_create(pool);
2818251881Speter
2819251881Speter  /* Loop through the unlock requests. */
2820251881Speter  log_paths = apr_array_make(pool, unlock_tokens->nelts, sizeof(full_path));
2821251881Speter  for (i = 0; i < unlock_tokens->nelts; i++)
2822251881Speter    {
2823251881Speter      svn_ra_svn_item_t *item = &APR_ARRAY_IDX(unlock_tokens, i,
2824251881Speter                                               svn_ra_svn_item_t);
2825251881Speter
2826251881Speter      svn_pool_clear(subpool);
2827251881Speter
2828251881Speter      if (item->kind != SVN_RA_SVN_LIST)
2829251881Speter        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
2830251881Speter                                "Unlock request should be a list of lists");
2831251881Speter
2832251881Speter      SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?c)", &path,
2833251881Speter                                      &token));
2834251881Speter
2835251881Speter      /* Allocate the full_path out of pool so it will survive for use
2836251881Speter       * by operational logging, after this loop. */
2837251881Speter      full_path = svn_fspath__join(b->fs_path->data,
2838251881Speter                                   svn_relpath_canonicalize(path, subpool),
2839251881Speter                                   pool);
2840251881Speter      APR_ARRAY_PUSH(log_paths, const char *) = full_path;
2841251881Speter
2842251881Speter      if (! lookup_access(subpool, b, conn, svn_authz_write, full_path,
2843251881Speter                          ! break_lock))
2844251881Speter        return svn_error_create(SVN_ERR_RA_SVN_CMD_ERR,
2845251881Speter                                error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED,
2846251881Speter                                                     NULL, NULL,
2847251881Speter                                                     b, conn, pool),
2848251881Speter                                NULL);
2849251881Speter
2850251881Speter      err = svn_repos_fs_unlock(b->repos, full_path, token, break_lock,
2851251881Speter                                subpool);
2852251881Speter      if (err)
2853251881Speter        {
2854251881Speter          if (SVN_ERR_IS_UNLOCK_ERROR(err))
2855251881Speter            {
2856251881Speter              write_err = svn_ra_svn__write_cmd_failure(conn, pool, err);
2857251881Speter              svn_error_clear(err);
2858251881Speter              err = NULL;
2859251881Speter              SVN_ERR(write_err);
2860251881Speter            }
2861251881Speter          else
2862251881Speter            break;
2863251881Speter        }
2864251881Speter      else
2865251881Speter        SVN_ERR(svn_ra_svn__write_tuple(conn, subpool, "w(c)", "success",
2866251881Speter                                        path));
2867251881Speter    }
2868251881Speter
2869251881Speter  svn_pool_destroy(subpool);
2870251881Speter
2871251881Speter  SVN_ERR(log_command(b, conn, pool, "%s",
2872251881Speter                      svn_log__unlock(log_paths, break_lock, pool)));
2873251881Speter
2874251881Speter  /* NOTE: err might contain a fatal unlocking error from the loop above. */
2875251881Speter  write_err = svn_ra_svn__write_word(conn, pool, "done");
2876251881Speter  if (! write_err)
2877251881Speter    SVN_CMD_ERR(err);
2878251881Speter  svn_error_clear(err);
2879251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
2880251881Speter
2881251881Speter  return SVN_NO_ERROR;
2882251881Speter}
2883251881Speter
2884251881Speterstatic svn_error_t *get_lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
2885251881Speter                             apr_array_header_t *params, void *baton)
2886251881Speter{
2887251881Speter  server_baton_t *b = baton;
2888251881Speter  const char *path;
2889251881Speter  const char *full_path;
2890251881Speter  svn_lock_t *l;
2891251881Speter
2892251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &path));
2893251881Speter
2894251881Speter  full_path = svn_fspath__join(b->fs_path->data,
2895251881Speter                               svn_relpath_canonicalize(path, pool), pool);
2896251881Speter
2897251881Speter  SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
2898251881Speter                           full_path, FALSE));
2899251881Speter  SVN_ERR(log_command(b, conn, pool, "get-lock %s",
2900251881Speter                      svn_path_uri_encode(full_path, pool)));
2901251881Speter
2902251881Speter  SVN_CMD_ERR(svn_fs_get_lock(&l, b->fs, full_path, pool));
2903251881Speter
2904251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success"));
2905251881Speter  if (l)
2906251881Speter    SVN_ERR(write_lock(conn, pool, l));
2907251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));
2908251881Speter
2909251881Speter  return SVN_NO_ERROR;
2910251881Speter}
2911251881Speter
2912251881Speterstatic svn_error_t *get_locks(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
2913251881Speter                              apr_array_header_t *params, void *baton)
2914251881Speter{
2915251881Speter  server_baton_t *b = baton;
2916251881Speter  const char *path;
2917251881Speter  const char *full_path;
2918251881Speter  const char *depth_word;
2919251881Speter  svn_depth_t depth;
2920251881Speter  apr_hash_t *locks;
2921251881Speter  apr_hash_index_t *hi;
2922251881Speter  svn_error_t *err;
2923251881Speter  authz_baton_t ab;
2924251881Speter
2925251881Speter  ab.server = b;
2926251881Speter  ab.conn = conn;
2927251881Speter
2928251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c?(?w)", &path, &depth_word));
2929251881Speter
2930251881Speter  depth = depth_word ? svn_depth_from_word(depth_word) : svn_depth_infinity;
2931251881Speter  if ((depth != svn_depth_empty) &&
2932251881Speter      (depth != svn_depth_files) &&
2933251881Speter      (depth != svn_depth_immediates) &&
2934251881Speter      (depth != svn_depth_infinity))
2935251881Speter    {
2936251881Speter      err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
2937251881Speter                             "Invalid 'depth' specified in get-locks request");
2938251881Speter      return log_fail_and_flush(err, b, conn, pool);
2939251881Speter    }
2940251881Speter
2941251881Speter  full_path = svn_fspath__join(b->fs_path->data,
2942251881Speter                               svn_relpath_canonicalize(path, pool), pool);
2943251881Speter
2944251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
2945251881Speter
2946251881Speter  SVN_ERR(log_command(b, conn, pool, "get-locks %s",
2947251881Speter                      svn_path_uri_encode(full_path, pool)));
2948251881Speter  SVN_CMD_ERR(svn_repos_fs_get_locks2(&locks, b->repos, full_path, depth,
2949251881Speter                                      authz_check_access_cb_func(b), &ab,
2950251881Speter                                      pool));
2951251881Speter
2952251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success"));
2953251881Speter  for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi))
2954251881Speter    {
2955251881Speter      svn_lock_t *l = svn__apr_hash_index_val(hi);
2956251881Speter
2957251881Speter      SVN_ERR(write_lock(conn, pool, l));
2958251881Speter    }
2959251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));
2960251881Speter
2961251881Speter  return SVN_NO_ERROR;
2962251881Speter}
2963251881Speter
2964251881Speterstatic svn_error_t *replay_one_revision(svn_ra_svn_conn_t *conn,
2965251881Speter                                        server_baton_t *b,
2966251881Speter                                        svn_revnum_t rev,
2967251881Speter                                        svn_revnum_t low_water_mark,
2968251881Speter                                        svn_boolean_t send_deltas,
2969251881Speter                                        apr_pool_t *pool)
2970251881Speter{
2971251881Speter  const svn_delta_editor_t *editor;
2972251881Speter  void *edit_baton;
2973251881Speter  svn_fs_root_t *root;
2974251881Speter  svn_error_t *err;
2975251881Speter  authz_baton_t ab;
2976251881Speter
2977251881Speter  ab.server = b;
2978251881Speter  ab.conn = conn;
2979251881Speter
2980251881Speter  SVN_ERR(log_command(b, conn, pool,
2981251881Speter                      svn_log__replay(b->fs_path->data, rev, pool)));
2982251881Speter
2983251881Speter  svn_ra_svn_get_editor(&editor, &edit_baton, conn, pool, NULL, NULL);
2984251881Speter
2985251881Speter  err = svn_fs_revision_root(&root, b->fs, rev, pool);
2986251881Speter
2987251881Speter  if (! err)
2988251881Speter    err = svn_repos_replay2(root, b->fs_path->data, low_water_mark,
2989251881Speter                            send_deltas, editor, edit_baton,
2990251881Speter                            authz_check_access_cb_func(b), &ab, pool);
2991251881Speter
2992251881Speter  if (err)
2993251881Speter    svn_error_clear(editor->abort_edit(edit_baton, pool));
2994251881Speter  SVN_CMD_ERR(err);
2995251881Speter
2996251881Speter  return svn_ra_svn__write_cmd_finish_replay(conn, pool);
2997251881Speter}
2998251881Speter
2999251881Speterstatic svn_error_t *replay(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
3000251881Speter                           apr_array_header_t *params, void *baton)
3001251881Speter{
3002251881Speter  svn_revnum_t rev, low_water_mark;
3003251881Speter  svn_boolean_t send_deltas;
3004251881Speter  server_baton_t *b = baton;
3005251881Speter
3006251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rrb", &rev, &low_water_mark,
3007251881Speter                                 &send_deltas));
3008251881Speter
3009251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
3010251881Speter
3011251881Speter  SVN_ERR(replay_one_revision(conn, b, rev, low_water_mark,
3012251881Speter                              send_deltas, pool));
3013251881Speter
3014251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
3015251881Speter
3016251881Speter  return SVN_NO_ERROR;
3017251881Speter}
3018251881Speter
3019251881Speterstatic svn_error_t *replay_range(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
3020251881Speter                                 apr_array_header_t *params, void *baton)
3021251881Speter{
3022251881Speter  svn_revnum_t start_rev, end_rev, rev, low_water_mark;
3023251881Speter  svn_boolean_t send_deltas;
3024251881Speter  server_baton_t *b = baton;
3025251881Speter  apr_pool_t *iterpool;
3026251881Speter  authz_baton_t ab;
3027251881Speter
3028251881Speter  ab.server = b;
3029251881Speter  ab.conn = conn;
3030251881Speter
3031251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rrrb", &start_rev,
3032251881Speter                                 &end_rev, &low_water_mark,
3033251881Speter                                 &send_deltas));
3034251881Speter
3035251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
3036251881Speter
3037251881Speter  iterpool = svn_pool_create(pool);
3038251881Speter  for (rev = start_rev; rev <= end_rev; rev++)
3039251881Speter    {
3040251881Speter      apr_hash_t *props;
3041251881Speter
3042251881Speter      svn_pool_clear(iterpool);
3043251881Speter
3044251881Speter      SVN_CMD_ERR(svn_repos_fs_revision_proplist(&props, b->repos, rev,
3045251881Speter                                                 authz_check_access_cb_func(b),
3046251881Speter                                                 &ab,
3047251881Speter                                                 iterpool));
3048251881Speter      SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "w(!", "revprops"));
3049251881Speter      SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, props));
3050251881Speter      SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!)"));
3051251881Speter
3052251881Speter      SVN_ERR(replay_one_revision(conn, b, rev, low_water_mark,
3053251881Speter                                  send_deltas, iterpool));
3054251881Speter
3055251881Speter    }
3056251881Speter  svn_pool_destroy(iterpool);
3057251881Speter
3058251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
3059251881Speter
3060251881Speter  return SVN_NO_ERROR;
3061251881Speter}
3062251881Speter
3063251881Speterstatic svn_error_t *
3064251881Speterget_deleted_rev(svn_ra_svn_conn_t *conn,
3065251881Speter                apr_pool_t *pool,
3066251881Speter                apr_array_header_t *params,
3067251881Speter                void *baton)
3068251881Speter{
3069251881Speter  server_baton_t *b = baton;
3070251881Speter  const char *path, *full_path;
3071251881Speter  svn_revnum_t peg_revision;
3072251881Speter  svn_revnum_t end_revision;
3073251881Speter  svn_revnum_t revision_deleted;
3074251881Speter
3075251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "crr",
3076251881Speter                                 &path, &peg_revision, &end_revision));
3077251881Speter  full_path = svn_fspath__join(b->fs_path->data,
3078251881Speter                               svn_relpath_canonicalize(path, pool), pool);
3079251881Speter  SVN_ERR(log_command(b, conn, pool, "get-deleted-rev"));
3080251881Speter  SVN_ERR(trivial_auth_request(conn, pool, b));
3081251881Speter  SVN_ERR(svn_repos_deleted_rev(b->fs, full_path, peg_revision, end_revision,
3082251881Speter                                &revision_deleted, pool));
3083251881Speter  SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", revision_deleted));
3084251881Speter  return SVN_NO_ERROR;
3085251881Speter}
3086251881Speter
3087251881Speterstatic svn_error_t *
3088251881Speterget_inherited_props(svn_ra_svn_conn_t *conn,
3089251881Speter                    apr_pool_t *pool,
3090251881Speter                    apr_array_header_t *params,
3091251881Speter                    void *baton)
3092251881Speter{
3093251881Speter  server_baton_t *b = baton;
3094251881Speter  const char *path, *full_path;
3095251881Speter  svn_revnum_t rev;
3096251881Speter  svn_fs_root_t *root;
3097251881Speter  apr_array_header_t *inherited_props;
3098251881Speter  int i;
3099251881Speter  apr_pool_t *iterpool = svn_pool_create(pool);
3100251881Speter  authz_baton_t ab;
3101251881Speter
3102251881Speter  ab.server = b;
3103251881Speter  ab.conn = conn;
3104251881Speter
3105251881Speter  /* Parse arguments. */
3106251881Speter  SVN_ERR(svn_ra_svn__parse_tuple(params, iterpool, "c(?r)", &path, &rev));
3107251881Speter
3108251881Speter  full_path = svn_fspath__join(b->fs_path->data,
3109251881Speter                               svn_relpath_canonicalize(path, iterpool),
3110251881Speter                               pool);
3111251881Speter
3112251881Speter  /* Check authorizations */
3113251881Speter  SVN_ERR(must_have_access(conn, iterpool, b, svn_authz_read,
3114251881Speter                           full_path, FALSE));
3115251881Speter
3116251881Speter  if (!SVN_IS_VALID_REVNUM(rev))
3117251881Speter    SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
3118251881Speter
3119251881Speter  SVN_ERR(log_command(b, conn, pool, "%s",
3120251881Speter                      svn_log__get_inherited_props(full_path, rev,
3121251881Speter                                                   iterpool)));
3122251881Speter
3123251881Speter  /* Fetch the properties and a stream for the contents. */
3124251881Speter  SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, iterpool));
3125251881Speter  SVN_CMD_ERR(get_props(NULL, &inherited_props, &ab, root, full_path, pool));
3126251881Speter
3127251881Speter  /* Send successful command response with revision and props. */
3128251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "w(!", "success"));
3129251881Speter
3130251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(?!"));
3131251881Speter
3132251881Speter  for (i = 0; i < inherited_props->nelts; i++)
3133251881Speter    {
3134251881Speter      svn_prop_inherited_item_t *iprop =
3135251881Speter        APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *);
3136251881Speter
3137251881Speter      svn_pool_clear(iterpool);
3138251881Speter      SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!",
3139251881Speter                                      iprop->path_or_url));
3140251881Speter      SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash));
3141251881Speter      SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!",
3142251881Speter                                      iprop->path_or_url));
3143251881Speter    }
3144251881Speter
3145251881Speter  SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))"));
3146251881Speter  svn_pool_destroy(iterpool);
3147251881Speter  return SVN_NO_ERROR;
3148251881Speter}
3149251881Speter
3150251881Speterstatic const svn_ra_svn_cmd_entry_t main_commands[] = {
3151251881Speter  { "reparent",        reparent },
3152251881Speter  { "get-latest-rev",  get_latest_rev },
3153251881Speter  { "get-dated-rev",   get_dated_rev },
3154251881Speter  { "change-rev-prop", change_rev_prop },
3155251881Speter  { "change-rev-prop2",change_rev_prop2 },
3156251881Speter  { "rev-proplist",    rev_proplist },
3157251881Speter  { "rev-prop",        rev_prop },
3158251881Speter  { "commit",          commit },
3159251881Speter  { "get-file",        get_file },
3160251881Speter  { "get-dir",         get_dir },
3161251881Speter  { "update",          update },
3162251881Speter  { "switch",          switch_cmd },
3163251881Speter  { "status",          status },
3164251881Speter  { "diff",            diff },
3165251881Speter  { "get-mergeinfo",   get_mergeinfo },
3166251881Speter  { "log",             log_cmd },
3167251881Speter  { "check-path",      check_path },
3168251881Speter  { "stat",            stat_cmd },
3169251881Speter  { "get-locations",   get_locations },
3170251881Speter  { "get-location-segments",   get_location_segments },
3171251881Speter  { "get-file-revs",   get_file_revs },
3172251881Speter  { "lock",            lock },
3173251881Speter  { "lock-many",       lock_many },
3174251881Speter  { "unlock",          unlock },
3175251881Speter  { "unlock-many",     unlock_many },
3176251881Speter  { "get-lock",        get_lock },
3177251881Speter  { "get-locks",       get_locks },
3178251881Speter  { "replay",          replay },
3179251881Speter  { "replay-range",    replay_range },
3180251881Speter  { "get-deleted-rev", get_deleted_rev },
3181251881Speter  { "get-iprops",      get_inherited_props },
3182251881Speter  { NULL }
3183251881Speter};
3184251881Speter
3185251881Speter/* Skip past the scheme part of a URL, including the tunnel specification
3186251881Speter * if present.  Return NULL if the scheme part is invalid for ra_svn. */
3187251881Speterstatic const char *skip_scheme_part(const char *url)
3188251881Speter{
3189251881Speter  if (strncmp(url, "svn", 3) != 0)
3190251881Speter    return NULL;
3191251881Speter  url += 3;
3192251881Speter  if (*url == '+')
3193251881Speter    url += strcspn(url, ":");
3194251881Speter  if (strncmp(url, "://", 3) != 0)
3195251881Speter    return NULL;
3196251881Speter  return url + 3;
3197251881Speter}
3198251881Speter
3199251881Speter/* Check that PATH is a valid repository path, meaning it doesn't contain any
3200251881Speter   '..' path segments.
3201251881Speter   NOTE: This is similar to svn_path_is_backpath_present, but that function
3202251881Speter   assumes the path separator is '/'.  This function also checks for
3203251881Speter   segments delimited by the local path separator. */
3204251881Speterstatic svn_boolean_t
3205251881Speterrepos_path_valid(const char *path)
3206251881Speter{
3207251881Speter  const char *s = path;
3208251881Speter
3209251881Speter  while (*s)
3210251881Speter    {
3211251881Speter      /* Scan for the end of the segment. */
3212251881Speter      while (*path && *path != '/' && *path != SVN_PATH_LOCAL_SEPARATOR)
3213251881Speter        ++path;
3214251881Speter
3215251881Speter      /* Check for '..'. */
3216251881Speter#ifdef WIN32
3217251881Speter      /* On Windows, don't allow sequences of more than one character
3218251881Speter         consisting of just dots and spaces.  Win32 functions treat
3219251881Speter         paths such as ".. " and "......." inconsistently.  Make sure
3220251881Speter         no one can escape out of the root. */
3221251881Speter      if (path - s >= 2 && strspn(s, ". ") == (size_t)(path - s))
3222251881Speter        return FALSE;
3223251881Speter#else  /* ! WIN32 */
3224251881Speter      if (path - s == 2 && s[0] == '.' && s[1] == '.')
3225251881Speter        return FALSE;
3226251881Speter#endif
3227251881Speter
3228251881Speter      /* Skip all separators. */
3229251881Speter      while (*path && (*path == '/' || *path == SVN_PATH_LOCAL_SEPARATOR))
3230251881Speter        ++path;
3231251881Speter      s = path;
3232251881Speter    }
3233251881Speter
3234251881Speter  return TRUE;
3235251881Speter}
3236251881Speter
3237251881Speter/* Look for the repository given by URL, using ROOT as the virtual
3238251881Speter * repository root.  If we find one, fill in the repos, fs, cfg,
3239251881Speter * repos_url, and fs_path fields of B.  Set B->repos's client
3240251881Speter * capabilities to CAPABILITIES, which must be at least as long-lived
3241251881Speter * as POOL, and whose elements are SVN_RA_CAPABILITY_*.
3242251881Speter */
3243251881Speterstatic svn_error_t *find_repos(const char *url, const char *root,
3244251881Speter                               server_baton_t *b,
3245251881Speter                               svn_ra_svn_conn_t *conn,
3246251881Speter                               const apr_array_header_t *capabilities,
3247251881Speter                               apr_pool_t *pool)
3248251881Speter{
3249251881Speter  const char *path, *full_path, *repos_root, *fs_path, *hooks_env;
3250251881Speter  svn_stringbuf_t *url_buf;
3251251881Speter
3252251881Speter  /* Skip past the scheme and authority part. */
3253251881Speter  path = skip_scheme_part(url);
3254251881Speter  if (path == NULL)
3255251881Speter    return svn_error_createf(SVN_ERR_BAD_URL, NULL,
3256251881Speter                             "Non-svn URL passed to svn server: '%s'", url);
3257251881Speter
3258251881Speter  if (! b->vhost)
3259251881Speter    {
3260251881Speter      path = strchr(path, '/');
3261251881Speter      if (path == NULL)
3262251881Speter        path = "";
3263251881Speter    }
3264251881Speter  path = svn_relpath_canonicalize(path, pool);
3265251881Speter  path = svn_path_uri_decode(path, pool);
3266251881Speter
3267251881Speter  /* Ensure that it isn't possible to escape the root by disallowing
3268251881Speter     '..' segments. */
3269251881Speter  if (!repos_path_valid(path))
3270251881Speter    return svn_error_create(SVN_ERR_BAD_FILENAME, NULL,
3271251881Speter                            "Couldn't determine repository path");
3272251881Speter
3273251881Speter  /* Join the server-configured root with the client path. */
3274251881Speter  full_path = svn_dirent_join(svn_dirent_canonicalize(root, pool),
3275251881Speter                              path, pool);
3276251881Speter
3277251881Speter  /* Search for a repository in the full path. */
3278251881Speter  repos_root = svn_repos_find_root_path(full_path, pool);
3279251881Speter  if (!repos_root)
3280251881Speter    return svn_error_createf(SVN_ERR_RA_SVN_REPOS_NOT_FOUND, NULL,
3281251881Speter                             "No repository found in '%s'", url);
3282251881Speter
3283251881Speter  /* Open the repository and fill in b with the resulting information. */
3284251881Speter  SVN_ERR(svn_repos_open2(&b->repos, repos_root, b->fs_config, pool));
3285251881Speter  SVN_ERR(svn_repos_remember_client_capabilities(b->repos, capabilities));
3286251881Speter  b->fs = svn_repos_fs(b->repos);
3287251881Speter  fs_path = full_path + strlen(repos_root);
3288251881Speter  b->fs_path = svn_stringbuf_create(*fs_path ? fs_path : "/", pool);
3289251881Speter  url_buf = svn_stringbuf_create(url, pool);
3290251881Speter  svn_path_remove_components(url_buf,
3291251881Speter                             svn_path_component_count(b->fs_path->data));
3292251881Speter  b->repos_url = url_buf->data;
3293251881Speter  b->authz_repos_name = svn_dirent_is_child(root, repos_root, pool);
3294251881Speter  if (b->authz_repos_name == NULL)
3295251881Speter    b->repos_name = svn_dirent_basename(repos_root, pool);
3296251881Speter  else
3297251881Speter    b->repos_name = b->authz_repos_name;
3298251881Speter  b->repos_name = svn_path_uri_encode(b->repos_name, pool);
3299251881Speter
3300251881Speter  /* If the svnserve configuration has not been loaded then load it from the
3301251881Speter   * repository. */
3302251881Speter  if (NULL == b->cfg)
3303251881Speter    {
3304251881Speter      b->base = svn_repos_conf_dir(b->repos, pool);
3305251881Speter
3306251881Speter      SVN_ERR(svn_config_read3(&b->cfg, svn_repos_svnserve_conf(b->repos, pool),
3307251881Speter                               FALSE, /* must_exist */
3308251881Speter                               FALSE, /* section_names_case_sensitive */
3309251881Speter                               FALSE, /* option_names_case_sensitive */
3310251881Speter                               pool));
3311251881Speter      SVN_ERR(load_pwdb_config(b, conn, pool));
3312251881Speter      SVN_ERR(load_authz_config(b, conn, repos_root, pool));
3313251881Speter    }
3314251881Speter  /* svnserve.conf has been loaded via the --config-file option so need
3315251881Speter   * to load pwdb and authz. */
3316251881Speter  else
3317251881Speter    {
3318251881Speter      SVN_ERR(load_pwdb_config(b, conn, pool));
3319251881Speter      SVN_ERR(load_authz_config(b, conn, repos_root, pool));
3320251881Speter    }
3321251881Speter
3322251881Speter#ifdef SVN_HAVE_SASL
3323251881Speter  /* Should we use Cyrus SASL? */
3324251881Speter  SVN_ERR(svn_config_get_bool(b->cfg, &b->use_sasl, SVN_CONFIG_SECTION_SASL,
3325251881Speter                              SVN_CONFIG_OPTION_USE_SASL, FALSE));
3326251881Speter#endif
3327251881Speter
3328251881Speter  /* Use the repository UUID as the default realm. */
3329251881Speter  SVN_ERR(svn_fs_get_uuid(b->fs, &b->realm, pool));
3330251881Speter  svn_config_get(b->cfg, &b->realm, SVN_CONFIG_SECTION_GENERAL,
3331251881Speter                 SVN_CONFIG_OPTION_REALM, b->realm);
3332251881Speter
3333251881Speter  /* Make sure it's possible for the client to authenticate.  Note
3334251881Speter     that this doesn't take into account any authz configuration read
3335251881Speter     above, because we can't know about access it grants until paths
3336251881Speter     are given by the client. */
3337251881Speter  if (get_access(b, UNAUTHENTICATED) == NO_ACCESS
3338251881Speter      && (get_access(b, AUTHENTICATED) == NO_ACCESS
3339251881Speter          || (!b->tunnel_user && !b->pwdb && !b->use_sasl)))
3340251881Speter    return error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
3341251881Speter                                 "No access allowed to this repository",
3342251881Speter                                 b, conn, pool);
3343251881Speter
3344251881Speter  /* Configure hook script environment variables. */
3345251881Speter  svn_config_get(b->cfg, &hooks_env, SVN_CONFIG_SECTION_GENERAL,
3346251881Speter                 SVN_CONFIG_OPTION_HOOKS_ENV, NULL);
3347251881Speter  if (hooks_env)
3348251881Speter    hooks_env = svn_dirent_internal_style(hooks_env, pool);
3349251881Speter  SVN_ERR(svn_repos_hooks_setenv(b->repos, hooks_env, pool));
3350251881Speter
3351251881Speter  return SVN_NO_ERROR;
3352251881Speter}
3353251881Speter
3354251881Speter/* Compute the authentication name EXTERNAL should be able to get, if any. */
3355251881Speterstatic const char *get_tunnel_user(serve_params_t *params, apr_pool_t *pool)
3356251881Speter{
3357251881Speter  /* Only offer EXTERNAL for connections tunneled over a login agent. */
3358251881Speter  if (!params->tunnel)
3359251881Speter    return NULL;
3360251881Speter
3361251881Speter  /* If a tunnel user was provided on the command line, use that. */
3362251881Speter  if (params->tunnel_user)
3363251881Speter    return params->tunnel_user;
3364251881Speter
3365251881Speter  return svn_user_get_name(pool);
3366251881Speter}
3367251881Speter
3368251881Speterstatic void
3369251881Speterfs_warning_func(void *baton, svn_error_t *err)
3370251881Speter{
3371251881Speter  fs_warning_baton_t *b = baton;
3372251881Speter  log_server_error(err, b->server, b->conn, b->pool);
3373251881Speter  /* TODO: Keep log_pool in the server baton, cleared after every log? */
3374251881Speter  svn_pool_clear(b->pool);
3375251881Speter}
3376251881Speter
3377251881Speter/* Return the normalized repository-relative path for the given PATH
3378251881Speter * (may be a URL, full path or relative path) and fs contained in the
3379251881Speter * server baton BATON. Allocate the result in POOL.
3380251881Speter */
3381251881Speterstatic const char *
3382251881Speterget_normalized_repo_rel_path(void *baton,
3383251881Speter                             const char *path,
3384251881Speter                             apr_pool_t *pool)
3385251881Speter{
3386251881Speter  server_baton_t *sb = baton;
3387251881Speter
3388251881Speter  if (svn_path_is_url(path))
3389251881Speter    {
3390251881Speter      /* This is a copyfrom URL. */
3391251881Speter      path = svn_uri_skip_ancestor(sb->repos_url, path, pool);
3392251881Speter      path = svn_fspath__canonicalize(path, pool);
3393251881Speter    }
3394251881Speter  else
3395251881Speter    {
3396251881Speter      /* This is a base-relative path. */
3397251881Speter      if ((path)[0] != '/')
3398251881Speter        /* Get an absolute path for use in the FS. */
3399251881Speter        path = svn_fspath__join(sb->fs_path->data, path, pool);
3400251881Speter    }
3401251881Speter
3402251881Speter  return path;
3403251881Speter}
3404251881Speter
3405251881Speter/* Get the revision root for REVISION in fs given by server baton BATON
3406251881Speter * and return it in *FS_ROOT. Use HEAD if REVISION is SVN_INVALID_REVNUM.
3407251881Speter * Use POOL for allocations.
3408251881Speter */
3409251881Speterstatic svn_error_t *
3410251881Speterget_revision_root(svn_fs_root_t **fs_root,
3411251881Speter                  void *baton,
3412251881Speter                  svn_revnum_t revision,
3413251881Speter                  apr_pool_t *pool)
3414251881Speter{
3415251881Speter  server_baton_t *sb = baton;
3416251881Speter
3417251881Speter  if (!SVN_IS_VALID_REVNUM(revision))
3418251881Speter    SVN_ERR(svn_fs_youngest_rev(&revision, sb->fs, pool));
3419251881Speter
3420251881Speter  SVN_ERR(svn_fs_revision_root(fs_root, sb->fs, revision, pool));
3421251881Speter
3422251881Speter  return SVN_NO_ERROR;
3423251881Speter}
3424251881Speter
3425251881Speterstatic svn_error_t *
3426251881Speterfetch_props_func(apr_hash_t **props,
3427251881Speter                 void *baton,
3428251881Speter                 const char *path,
3429251881Speter                 svn_revnum_t base_revision,
3430251881Speter                 apr_pool_t *result_pool,
3431251881Speter                 apr_pool_t *scratch_pool)
3432251881Speter{
3433251881Speter  svn_fs_root_t *fs_root;
3434251881Speter  svn_error_t *err;
3435251881Speter
3436251881Speter  path = get_normalized_repo_rel_path(baton, path, scratch_pool);
3437251881Speter  SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool));
3438251881Speter
3439251881Speter  err = svn_fs_node_proplist(props, fs_root, path, result_pool);
3440251881Speter  if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
3441251881Speter    {
3442251881Speter      svn_error_clear(err);
3443251881Speter      *props = apr_hash_make(result_pool);
3444251881Speter      return SVN_NO_ERROR;
3445251881Speter    }
3446251881Speter  else if (err)
3447251881Speter    return svn_error_trace(err);
3448251881Speter
3449251881Speter  return SVN_NO_ERROR;
3450251881Speter}
3451251881Speter
3452251881Speterstatic svn_error_t *
3453251881Speterfetch_kind_func(svn_node_kind_t *kind,
3454251881Speter                void *baton,
3455251881Speter                const char *path,
3456251881Speter                svn_revnum_t base_revision,
3457251881Speter                apr_pool_t *scratch_pool)
3458251881Speter{
3459251881Speter  svn_fs_root_t *fs_root;
3460251881Speter
3461251881Speter  path = get_normalized_repo_rel_path(baton, path, scratch_pool);
3462251881Speter  SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool));
3463251881Speter
3464251881Speter  SVN_ERR(svn_fs_check_path(kind, fs_root, path, scratch_pool));
3465251881Speter
3466251881Speter  return SVN_NO_ERROR;
3467251881Speter}
3468251881Speter
3469251881Speterstatic svn_error_t *
3470251881Speterfetch_base_func(const char **filename,
3471251881Speter                void *baton,
3472251881Speter                const char *path,
3473251881Speter                svn_revnum_t base_revision,
3474251881Speter                apr_pool_t *result_pool,
3475251881Speter                apr_pool_t *scratch_pool)
3476251881Speter{
3477251881Speter  svn_stream_t *contents;
3478251881Speter  svn_stream_t *file_stream;
3479251881Speter  const char *tmp_filename;
3480251881Speter  svn_fs_root_t *fs_root;
3481251881Speter  svn_error_t *err;
3482251881Speter
3483251881Speter  path = get_normalized_repo_rel_path(baton, path, scratch_pool);
3484251881Speter  SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool));
3485251881Speter
3486251881Speter  err = svn_fs_file_contents(&contents, fs_root, path, scratch_pool);
3487251881Speter  if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
3488251881Speter    {
3489251881Speter      svn_error_clear(err);
3490251881Speter      *filename = NULL;
3491251881Speter      return SVN_NO_ERROR;
3492251881Speter    }
3493251881Speter  else if (err)
3494251881Speter    return svn_error_trace(err);
3495251881Speter  SVN_ERR(svn_stream_open_unique(&file_stream, &tmp_filename, NULL,
3496251881Speter                                 svn_io_file_del_on_pool_cleanup,
3497251881Speter                                 scratch_pool, scratch_pool));
3498251881Speter  SVN_ERR(svn_stream_copy3(contents, file_stream, NULL, NULL, scratch_pool));
3499251881Speter
3500251881Speter  *filename = apr_pstrdup(result_pool, tmp_filename);
3501251881Speter
3502251881Speter  return SVN_NO_ERROR;
3503251881Speter}
3504251881Speter
3505251881Spetersvn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params,
3506251881Speter                   apr_pool_t *pool)
3507251881Speter{
3508251881Speter  svn_error_t *err, *io_err;
3509251881Speter  apr_uint64_t ver;
3510251881Speter  const char *uuid, *client_url, *ra_client_string, *client_string;
3511251881Speter  apr_array_header_t *caplist, *cap_words;
3512251881Speter  server_baton_t b;
3513251881Speter  fs_warning_baton_t warn_baton;
3514251881Speter  svn_stringbuf_t *cap_log = svn_stringbuf_create_empty(pool);
3515251881Speter
3516251881Speter  b.tunnel = params->tunnel;
3517251881Speter  b.tunnel_user = get_tunnel_user(params, pool);
3518251881Speter  b.read_only = params->read_only;
3519251881Speter  b.user = NULL;
3520251881Speter  b.username_case = params->username_case;
3521251881Speter  b.authz_user = NULL;
3522251881Speter  b.base = params->base;
3523251881Speter  b.cfg = params->cfg;
3524251881Speter  b.pwdb = NULL;
3525251881Speter  b.authzdb = NULL;
3526251881Speter  b.realm = NULL;
3527251881Speter  b.log_file = params->log_file;
3528251881Speter  b.pool = pool;
3529251881Speter  b.use_sasl = FALSE;
3530251881Speter  b.vhost = params->vhost;
3531251881Speter
3532251881Speter  /* construct FS configuration parameters */
3533251881Speter  b.fs_config = apr_hash_make(pool);
3534251881Speter  svn_hash_sets(b.fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS,
3535251881Speter                params->cache_txdeltas ? "1" :"0");
3536251881Speter  svn_hash_sets(b.fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS,
3537251881Speter                params->cache_fulltexts ? "1" :"0");
3538251881Speter  svn_hash_sets(b.fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
3539251881Speter                params->cache_revprops ? "1" :"0");
3540251881Speter
3541251881Speter  /* Send greeting.  We don't support version 1 any more, so we can
3542251881Speter   * send an empty mechlist. */
3543251881Speter  if (params->compression_level > 0)
3544251881Speter    SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "nn()(wwwwwwwwwww)",
3545251881Speter                                           (apr_uint64_t) 2, (apr_uint64_t) 2,
3546251881Speter                                           SVN_RA_SVN_CAP_EDIT_PIPELINE,
3547251881Speter                                           SVN_RA_SVN_CAP_SVNDIFF1,
3548251881Speter                                           SVN_RA_SVN_CAP_ABSENT_ENTRIES,
3549251881Speter                                           SVN_RA_SVN_CAP_COMMIT_REVPROPS,
3550251881Speter                                           SVN_RA_SVN_CAP_DEPTH,
3551251881Speter                                           SVN_RA_SVN_CAP_LOG_REVPROPS,
3552251881Speter                                           SVN_RA_SVN_CAP_ATOMIC_REVPROPS,
3553251881Speter                                           SVN_RA_SVN_CAP_PARTIAL_REPLAY,
3554251881Speter                                           SVN_RA_SVN_CAP_INHERITED_PROPS,
3555251881Speter                                           SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS,
3556251881Speter                                           SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE
3557251881Speter                                           ));
3558251881Speter  else
3559251881Speter    SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "nn()(wwwwwwwwww)",
3560251881Speter                                           (apr_uint64_t) 2, (apr_uint64_t) 2,
3561251881Speter                                           SVN_RA_SVN_CAP_EDIT_PIPELINE,
3562251881Speter                                           SVN_RA_SVN_CAP_ABSENT_ENTRIES,
3563251881Speter                                           SVN_RA_SVN_CAP_COMMIT_REVPROPS,
3564251881Speter                                           SVN_RA_SVN_CAP_DEPTH,
3565251881Speter                                           SVN_RA_SVN_CAP_LOG_REVPROPS,
3566251881Speter                                           SVN_RA_SVN_CAP_ATOMIC_REVPROPS,
3567251881Speter                                           SVN_RA_SVN_CAP_PARTIAL_REPLAY,
3568251881Speter                                           SVN_RA_SVN_CAP_INHERITED_PROPS,
3569251881Speter                                           SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS,
3570251881Speter                                           SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE
3571251881Speter                                           ));
3572251881Speter
3573251881Speter  /* Read client response, which we assume to be in version 2 format:
3574251881Speter   * version, capability list, and client URL; then we do an auth
3575251881Speter   * request. */
3576251881Speter  SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "nlc?c(?c)",
3577251881Speter                                 &ver, &caplist, &client_url,
3578251881Speter                                 &ra_client_string,
3579251881Speter                                 &client_string));
3580251881Speter  if (ver != 2)
3581251881Speter    return SVN_NO_ERROR;
3582251881Speter
3583251881Speter  client_url = svn_uri_canonicalize(client_url, pool);
3584251881Speter  SVN_ERR(svn_ra_svn_set_capabilities(conn, caplist));
3585251881Speter
3586251881Speter  /* All released versions of Subversion support edit-pipeline,
3587251881Speter   * so we do not accept connections from clients that do not. */
3588251881Speter  if (! svn_ra_svn_has_capability(conn, SVN_RA_SVN_CAP_EDIT_PIPELINE))
3589251881Speter    return SVN_NO_ERROR;
3590251881Speter
3591251881Speter  /* find_repos needs the capabilities as a list of words (eventually
3592251881Speter     they get handed to the start-commit hook).  While we could add a
3593251881Speter     new interface to re-retrieve them from conn and convert the
3594251881Speter     result to a list, it's simpler to just convert caplist by hand
3595251881Speter     here, since we already have it and turning 'svn_ra_svn_item_t's
3596251881Speter     into 'const char *'s is pretty easy.
3597251881Speter
3598251881Speter     We only record capabilities we care about.  The client may report
3599251881Speter     more (because it doesn't know what the server cares about). */
3600251881Speter  {
3601251881Speter    int i;
3602251881Speter    svn_ra_svn_item_t *item;
3603251881Speter
3604251881Speter    cap_words = apr_array_make(pool, 1, sizeof(const char *));
3605251881Speter    for (i = 0; i < caplist->nelts; i++)
3606251881Speter      {
3607251881Speter        item = &APR_ARRAY_IDX(caplist, i, svn_ra_svn_item_t);
3608251881Speter        /* ra_svn_set_capabilities() already type-checked for us */
3609251881Speter        if (strcmp(item->u.word, SVN_RA_SVN_CAP_MERGEINFO) == 0)
3610251881Speter          {
3611251881Speter            APR_ARRAY_PUSH(cap_words, const char *)
3612251881Speter              = SVN_RA_CAPABILITY_MERGEINFO;
3613251881Speter          }
3614251881Speter        /* Save for operational log. */
3615251881Speter        if (cap_log->len > 0)
3616251881Speter          svn_stringbuf_appendcstr(cap_log, " ");
3617251881Speter        svn_stringbuf_appendcstr(cap_log, item->u.word);
3618251881Speter      }
3619251881Speter  }
3620251881Speter
3621251881Speter  err = find_repos(client_url, params->root, &b, conn, cap_words, pool);
3622251881Speter  if (!err)
3623251881Speter    {
3624251881Speter      SVN_ERR(auth_request(conn, pool, &b, READ_ACCESS, FALSE));
3625251881Speter      if (current_access(&b) == NO_ACCESS)
3626251881Speter        err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
3627251881Speter                                   "Not authorized for access",
3628251881Speter                                   &b, conn, pool);
3629251881Speter    }
3630251881Speter  if (err)
3631251881Speter    {
3632251881Speter      log_error(err, b.log_file, svn_ra_svn_conn_remote_host(conn),
3633251881Speter                b.user, NULL, pool);
3634251881Speter      io_err = svn_ra_svn__write_cmd_failure(conn, pool, err);
3635251881Speter      svn_error_clear(err);
3636251881Speter      SVN_ERR(io_err);
3637251881Speter      return svn_ra_svn__flush(conn, pool);
3638251881Speter    }
3639251881Speter
3640251881Speter  /* Log the open. */
3641251881Speter  if (ra_client_string == NULL || ra_client_string[0] == '\0')
3642251881Speter    ra_client_string = "-";
3643251881Speter  else
3644251881Speter    ra_client_string = svn_path_uri_encode(ra_client_string, pool);
3645251881Speter  if (client_string == NULL || client_string[0] == '\0')
3646251881Speter    client_string = "-";
3647251881Speter  else
3648251881Speter    client_string = svn_path_uri_encode(client_string, pool);
3649251881Speter  SVN_ERR(log_command(&b, conn, pool,
3650251881Speter                      "open %" APR_UINT64_T_FMT " cap=(%s) %s %s %s",
3651251881Speter                      ver, cap_log->data,
3652251881Speter                      svn_path_uri_encode(b.fs_path->data, pool),
3653251881Speter                      ra_client_string, client_string));
3654251881Speter
3655251881Speter  warn_baton.server = &b;
3656251881Speter  warn_baton.conn = conn;
3657251881Speter  warn_baton.pool = svn_pool_create(pool);
3658251881Speter  svn_fs_set_warning_func(b.fs, fs_warning_func, &warn_baton);
3659251881Speter
3660251881Speter  SVN_ERR(svn_fs_get_uuid(b.fs, &uuid, pool));
3661251881Speter
3662251881Speter  /* We can't claim mergeinfo capability until we know whether the
3663251881Speter     repository supports mergeinfo (i.e., is not a 1.4 repository),
3664251881Speter     but we don't get the repository url from the client until after
3665251881Speter     we've already sent the initial list of server capabilities.  So
3666251881Speter     we list repository capabilities here, in our first response after
3667251881Speter     the client has sent the url. */
3668251881Speter  {
3669251881Speter    svn_boolean_t supports_mergeinfo;
3670251881Speter    SVN_ERR(svn_repos_has_capability(b.repos, &supports_mergeinfo,
3671251881Speter                                     SVN_REPOS_CAPABILITY_MERGEINFO, pool));
3672251881Speter
3673251881Speter    SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(cc(!",
3674251881Speter                                    "success", uuid, b.repos_url));
3675251881Speter    if (supports_mergeinfo)
3676251881Speter      SVN_ERR(svn_ra_svn__write_word(conn, pool, SVN_RA_SVN_CAP_MERGEINFO));
3677251881Speter    SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));
3678251881Speter  }
3679251881Speter
3680251881Speter  /* Set up editor shims. */
3681251881Speter  {
3682251881Speter    svn_delta_shim_callbacks_t *callbacks =
3683251881Speter                                svn_delta_shim_callbacks_default(pool);
3684251881Speter
3685251881Speter    callbacks->fetch_base_func = fetch_base_func;
3686251881Speter    callbacks->fetch_props_func = fetch_props_func;
3687251881Speter    callbacks->fetch_kind_func = fetch_kind_func;
3688251881Speter    callbacks->fetch_baton = &b;
3689251881Speter
3690251881Speter    SVN_ERR(svn_ra_svn__set_shim_callbacks(conn, callbacks));
3691251881Speter  }
3692251881Speter
3693251881Speter  return svn_ra_svn__handle_commands2(conn, pool, main_commands, &b, FALSE);
3694251881Speter}
3695