Author: stefan2
Date: Sat Nov 25 17:12:38 2017
New Revision: 1816330

URL: http://svn.apache.org/viewvc?rev=1816330&view=rev
Log:
Add the server-side implementation for the 'svn/list' report.

* subversion/mod_dav_svn/dav_svn.h
  (dav_svn__reports_list): Add new report type.
  (dav_svn__list_report):  Declare new vtable function implementaton.

* subversion/mod_dav_svn/reports/list.c
  (): New file implementing the server-side handling of the new report.

* subversion/mod_dav_svn/version.c
  (get_vsn_options): Advertise the new reporting capability.
  (deliver_report): Dispatch requests to the new report implementation.

Added:
    subversion/trunk/subversion/mod_dav_svn/reports/list.c   (with props)
Modified:
    subversion/trunk/subversion/mod_dav_svn/dav_svn.h
    subversion/trunk/subversion/mod_dav_svn/version.c

Modified: subversion/trunk/subversion/mod_dav_svn/dav_svn.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/mod_dav_svn/dav_svn.h?rev=1816330&r1=1816329&r2=1816330&view=diff
==============================================================================
--- subversion/trunk/subversion/mod_dav_svn/dav_svn.h (original)
+++ subversion/trunk/subversion/mod_dav_svn/dav_svn.h Sat Nov 25 17:12:38 2017
@@ -705,6 +705,7 @@ static const dav_report_elem dav_svn__re
   { SVN_XML_NAMESPACE, "get-deleted-rev-report" },
   { SVN_XML_NAMESPACE, SVN_DAV__MERGEINFO_REPORT },
   { SVN_XML_NAMESPACE, SVN_DAV__INHERITED_PROPS_REPORT },
+  { SVN_XML_NAMESPACE, "list-report" },
   { NULL, NULL },
 };
 
@@ -757,6 +758,11 @@ dav_svn__get_inherited_props_report(cons
                                     const apr_xml_doc *doc,
                                     dav_svn__output *output);
 
+dav_error *
+dav_svn__list_report(const dav_resource *resource,
+                     const apr_xml_doc *doc,
+                     dav_svn__output *output);
+
 /*** posts/ ***/
 
 /* The various POST handlers, defined in posts/, and used by repos.c.  */

Added: subversion/trunk/subversion/mod_dav_svn/reports/list.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/mod_dav_svn/reports/list.c?rev=1816330&view=auto
==============================================================================
--- subversion/trunk/subversion/mod_dav_svn/reports/list.c (added)
+++ subversion/trunk/subversion/mod_dav_svn/reports/list.c Sat Nov 25 17:12:38 
2017
@@ -0,0 +1,338 @@
+/*
+ * list.c: mod_dav_svn REPORT handler for recursive directory listings
+ *
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+#include <apr_pools.h>
+#include <apr_strings.h>
+#include <apr_xml.h>
+
+#include <mod_dav.h>
+
+#include "svn_repos.h"
+#include "svn_ctype.h"
+#include "svn_string.h"
+#include "svn_types.h"
+#include "svn_base64.h"
+#include "svn_xml.h"
+#include "svn_path.h"
+#include "svn_dav.h"
+#include "svn_pools.h"
+#include "svn_props.h"
+#include "svn_time.h"
+
+#include "private/svn_log.h"
+#include "private/svn_fspath.h"
+
+#include "../dav_svn.h"
+
+/* Baton type to be used with list_receiver. */
+typedef struct list_receiver_baton_t
+{
+  /* this buffers the output for a bit and is automatically flushed,
+     at appropriate times, by the Apache filter system. */
+  apr_bucket_brigade *bb;
+
+  /* where to deliver the output */
+  dav_svn__output *output;
+
+  /* Whether we've written the <S:log-report> header.  Allows for lazy
+     writes to support mod_dav-based error handling. */
+  svn_boolean_t needs_header;
+
+  /* Are we talking to a SVN client? */
+  svn_boolean_t is_svn_client;
+
+  /* Helper variables to force early bucket brigade flushes */
+  int result_count;
+  int next_forced_flush;
+
+  /* Send the field selected by these flags. */
+  apr_uint32_t dirent_fields;
+} list_receiver_baton_t;
+
+
+/* If LRB->needs_header is true, send the "<S:list-report>" start
+   element and set LRB->needs_header to zero.  Else do nothing.
+   This is basically duplicated in file_revs.c.  Consider factoring if
+   duplicating again. */
+static svn_error_t *
+maybe_send_header(list_receiver_baton_t *lrb)
+{
+  if (lrb->needs_header)
+    {
+      SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output,
+                                    DAV_XML_HEADER DEBUG_CR
+                                    "<S:list-report xmlns:S=\""
+                                    SVN_XML_NAMESPACE "\" "
+                                    "xmlns:D=\"DAV:\">" DEBUG_CR));
+      lrb->needs_header = FALSE;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+
+/* Implements svn_repos_dirent_receiver_t, sending DIRENT and PATH to the
+ * client.  BATON must be a list_receiver_baton_t. */
+static svn_error_t *
+list_receiver(const char *path,
+              svn_dirent_t *dirent,
+              void *baton,
+              apr_pool_t *pool)
+{
+  list_receiver_baton_t *b = baton;
+  const char *kind = (b->dirent_fields & SVN_DIRENT_KIND)
+                   ? svn_node_kind_to_word(dirent->kind)
+                   : "unknown";
+  const char *attr_size = "";
+  const char *attr_has_props = "";
+  const char *attr_created_rev = "";
+  const char *attr_date = "";
+  const char *tag_author = "";
+
+  if (b->dirent_fields & SVN_DIRENT_SIZE)
+    attr_size = apr_psprintf(pool, " size=\"%" SVN_FILESIZE_T_FMT "\"",
+                             dirent->size);
+
+  if (b->dirent_fields & SVN_DIRENT_HAS_PROPS)
+    attr_has_props = dirent->has_props
+                   ? " has-props=\"true\""
+                   : " has-props=\"false\"";
+
+  if (b->dirent_fields & SVN_DIRENT_CREATED_REV)
+    attr_created_rev = apr_psprintf(pool, " created-rev=\"%ld\"",
+                                    dirent->created_rev);
+
+  if (b->dirent_fields & SVN_DIRENT_TIME)
+    {
+      const char *ctime = svn_time_to_cstring(dirent->time, pool);
+      attr_date = apr_psprintf(pool, " date=\"%s\"",
+                               apr_xml_quote_string(pool, ctime, 0));
+    }
+
+  if ((b->dirent_fields & SVN_DIRENT_LAST_AUTHOR) && dirent->last_author)
+    {
+      const char *author = dav_svn__fuzzy_escape_author(dirent->last_author,
+                                                        b->is_svn_client,
+                                                        pool, pool);
+      tag_author = apr_psprintf(pool,
+                                "<D:creator-displayname>%s"
+                                "</D:creator-displayname>",
+                                apr_xml_quote_string(pool, author, 1));
+    }
+
+  SVN_ERR(maybe_send_header(b));
+ 
+  /* If we need to close the element, then send the attributes
+     that apply to all changed items and then close the element. */
+  SVN_ERR(dav_svn__brigade_printf(b->bb, b->output,
+                                 "<S:item"
+                                 " node-kind=\"%s\""
+                                 "%s"
+                                 "%s"
+                                 "%s"
+                                 "%s>%s%s</S:item>" DEBUG_CR,
+                                 kind,
+                                 attr_size,
+                                 attr_has_props,
+                                 attr_created_rev,
+                                 attr_date,
+                                 apr_xml_quote_string(pool, path, 0),
+                                 tag_author));
+
+  /* In general APR will flush the brigade every 8000 bytes through the filter
+     stack, but log items may not be generated that fast, especially in
+     combination with authz and busy servers. We now explictly flush after
+     direntry 4, 16, 64 and 256 to produce a few results fast.
+
+     This introduces 4 full flushes of our brigade and the installed output
+     filters at growing intervals and then falls back to the standard
+     buffering of 8000 bytes + whatever buffers are added in output filters. */
+  b->result_count++;
+  if (b->result_count == b->next_forced_flush)
+    {
+      apr_bucket *bkt;
+
+      /* Compared to using ap_filter_flush(), which we use in other place
+         this adds a flush frame before flushing the brigade, to make output
+         filters perform a flush as well */
+
+      /* No brigade empty check. We want output filters to flush anyway */
+      bkt = apr_bucket_flush_create(
+                dav_svn__output_get_bucket_alloc(b->output));
+      APR_BRIGADE_INSERT_TAIL(b->bb, bkt);
+      SVN_ERR(dav_svn__output_pass_brigade(b->output, b->bb));
+
+      if (b->result_count < 256)
+        b->next_forced_flush = b->next_forced_flush * 4;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+dav_error *
+dav_svn__list_report(const dav_resource *resource,
+                     const apr_xml_doc *doc,
+                     dav_svn__output *output)
+{
+  svn_error_t *serr;
+  dav_error *derr = NULL;
+  apr_xml_elem *child;
+  list_receiver_baton_t lrb = { 0 };
+  dav_svn__authz_read_baton arb;
+  const dav_svn_repos *repos = resource->info->repos;
+  int ns;
+  const char *full_path;
+  svn_boolean_t path_info_only;
+  svn_fs_root_t *root;
+  svn_depth_t depth = svn_depth_unknown;
+
+  /* These get determined from the request document. */
+  svn_revnum_t rev = SVN_INVALID_REVNUM;     /* defaults to HEAD */
+  apr_array_header_t *patterns = NULL;
+
+  /* Sanity check. */
+  if (!resource->info->repos_path)
+    return dav_svn__new_error(resource->pool, HTTP_BAD_REQUEST, 0, 0,
+                              "The request does not specify a repository 
path");
+  ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE);
+  if (ns == -1)
+    {
+      return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0, 0,
+                                    "The request does not contain the 'svn:' "
+                                    "namespace, so it is not going to have "
+                                    "certain required elements");
+    }
+
+  for (child = doc->root->first_child; child != NULL; child = child->next)
+    {
+      /* if this element isn't one of ours, then skip it */
+      if (child->ns != ns)
+        continue;
+
+      else if (strcmp(child->name, "path") == 0)
+        {
+          const char *rel_path = dav_xml_get_cdata(child, resource->pool, 0);
+          if ((derr = dav_svn__test_canonical(rel_path, resource->pool)))
+            return derr;
+
+          /* Force REL_PATH to be a relative path, not an fspath. */
+          rel_path = svn_relpath_canonicalize(rel_path, resource->pool);
+
+          /* Append the REL_PATH to the base FS path to get an
+             absolute repository path. */
+          full_path = svn_fspath__join(resource->info->repos_path, rel_path,
+                                       resource->pool);
+        }
+      else if (strcmp(child->name, "revision") == 0)
+        rev = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1));
+      else if (strcmp(child->name, "depth") == 0)
+        depth = svn_depth_from_word(dav_xml_get_cdata(child, resource->pool, 
1));
+      else if (strcmp(child->name, "no-patterns") == 0)
+        {
+          /* specified but empty pattern list */
+          patterns = apr_array_make(resource->pool, 0, sizeof(const char *));
+        }
+      else if (strcmp(child->name, "pattern") == 0)
+        {
+          const char *name = dav_xml_get_cdata(child, resource->pool, 0);
+          if (!patterns)
+            patterns = apr_array_make(resource->pool, 1, sizeof(const char *));
+          APR_ARRAY_PUSH(patterns, const char *) = name;
+        }
+      else if (strcmp(child->name, "prop") == 0)
+        {
+          const char *name = dav_xml_get_cdata(child, resource->pool, 0);
+          if (strcmp(name, "DAV:resourcetype") == 0)
+            lrb.dirent_fields |= SVN_DIRENT_KIND;
+          else if (strcmp(name, "DAV:getcontentlength") == 0)
+            lrb.dirent_fields |= SVN_DIRENT_SIZE;
+          else if (strcmp(name, SVN_DAV_PROP_NS_DAV "deadprop-count") == 0)
+            lrb.dirent_fields |= SVN_DIRENT_HAS_PROPS;
+          else if (strcmp(name, "DAV:" SVN_DAV__VERSION_NAME) == 0)
+            lrb.dirent_fields |= SVN_DIRENT_CREATED_REV;
+          else if (strcmp(name, "DAV:" SVN_DAV__CREATIONDATE) == 0)
+            lrb.dirent_fields |= SVN_DIRENT_TIME;
+          else if (strcmp(name, "DAV:creator-displayname") == 0)
+            lrb.dirent_fields |= SVN_DIRENT_LAST_AUTHOR;
+          else if (strcmp(name, "DAV:allprop") == 0)
+            lrb.dirent_fields |= SVN_DIRENT_ALL;
+        }
+      /* else unknown element; skip it */
+    }
+
+  /* Build authz read baton */
+  arb.r = resource->info->r;
+  arb.repos = resource->info->repos;
+
+  /* Build log receiver baton */
+  lrb.bb = apr_brigade_create(resource->pool,  /* not the subpool! */
+                              dav_svn__output_get_bucket_alloc(output));
+  lrb.output = output;
+  lrb.needs_header = TRUE;
+  lrb.next_forced_flush = 4;
+  lrb.is_svn_client = resource->info->repos->is_svn_client;
+
+  /* Fetch the root of the appropriate revision. */
+  serr = svn_fs_revision_root(&root, repos->fs, rev, resource->pool);
+  if (!serr)
+    {
+      /* Fetch the directory entries if requested and send them immediately. */
+      path_info_only = (lrb.dirent_fields & ~SVN_DIRENT_KIND) == 0;
+      serr = svn_repos_list(root, full_path, patterns, depth, path_info_only,
+                            dav_svn__authz_read_func(&arb), &arb,
+                            list_receiver, &lrb, NULL, NULL, resource->pool);
+    }
+
+  if (serr)
+    {
+      derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, NULL,
+                                  resource->pool);
+      goto cleanup;
+    }
+
+  if ((serr = maybe_send_header(&lrb)))
+    {
+      derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
+                                  "Error beginning REPORT response.",
+                                  resource->pool);
+      goto cleanup;
+    }
+
+  if ((serr = dav_svn__brigade_puts(lrb.bb, lrb.output,
+                                    "</S:list-report>" DEBUG_CR)))
+    {
+      derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
+                                  "Error ending REPORT response.",
+                                  resource->pool);
+      goto cleanup;
+    }
+
+ cleanup:
+
+  dav_svn__operational_log(resource->info,
+                           svn_log__list(full_path, rev, patterns, depth,
+                                         lrb.dirent_fields, resource->pool));
+
+  return dav_svn__final_flush_or_error(resource->info->r, lrb.bb, output,
+                                       derr, resource->pool);
+}

Propchange: subversion/trunk/subversion/mod_dav_svn/reports/list.c
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/trunk/subversion/mod_dav_svn/version.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/mod_dav_svn/version.c?rev=1816330&r1=1816329&r2=1816330&view=diff
==============================================================================
--- subversion/trunk/subversion/mod_dav_svn/version.c (original)
+++ subversion/trunk/subversion/mod_dav_svn/version.c Sat Nov 25 17:12:38 2017
@@ -155,6 +155,7 @@ get_vsn_options(apr_pool_t *p, apr_text_
   apr_text_append(p, phdr, SVN_DAV_NS_DAV_SVN_SVNDIFF1);
   apr_text_append(p, phdr, SVN_DAV_NS_DAV_SVN_SVNDIFF2);
   apr_text_append(p, phdr, SVN_DAV_NS_DAV_SVN_PUT_RESULT_CHECKSUM);
+  apr_text_append(p, phdr, SVN_DAV_NS_DAV_SVN_LIST);
   /* Mergeinfo is a special case: here we merely say that the server
    * knows how to handle mergeinfo -- whether the repository does too
    * is a separate matter.
@@ -1154,6 +1155,10 @@ deliver_report(request_rec *r,
         {
           return dav_svn__get_inherited_props_report(resource, doc, output);
         }
+      else if (strcmp(doc->root->name, "list-report") == 0)
+        {
+          return dav_svn__list_report(resource, doc, output);
+        }
       /* NOTE: if you add a report, don't forget to add it to the
        *       dav_svn__reports_list[] array.
        */


Reply via email to