/* Respond to a S:dated-rev-report request.  The request contains a
 * DAV:creationdate element giving the requested date; the response
 * contains a DAV:version-name element giving the most recent revision
 * as of that date. */
dav_error *
dav_svn__dated_rev_report(const dav_resource *resource,
                          const apr_xml_doc *doc,
                          ap_filter_t *output)
{
  apr_xml_elem *child;
  int ns;
  apr_time_t tm = (apr_time_t) -1;
  svn_revnum_t rev;
  apr_bucket_brigade *bb;
  svn_error_t *err;
  apr_status_t apr_err;
  dav_error *derr = NULL;

  /* Find the DAV:creationdate element and get the requested time from it. */
  ns = dav_svn__find_ns(doc->namespaces, "DAV:");
  if (ns != -1)
    {
      for (child = doc->root->first_child; child != NULL; child = child->next)
        {
          if (child->ns != ns ||
              strcmp(child->name, SVN_DAV__CREATIONDATE) != 0)
            continue;
          /* If this fails, we'll notice below, so ignore any error for now. */
          svn_error_clear
            (svn_time_from_cstring(&tm, dav_xml_get_cdata(child,
                                                          resource->pool, 1),
                                   resource->pool));
        }
    }

  if (tm == (apr_time_t) -1)
    {
      return dav_svn__new_error(resource->pool, HTTP_BAD_REQUEST, 0,
                                "The request does not contain a valid "
                                "'DAV:" SVN_DAV__CREATIONDATE "' element.");
    }

  /* Do the actual work of finding the revision by date. */
  if ((err = svn_repos_dated_revision(&rev, resource->info->repos->repos, tm,
                                      resource->pool)) != SVN_NO_ERROR)
    {
      svn_error_clear(err);
      return dav_svn__new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
                                "Could not access revision times.");
    }

  bb = apr_brigade_create(resource->pool, output->c->bucket_alloc);
  apr_err = ap_fprintf(output, bb,
                       DAV_XML_HEADER DEBUG_CR
                       "<S:dated-rev-report xmlns:S=\"" SVN_XML_NAMESPACE "\" "
                       "xmlns:D=\"DAV:\">" DEBUG_CR
                       "<D:" SVN_DAV__VERSION_NAME ">%ld</D:"
                       SVN_DAV__VERSION_NAME ">""</S:dated-rev-report>", rev);
  if (apr_err)
    derr = dav_svn__convert_err(svn_error_create(apr_err, 0, NULL),
                                HTTP_INTERNAL_SERVER_ERROR,
                                "Error writing REPORT response.",
                                resource->pool);

  return dav_svn__final_flush_or_error(resource->info->r, bb, output,
                                       derr, resource->pool);
}
dav_error *
dav_svn__get_inherited_props_report(const dav_resource *resource,
                                    const apr_xml_doc *doc,
                                    ap_filter_t *output)
{
  svn_error_t *serr;
  dav_error *derr = NULL;
  apr_xml_elem *child;
  apr_array_header_t *inherited_props;
  dav_svn__authz_read_baton arb;
  int ns;
  apr_bucket_brigade *bb;
  const char *path = "/";
  svn_fs_root_t *root;
  int i;
  svn_revnum_t rev = SVN_INVALID_REVNUM;
  apr_pool_t *iterpool;

  /* Sanity check. */
  if (!resource->info->repos_path)
    return dav_svn__new_error(resource->pool, HTTP_BAD_REQUEST, 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,
                                    "The request does not contain the 'svn:' "
                                    "namespace, so it is not going to have "
                                    "certain required elements");
    }

  iterpool = svn_pool_create(resource->pool);

  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;

      if (strcmp(child->name, SVN_DAV__REVISION) == 0)
        {
          rev = SVN_STR_TO_REV(dav_xml_get_cdata(child, iterpool, 1));
        }
      else if (strcmp(child->name, SVN_DAV__PATH) == 0)
        {
          path = dav_xml_get_cdata(child, resource->pool, 0);
          if ((derr = dav_svn__test_canonical(path, iterpool)))
            return derr;
          path = svn_fspath__join(resource->info->repos_path, path,
                                  resource->pool);
        }
      /* else unknown element; skip it */
    }

  /* Build authz read baton */
  arb.r = resource->info->r;
  arb.repos = resource->info->repos;

  /* Build inherited property brigade */
  bb = apr_brigade_create(resource->pool, output->c->bucket_alloc);

  serr = svn_fs_revision_root(&root, resource->info->repos->fs,
                              rev, resource->pool);
  if (serr != NULL)
    return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                "couldn't retrieve revision root",
                                resource->pool);

  serr = svn_repos_fs_get_inherited_props(&inherited_props, root, path, NULL,
                                          dav_svn__authz_read_func(&arb),
                                          &arb, resource->pool, iterpool);
  if (serr)
    {
      derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, NULL,
                                  resource->pool);
      goto cleanup;
    }

  serr = dav_svn__brigade_puts(bb, output,
                               DAV_XML_HEADER DEBUG_CR
                               "<S:" SVN_DAV__INHERITED_PROPS_REPORT " "
                               "xmlns:S=\"" SVN_XML_NAMESPACE "\" "
                               "xmlns:D=\"DAV:\">" DEBUG_CR);
  if (serr)
    {
      derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, NULL,
                                  resource->pool);
      goto cleanup;
    }

  for (i = 0; i < inherited_props->nelts; i++)
    {
      svn_prop_inherited_item_t *elt =
        APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *);

      svn_pool_clear(iterpool);

      serr = dav_svn__brigade_printf(
        bb, output,
        "<S:" SVN_DAV__IPROP_ITEM ">"
        DEBUG_CR
        "<S:" SVN_DAV__IPROP_PATH ">%s</S:" SVN_DAV__IPROP_PATH ">"
        DEBUG_CR,
        apr_xml_quote_string(resource->pool, elt->path_or_url, 0));

      if (!serr)
        {
          apr_hash_index_t *hi;

          for (hi = apr_hash_first(resource->pool, elt->prop_hash);
               hi;
               hi = apr_hash_next(hi))
            {
              const char *propname = apr_hash_this_key(hi);
              svn_string_t *propval = apr_hash_this_val(hi);
              const char *xml_safe;

              serr = dav_svn__brigade_printf(
                bb, output,
                "<S:" SVN_DAV__IPROP_PROPNAME ">%s</S:"
                SVN_DAV__IPROP_PROPNAME ">" DEBUG_CR,
                apr_xml_quote_string(iterpool, propname, 0));

              if (!serr)
                {
                  if (svn_xml_is_xml_safe(propval->data, propval->len))
                    {
                      svn_stringbuf_t *tmp = NULL;
                      svn_xml_escape_cdata_string(&tmp, propval,
                                                  iterpool);
                      xml_safe = tmp->data;
                      serr = dav_svn__brigade_printf(
                        bb, output,
                        "<S:" SVN_DAV__IPROP_PROPVAL ">%s</S:"
                        SVN_DAV__IPROP_PROPVAL ">" DEBUG_CR, xml_safe);
                    }
                  else
                    {
                      xml_safe = svn_base64_encode_string2(
                        propval, TRUE, iterpool)->data;
                      serr = dav_svn__brigade_printf(
                        bb, output,
                        "<S:" SVN_DAV__IPROP_PROPVAL
                        " encoding=\"base64\"" ">%s</S:"
                        SVN_DAV__IPROP_PROPVAL ">" DEBUG_CR, xml_safe);
                    }
                }

              if (serr)
                break;
            }
          if (!serr)
            serr = dav_svn__brigade_printf(bb, output,
                                           "</S:" SVN_DAV__IPROP_ITEM ">"
                                           DEBUG_CR);
        }

      if (serr)
        {
          derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                      "Error ending REPORT response.",
                                      resource->pool);
          goto cleanup;
        }
    }

  if ((serr = dav_svn__brigade_puts(bb, output,
                                    "</S:" SVN_DAV__INHERITED_PROPS_REPORT ">"
                                    DEBUG_CR)))
    {
      derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                  "Error ending REPORT response.",
                                  resource->pool);
      goto cleanup;
    }

 cleanup:

  /* Log this 'high level' svn action. */
  dav_svn__operational_log(resource->info,
                           svn_log__get_inherited_props(path, rev,
                                                        resource->pool));
  svn_pool_destroy(iterpool);
  return dav_svn__final_flush_or_error(resource->info->r, bb, output,
                                       derr, resource->pool);
}
Beispiel #3
0
dav_error *
dav_svn__get_mergeinfo_report(const dav_resource *resource,
                              const apr_xml_doc *doc,
                              ap_filter_t *output)
{
  svn_error_t *serr;
  dav_error *derr = NULL;
  apr_xml_elem *child;
  svn_mergeinfo_catalog_t catalog;
  svn_boolean_t include_descendants = FALSE;
  dav_svn__authz_read_baton arb;
  const dav_svn_repos *repos = resource->info->repos;
  int ns;
  apr_bucket_brigade *bb;
  apr_hash_index_t *hi;

  /* These get determined from the request document. */
  svn_revnum_t rev = SVN_INVALID_REVNUM;
  /* By default look for explicit mergeinfo only. */
  svn_mergeinfo_inheritance_t inherit = svn_mergeinfo_explicit;
  apr_array_header_t *paths
    = apr_array_make(resource->pool, 0, sizeof(const char *));

  /* Sanity check. */
  if (!resource->info->repos_path)
    return dav_svn__new_error(resource->pool, HTTP_BAD_REQUEST, 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_tag(resource->pool, HTTP_BAD_REQUEST, 0,
                                    "The request does not contain the 'svn:' "
                                    "namespace, so it is not going to have "
                                    "certain required elements.",
                                    SVN_DAV_ERROR_NAMESPACE,
                                    SVN_DAV_ERROR_TAG);
    }

  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;

      if (strcmp(child->name, SVN_DAV__REVISION) == 0)
        rev = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1));
      else if (strcmp(child->name, SVN_DAV__INHERIT) == 0)
        {
          inherit = svn_inheritance_from_word(
            dav_xml_get_cdata(child, resource->pool, 1));
        }
      else if (strcmp(child->name, SVN_DAV__PATH) == 0)
        {
          const char *target;
          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. */
          target = svn_fspath__join(resource->info->repos_path, rel_path,
                                    resource->pool);
          (*((const char **)(apr_array_push(paths)))) = target;
        }
      else if (strcmp(child->name, SVN_DAV__INCLUDE_DESCENDANTS) == 0)
        {
          const char *word = dav_xml_get_cdata(child, resource->pool, 1);
          if (strcmp(word, "yes") == 0)
            include_descendants = TRUE;
          /* Else the client isn't supposed to send anyway, so just
             leave it false. */
        }
      /* else unknown element; skip it */
    }

  /* Build authz read baton */
  arb.r = resource->info->r;
  arb.repos = resource->info->repos;

  /* Build mergeinfo brigade */
  bb = apr_brigade_create(resource->pool, output->c->bucket_alloc);

  serr = svn_repos_fs_get_mergeinfo(&catalog, repos->repos, paths, rev,
                                    inherit, include_descendants,
                                    dav_svn__authz_read_func(&arb),
                                    &arb, resource->pool);
  if (serr)
    {
      derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, serr->message,
                                  resource->pool);
      goto cleanup;
    }

  serr = svn_mergeinfo__remove_prefix_from_catalog(&catalog, catalog,
                                                   resource->info->repos_path,
                                                   resource->pool);
  if (serr)
    {
      derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, serr->message,
                                  resource->pool);
      goto cleanup;
    }

  /* Ideally, dav_svn__brigade_printf() would set a flag in bb (or rather,
     in r->sent_bodyct, see dav_method_report()), and ap_fflush()
     would not set that flag unless it actually sent something.  But
     we are condemned to live in another universe, so we must keep
     track ourselves of whether we've sent anything or not.  See the
     long comment after the 'cleanup' label for more details. */
  serr = dav_svn__brigade_puts(bb, output,
                               DAV_XML_HEADER DEBUG_CR
                               "<S:" SVN_DAV__MERGEINFO_REPORT " "
                               "xmlns:S=\"" SVN_XML_NAMESPACE "\" "
                               "xmlns:D=\"DAV:\">" DEBUG_CR);
  if (serr)
    {
      derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, serr->message,
                                  resource->pool);
      goto cleanup;
    }

  for (hi = apr_hash_first(resource->pool, catalog); hi;
       hi = apr_hash_next(hi))
    {
      const void *key;
      void *value;
      const char *path;
      svn_mergeinfo_t mergeinfo;
      svn_string_t *mergeinfo_string;

      apr_hash_this(hi, &key, NULL, &value);
      path = key;
      mergeinfo = value;
      serr = svn_mergeinfo_to_string(&mergeinfo_string, mergeinfo,
                                     resource->pool);
      if (serr)
        {
          derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                      "Error ending REPORT response.",
                                      resource->pool);
          goto cleanup;
        }
      serr = dav_svn__brigade_printf
        (bb, output,
         "<S:" SVN_DAV__MERGEINFO_ITEM ">"
         DEBUG_CR
         "<S:" SVN_DAV__MERGEINFO_PATH ">%s</S:" SVN_DAV__MERGEINFO_PATH ">"
         DEBUG_CR
         "<S:" SVN_DAV__MERGEINFO_INFO ">%s</S:" SVN_DAV__MERGEINFO_INFO ">"
         DEBUG_CR
         "</S:" SVN_DAV__MERGEINFO_ITEM ">",
         apr_xml_quote_string(resource->pool, path, 0),
         apr_xml_quote_string(resource->pool, mergeinfo_string->data, 0));
      if (serr)
        {
          derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                      "Error ending REPORT response.",
                                      resource->pool);
          goto cleanup;
        }
    }

  if ((serr = dav_svn__brigade_puts(bb, output,
                                    "</S:" SVN_DAV__MERGEINFO_REPORT ">"
                                    DEBUG_CR)))
    {
      derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                  "Error ending REPORT response.",
                                  resource->pool);
      goto cleanup;
    }

 cleanup:

  /* We've detected a 'high level' svn action to log. */
  dav_svn__operational_log(resource->info,
                           svn_log__get_mergeinfo(paths, inherit,
                                                  include_descendants,
                                                  resource->pool));

  return dav_svn__final_flush_or_error(resource->info->r, bb, output,
                                       derr, resource->pool);
}
Beispiel #4
0
/* Respond to a client request for a REPORT of type file-revs-report for the
   RESOURCE.  Get request body from DOC and send result to OUTPUT. */
dav_error *
dav_svn__file_revs_report(const dav_resource *resource,
                          const apr_xml_doc *doc,
                          ap_filter_t *output)
{
  svn_error_t *serr;
  dav_error *derr = NULL;
  apr_xml_elem *child;
  int ns;
  struct file_rev_baton frb;
  dav_svn__authz_read_baton arb;
  const char *abs_path = NULL;

  /* These get determined from the request document. */
  svn_revnum_t start = SVN_INVALID_REVNUM;
  svn_revnum_t end = SVN_INVALID_REVNUM;
  svn_boolean_t include_merged_revisions = FALSE;    /* off by default */

  /* Construct the authz read check baton. */
  arb.r = resource->info->r;
  arb.repos = resource->info->repos;

  /* Sanity check. */
  if (!resource->info->repos_path)
    return dav_svn__new_error(resource->pool, HTTP_BAD_REQUEST, 0,
                              "The request does not specify a repository path");
  ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE);
  /* ### This is done on other places, but the document element is
     in this namespace, so is this necessary at all? */
  if (ns == -1)
    {
      return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0,
                                    "The request does not contain the 'svn:' "
                                    "namespace, so it is not going to have "
                                    "certain required elements.",
                                    SVN_DAV_ERROR_NAMESPACE,
                                    SVN_DAV_ERROR_TAG);
    }

  /* Get request information. */
  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;

      if (strcmp(child->name, "start-revision") == 0)
        start = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1));
      else if (strcmp(child->name, "end-revision") == 0)
        end = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1));
      else if (strcmp(child->name, "include-merged-revisions") == 0)
        include_merged_revisions = TRUE; /* presence indicates positivity */
      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. */
          abs_path = svn_fspath__join(resource->info->repos_path, rel_path,
                                      resource->pool);
        }
      /* else unknown element; skip it */
    }

  /* Check that all parameters are present and valid. */
  if (! abs_path)
    return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0,
                                  "Not all parameters passed.",
                                  SVN_DAV_ERROR_NAMESPACE,
                                  SVN_DAV_ERROR_TAG);

  frb.bb = apr_brigade_create(resource->pool,
                              output->c->bucket_alloc);
  frb.output = output;
  frb.needs_header = TRUE;
  frb.svndiff_version = resource->info->svndiff_version;
  frb.compression_level = dav_svn__get_compression_level(resource->info->r);

  /* file_rev_handler will send header first time it is called. */

  /* Get the revisions and send them. */
  serr = svn_repos_get_file_revs2(resource->info->repos->repos,
                                  abs_path, start, end, include_merged_revisions,
                                  dav_svn__authz_read_func(&arb), &arb,
                                  file_rev_handler, &frb, resource->pool);

  if (serr)
    {
      /* We don't 'goto cleanup' because ap_fflush() tells httpd
         to write the HTTP headers out, and that includes whatever
         r->status is at that particular time.  When we call
         dav_svn__convert_err(), we don't immediately set r->status
         right then, so r->status remains 0, hence HTTP status 200
         would be misleadingly returned. */
      return (dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                   serr->message, resource->pool));
    }

  if ((serr = maybe_send_header(&frb)))
    {
      derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                  "Error beginning REPORT reponse",
                                  resource->pool);
      goto cleanup;
    }

  if ((serr = dav_svn__brigade_puts(frb.bb, frb.output,
                                    "</S:file-revs-report>" DEBUG_CR)))
    {
      derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                  "Error ending REPORT reponse",
                                  resource->pool);
      goto cleanup;
    }

 cleanup:

  /* We've detected a 'high level' svn action to log. */
  dav_svn__operational_log(resource->info,
                           svn_log__get_file_revs(abs_path, start, end,
                                                  include_merged_revisions,
                                                  resource->pool));

  return dav_svn__final_flush_or_error(resource->info->r, frb.bb, output,
                                       derr, resource->pool);
}
Beispiel #5
0
/* Respond to a S:deleted-rev-report request. */
dav_error *
dav_svn__get_deleted_rev_report(const dav_resource *resource,
                                const apr_xml_doc *doc,
                                ap_filter_t *output)
{
  apr_xml_elem *child;
  int ns;
  const char *rel_path = NULL, *abs_path;
  svn_revnum_t peg_rev = SVN_INVALID_REVNUM, end_rev = SVN_INVALID_REVNUM,
      deleted_rev;
  apr_bucket_brigade *bb;
  svn_error_t *err;
  apr_status_t apr_err;
  dav_error *derr = NULL;

  /* Sanity check. */
  ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE);
  if (ns == -1)
    return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0,
                                  "The request does not contain the 'svn:' "
                                  "namespace, so it is not going to have "
                                  "certain required elements.",
                                  SVN_DAV_ERROR_NAMESPACE,
                                  SVN_DAV_ERROR_TAG);

  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;

      if (strcmp(child->name, "peg-revision") == 0)
        {
          peg_rev = SVN_STR_TO_REV(dav_xml_get_cdata(child,
                                                     resource->pool, 1));
        }
      else if (strcmp(child->name, "end-revision") == 0)
        {
          end_rev = SVN_STR_TO_REV(dav_xml_get_cdata(child,
                                                     resource->pool, 1));
        }
      else if (strcmp(child->name, "path") == 0)
        {
          rel_path = dav_xml_get_cdata(child, resource->pool, 0);
          if ((derr = dav_svn__test_canonical(rel_path, resource->pool)))
            return derr;
        }
    }

    /* Check that all parameters are present. */
  if (! (rel_path
         && SVN_IS_VALID_REVNUM(peg_rev)
         && SVN_IS_VALID_REVNUM(end_rev)))
    {
      return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0,
                                    "Not all parameters passed.",
                                    SVN_DAV_ERROR_NAMESPACE,
                                    SVN_DAV_ERROR_TAG);
    }

  /* Append the relative path to the base FS path to get an absolute
     repository path. */
  abs_path = svn_path_join(resource->info->repos_path, rel_path,
                           resource->pool);

  /* Do what we actually came here for: Find the rev abs_path was deleted. */
  err = svn_repos_deleted_rev(resource->info->repos->fs,
                              abs_path, peg_rev, end_rev,
                              &deleted_rev, resource->pool);
  if (err)
    {
      svn_error_clear(err);
      return dav_new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
                           "Could not find revision path was deleted.");
    }

  bb = apr_brigade_create(resource->pool, output->c->bucket_alloc);
  apr_err = ap_fprintf(output, bb,
                       DAV_XML_HEADER DEBUG_CR
                       "<S:get-deleted-rev-report xmlns:S=\""
                       SVN_XML_NAMESPACE "\" xmlns:D=\"DAV:\">" DEBUG_CR
                       "<D:" SVN_DAV__VERSION_NAME ">%ld</D:"
                       SVN_DAV__VERSION_NAME ">""</S:get-deleted-rev-report>",
                       deleted_rev);
  if (apr_err)
    derr = dav_svn__convert_err(svn_error_create(apr_err, 0, NULL),
                                HTTP_INTERNAL_SERVER_ERROR,
                                "Error writing REPORT response.",
                                resource->pool);

  return dav_svn__final_flush_or_error(resource->info->r, bb, output,
                                       derr, resource->pool);
}
Beispiel #6
0
dav_error *
dav_svn__replay_report(const dav_resource *resource,
                       const apr_xml_doc *doc,
                       dav_svn__output *output)
{
  dav_error *derr = NULL;
  svn_revnum_t low_water_mark = SVN_INVALID_REVNUM;
  svn_revnum_t rev;
  const svn_delta_editor_t *editor;
  svn_boolean_t send_deltas = TRUE;
  dav_svn__authz_read_baton arb;
  const char *base_dir;
  apr_bucket_brigade *bb;
  apr_xml_elem *child;
  svn_fs_root_t *root;
  svn_error_t *err;
  void *edit_baton;
  int ns;

  /* In Subversion 1.8, we allowed this REPORT to be issue against a
     revision resource.  Doing so means the REV is part of the request
     URL, and BASE_DIR is embedded in the request body.

     The old-school (and incorrect, see issue #4287 --
     https://issues.apache.org/jira/browse/SVN-4287) way was
     to REPORT on the public URL of the BASE_DIR and embed the REV in
     the report body.
  */
  if (resource->baselined
      && (resource->type == DAV_RESOURCE_TYPE_VERSION))
    {
      rev = resource->info->root.rev;
      base_dir = NULL;
    }
  else
    {
      rev = SVN_INVALID_REVNUM;
      base_dir = resource->info->repos_path;
    }

  arb.r = resource->info->r;
  arb.repos = resource->info->repos;

  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 an "
                                  "svn:revision element. That element is "
                                  "required");

  for (child = doc->root->first_child; child != NULL; child = child->next)
    {
      if (child->ns == ns)
        {
          const char *cdata;

          if (strcmp(child->name, "revision") == 0)
            {
              if (SVN_IS_VALID_REVNUM(rev))
                {
                  /* Uh... we already have a revision to use, either
                     because this tag is non-unique or because the
                     request was submitted against a revision-bearing
                     resource URL.  Either way, something's not
                     right.  */
                  return malformed_element_error("revision", resource->pool);
                }

              cdata = dav_xml_get_cdata(child, resource->pool, 1);
              rev = SVN_STR_TO_REV(cdata);
            }
          else if (strcmp(child->name, "low-water-mark") == 0)
            {
              cdata = dav_xml_get_cdata(child, resource->pool, 1);
              if (! cdata)
                return malformed_element_error("low-water-mark",
                                               resource->pool);
              low_water_mark = SVN_STR_TO_REV(cdata);
            }
          else if (strcmp(child->name, "send-deltas") == 0)
            {
              apr_int64_t parsed_val;

              cdata = dav_xml_get_cdata(child, resource->pool, 1);
              if (! cdata)
                return malformed_element_error("send-deltas", resource->pool);
              err = svn_cstring_strtoi64(&parsed_val, cdata, 0, 1, 10);
              if (err)
                {
                  svn_error_clear(err);
                  return malformed_element_error("send-deltas", resource->pool);
                }
              send_deltas = parsed_val != 0;
            }
          else if (strcmp(child->name, "include-path") == 0)
            {
              cdata = dav_xml_get_cdata(child, resource->pool, 1);
              if ((derr = dav_svn__test_canonical(cdata, resource->pool)))
                return derr;

              /* Force BASE_DIR to be a relative path, not an fspath. */
              base_dir = svn_relpath_canonicalize(cdata, resource->pool);
            }
        }
    }

  if (! SVN_IS_VALID_REVNUM(rev))
    return dav_svn__new_error_svn
             (resource->pool, HTTP_BAD_REQUEST, 0, 0,
              "Request was missing the revision argument");

  if (! SVN_IS_VALID_REVNUM(low_water_mark))
    return dav_svn__new_error_svn
             (resource->pool, HTTP_BAD_REQUEST, 0, 0,
              "Request was missing the low-water-mark argument");

  if (! base_dir)
    base_dir = "";

  bb = apr_brigade_create(resource->pool,
                          dav_svn__output_get_bucket_alloc(output));

  if ((err = svn_fs_revision_root(&root, resource->info->repos->fs, rev,
                                  resource->pool)))
    {
      derr = dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR,
                                  "Couldn't retrieve revision root",
                                  resource->pool);
      goto cleanup;
    }

  make_editor(&editor, &edit_baton, bb, output,
              dav_svn__get_compression_level(resource->info->r),
              resource->info->svndiff_version,
              resource->pool);

  if ((err = svn_repos_replay2(root, base_dir, low_water_mark,
                               send_deltas, editor, edit_baton,
                               dav_svn__authz_read_func(&arb), &arb,
                               resource->pool)))
    {
      derr = dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR,
                                  "Problem replaying revision",
                                  resource->pool);
      goto cleanup;
    }

  if ((err = end_report(edit_baton)))
    {
      derr = dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR,
                                  "Problem closing editor drive",
                                  resource->pool);
      goto cleanup;
    }

 cleanup:
  dav_svn__operational_log(resource->info,
                           svn_log__replay(base_dir, rev,
                                           resource->info->r->pool));

  /* Flush the brigade. */
  return dav_svn__final_flush_or_error(resource->info->r, bb, output,
                                       derr, resource->pool);
}
Beispiel #7
0
dav_error *
dav_svn__get_locations_report(const dav_resource *resource,
                              const apr_xml_doc *doc,
                              ap_filter_t *output)
{
  svn_error_t *serr;
  dav_error *derr = NULL;
  apr_status_t apr_err;
  apr_bucket_brigade *bb;
  dav_svn__authz_read_baton arb;

  /* The parameters to do the operation on. */
  const char *relative_path = NULL;
  const char *abs_path;
  svn_revnum_t peg_revision = SVN_INVALID_REVNUM;
  apr_array_header_t *location_revisions;

  /* XML Parsing Variables */
  int ns;
  apr_xml_elem *child;

  apr_hash_t *fs_locations;

  location_revisions = apr_array_make(resource->pool, 0,
                                      sizeof(svn_revnum_t));

  /* Sanity check. */
  ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE);
  if (ns == -1)
    {
      return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0,
                                    "The request does not contain the 'svn:' "
                                    "namespace, so it is not going to have "
                                    "certain required elements.",
                                    SVN_DAV_ERROR_NAMESPACE,
                                    SVN_DAV_ERROR_TAG);
    }

  /* Gather the parameters. */
  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;

      if (strcmp(child->name, "peg-revision") == 0)
        peg_revision = SVN_STR_TO_REV(dav_xml_get_cdata(child,
                                                        resource->pool, 1));
      else if (strcmp(child->name, "location-revision") == 0)
        {
          svn_revnum_t revision
            = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1));
          APR_ARRAY_PUSH(location_revisions, svn_revnum_t) = revision;
        }
      else if (strcmp(child->name, "path") == 0)
        {
          relative_path = dav_xml_get_cdata(child, resource->pool, 0);
          if ((derr = dav_svn__test_canonical(relative_path, resource->pool)))
            return derr;
        }
    }

  /* Now we should have the parameters ready - let's
     check if they are all present. */
  if (! (relative_path && SVN_IS_VALID_REVNUM(peg_revision)))
    {
      return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0,
                                    "Not all parameters passed.",
                                    SVN_DAV_ERROR_NAMESPACE,
                                    SVN_DAV_ERROR_TAG);
    }

  /* Append the relative path to the base FS path to get an absolute
     repository path. */
  abs_path = svn_path_join(resource->info->repos_path, relative_path,
                           resource->pool);

  /* Build an authz read baton */
  arb.r = resource->info->r;
  arb.repos = resource->info->repos;

  serr = svn_repos_trace_node_locations(resource->info->repos->fs,
                                        &fs_locations, abs_path, peg_revision,
                                        location_revisions,
                                        dav_svn__authz_read_func(&arb), &arb,
                                        resource->pool);

  if (serr)
    {
      return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                  serr->message, resource->pool);
    }

  bb = apr_brigade_create(resource->pool, output->c->bucket_alloc);

  apr_err = send_get_locations_report(output, bb, resource, fs_locations);

  if (apr_err)
    derr = dav_svn__convert_err(svn_error_create(apr_err, 0, NULL),
                                HTTP_INTERNAL_SERVER_ERROR,
                                "Error writing REPORT response.",
                                resource->pool);

  /* Flush the contents of the brigade (returning an error only if we
     don't already have one). */
  if (((apr_err = ap_fflush(output, bb))) && (! derr))
    return dav_svn__convert_err(svn_error_create(apr_err, 0, NULL),
                                HTTP_INTERNAL_SERVER_ERROR,
                                "Error flushing brigade.",
                                resource->pool);

  return derr;
}
Beispiel #8
0
dav_error *
dav_svn__log_report(const dav_resource *resource,
                    const apr_xml_doc *doc,
                    ap_filter_t *output)
{
  svn_error_t *serr;
  dav_error *derr = NULL;
  apr_xml_elem *child;
  struct log_receiver_baton lrb;
  dav_svn__authz_read_baton arb;
  const dav_svn_repos *repos = resource->info->repos;
  const char *target = NULL;
  int limit = 0;
  int ns;
  svn_boolean_t seen_revprop_element;

  /* These get determined from the request document. */
  svn_revnum_t start = SVN_INVALID_REVNUM;   /* defaults to HEAD */
  svn_revnum_t end = SVN_INVALID_REVNUM;     /* defaults to HEAD */
  svn_boolean_t discover_changed_paths = FALSE;      /* off by default */
  svn_boolean_t strict_node_history = FALSE;         /* off by default */
  svn_boolean_t include_merged_revisions = FALSE;    /* off by default */
  apr_array_header_t *revprops = apr_array_make(resource->pool, 3,
                                                sizeof(const char *));
  apr_array_header_t *paths
    = apr_array_make(resource->pool, 1, sizeof(const char *));

  /* Sanity check. */
  if (!resource->info->repos_path)
    return dav_svn__new_error(resource->pool, HTTP_BAD_REQUEST, 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_tag(resource->pool, HTTP_BAD_REQUEST, 0,
                                    "The request does not contain the 'svn:' "
                                    "namespace, so it is not going to have "
                                    "certain required elements.",
                                    SVN_DAV_ERROR_NAMESPACE,
                                    SVN_DAV_ERROR_TAG);
    }

  /* If this is still FALSE after the loop, we haven't seen either of
     the revprop elements, meaning a pre-1.5 client; we'll return the
     standard author/date/log revprops. */
  seen_revprop_element = FALSE;

  lrb.requested_custom_revprops = FALSE;
  lrb.encode_binary_props = FALSE;
  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;

      if (strcmp(child->name, "start-revision") == 0)
        start = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1));
      else if (strcmp(child->name, "end-revision") == 0)
        end = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1));
      else if (strcmp(child->name, "limit") == 0)
        {
          serr = svn_cstring_atoi(&limit,
                                  dav_xml_get_cdata(child, resource->pool, 1));
          if (serr)
            {
              return dav_svn__convert_err(serr, HTTP_BAD_REQUEST,
                                          "Malformed CDATA in element "
                                          "\"limit\"", resource->pool);
            }
        }
      else if (strcmp(child->name, "discover-changed-paths") == 0)
        discover_changed_paths = TRUE; /* presence indicates positivity */
      else if (strcmp(child->name, "strict-node-history") == 0)
        strict_node_history = TRUE; /* presence indicates positivity */
      else if (strcmp(child->name, "include-merged-revisions") == 0)
        include_merged_revisions = TRUE; /* presence indicates positivity */
      else if (strcmp(child->name, "encode-binary-props") == 0)
        lrb.encode_binary_props = TRUE; /* presence indicates positivity */
      else if (strcmp(child->name, "all-revprops") == 0)
        {
          revprops = NULL; /* presence indicates fetch all revprops */
          seen_revprop_element = lrb.requested_custom_revprops = TRUE;
        }
      else if (strcmp(child->name, "no-revprops") == 0)
        {
          /* presence indicates fetch no revprops */

          seen_revprop_element = lrb.requested_custom_revprops = TRUE;
        }
      else if (strcmp(child->name, "revprop") == 0)
        {
          if (revprops)
            {
              /* We're not fetching all revprops, append to fetch list. */
              const char *name = dav_xml_get_cdata(child, resource->pool, 0);
              APR_ARRAY_PUSH(revprops, const char *) = name;
              if (!lrb.requested_custom_revprops
                  && strcmp(name, SVN_PROP_REVISION_AUTHOR) != 0
                  && strcmp(name, SVN_PROP_REVISION_DATE) != 0
                  && strcmp(name, SVN_PROP_REVISION_LOG) != 0)
                lrb.requested_custom_revprops = TRUE;
            }
          seen_revprop_element = TRUE;
        }
      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. */
          target = svn_fspath__join(resource->info->repos_path, rel_path,
                                    resource->pool);
          APR_ARRAY_PUSH(paths, const char *) = target;
        }
      /* else unknown element; skip it */
    }

  if (!seen_revprop_element)
    {
      /* pre-1.5 client */
      APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_AUTHOR;
      APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_DATE;
      APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_LOG;
    }

  /* 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! */
                              output->c->bucket_alloc);
  lrb.output = output;
  lrb.needs_header = TRUE;
  lrb.stack_depth = 0;
  /* lrb.requested_custom_revprops set above */

  /* Our svn_log_entry_receiver_t sends the <S:log-report> header in
     a lazy fashion.  Before writing the first log message, it assures
     that the header has already been sent (checking the needs_header
     flag in our log_receiver_baton structure). */

  /* Send zero or more log items. */
  serr = svn_repos_get_logs4(repos->repos,
                             paths,
                             start,
                             end,
                             limit,
                             discover_changed_paths,
                             strict_node_history,
                             include_merged_revisions,
                             revprops,
                             dav_svn__authz_read_func(&arb),
                             &arb,
                             log_receiver,
                             &lrb,
                             resource->pool);
  if (serr)
    {
      derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, serr->message,
                                  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:log-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__log(paths, start, end, limit,
                                        discover_changed_paths,
                                        strict_node_history,
                                        include_merged_revisions, revprops,
                                        resource->pool));

  return dav_svn__final_flush_or_error(resource->info->r, lrb.bb, output,
                                       derr, resource->pool);
}
Beispiel #9
0
dav_error *
dav_svn__replay_report(const dav_resource *resource,
                       const apr_xml_doc *doc,
                       ap_filter_t *output)
{
  svn_revnum_t low_water_mark = SVN_INVALID_REVNUM;
  svn_revnum_t rev = SVN_INVALID_REVNUM;
  const svn_delta_editor_t *editor;
  svn_boolean_t send_deltas = TRUE;
  dav_svn__authz_read_baton arb;
  const char *base_dir = resource->info->repos_path;
  apr_bucket_brigade *bb;
  apr_xml_elem *child;
  svn_fs_root_t *root;
  svn_error_t *err;
  void *edit_baton;
  int ns;

  /* The request won't have a repos_path if it's for the root. */
  if (! base_dir)
    base_dir = "";

  arb.r = resource->info->r;
  arb.repos = resource->info->repos;

  ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE);
  if (ns == -1)
    return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0,
                                  "The request does not contain the 'svn:' "
                                  "namespace, so it is not going to have an "
                                  "svn:revision element. That element is "
                                  "required.",
                                  SVN_DAV_ERROR_NAMESPACE,
                                  SVN_DAV_ERROR_TAG);

  for (child = doc->root->first_child; child != NULL; child = child->next)
    {
      if (child->ns == ns)
        {
          const char *cdata;

          if (strcmp(child->name, "revision") == 0)
            {
              cdata = dav_xml_get_cdata(child, resource->pool, 1);
              if (! cdata)
                return malformed_element_error("revision", resource->pool);
              rev = SVN_STR_TO_REV(cdata);
            }
          else if (strcmp(child->name, "low-water-mark") == 0)
            {
              cdata = dav_xml_get_cdata(child, resource->pool, 1);
              if (! cdata)
                return malformed_element_error("low-water-mark",
                                               resource->pool);
              low_water_mark = SVN_STR_TO_REV(cdata);
            }
          else if (strcmp(child->name, "send-deltas") == 0)
            {
              cdata = dav_xml_get_cdata(child, resource->pool, 1);
              if (! cdata)
                return malformed_element_error("send-deltas", resource->pool);
              send_deltas = atoi(cdata);
            }
        }
    }

  if (! SVN_IS_VALID_REVNUM(rev))
    return dav_svn__new_error_tag
             (resource->pool, HTTP_BAD_REQUEST, 0,
              "Request was missing the revision argument.",
              SVN_DAV_ERROR_NAMESPACE, SVN_DAV_ERROR_TAG);

  if (! SVN_IS_VALID_REVNUM(low_water_mark))
    return dav_svn__new_error_tag
             (resource->pool, HTTP_BAD_REQUEST, 0,
              "Request was missing the low-water-mark argument.",
              SVN_DAV_ERROR_NAMESPACE, SVN_DAV_ERROR_TAG);

  bb = apr_brigade_create(resource->pool, output->c->bucket_alloc);

  if ((err = svn_fs_revision_root(&root, resource->info->repos->fs, rev,
                                  resource->pool)))
    return dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR,
                                "Couldn't retrieve revision root",
                                resource->pool);

  make_editor(&editor, &edit_baton, bb, output, resource->pool);

  if ((err = svn_repos_replay2(root, base_dir, low_water_mark,
                               send_deltas, editor, edit_baton,
                               dav_svn__authz_read_func(&arb), &arb,
                               resource->pool)))
    return dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR,
                                "Problem replaying revision",
                                resource->pool);

  if ((err = end_report(edit_baton)))
    return dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR,
                                "Problem closing editor drive",
                                resource->pool);

  {
    const char *action, *log_base_dir;

    if (base_dir && base_dir[0] != '\0')
      log_base_dir = svn_path_uri_encode(base_dir, resource->info->r->pool);
    else
      log_base_dir = "/";
    action = apr_psprintf(resource->info->r->pool, "replay %s r%ld",
                          log_base_dir, rev);
    dav_svn__operational_log(resource->info, action);
  }

  ap_fflush(output, bb);

  return NULL;
}
dav_error *
dav_svn__get_location_segments_report(const dav_resource *resource,
                                      const apr_xml_doc *doc,
                                      ap_filter_t *output)
{
  svn_error_t *serr;
  dav_error *derr = NULL;
  apr_bucket_brigade *bb;
  int ns;
  apr_xml_elem *child;
  const char *abs_path = NULL;
  svn_revnum_t peg_revision = SVN_INVALID_REVNUM;
  svn_revnum_t start_rev = SVN_INVALID_REVNUM;
  svn_revnum_t end_rev = SVN_INVALID_REVNUM;
  dav_svn__authz_read_baton arb;
  struct location_segment_baton location_segment_baton;

  /* Sanity check. */
  if (!resource->info->repos_path)
    return dav_svn__new_error(resource->pool, HTTP_BAD_REQUEST, 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,
                                    "The request does not contain the 'svn:' "
                                    "namespace, so it is not going to have "
                                    "certain required elements");
    }

  /* Gather the parameters. */
  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;

      if (strcmp(child->name, "peg-revision") == 0)
        {
          peg_revision = SVN_STR_TO_REV(dav_xml_get_cdata(child,
                                                          resource->pool, 1));
        }
      else if (strcmp(child->name, "start-revision") == 0)
        {
          start_rev = SVN_STR_TO_REV(dav_xml_get_cdata(child,
                                                       resource->pool, 1));
        }
      else if (strcmp(child->name, "end-revision") == 0)
        {
          end_rev = SVN_STR_TO_REV(dav_xml_get_cdata(child,
                                                     resource->pool, 1));
        }
      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. */
          abs_path = svn_fspath__join(resource->info->repos_path, rel_path,
                                      resource->pool);
        }
    }

  /* Check that all parameters are present and valid. */
  if (! abs_path)
    return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0,
                                  "Not all parameters passed");

  /* No START_REV or PEG_REVISION?  We'll use HEAD. */
  if (!SVN_IS_VALID_REVNUM(start_rev) || !SVN_IS_VALID_REVNUM(peg_revision))
    {
      svn_revnum_t youngest;

      serr = svn_fs_youngest_rev(&youngest, resource->info->repos->fs,
                                 resource->pool);
      if (serr != NULL)
        return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                    "Could not determine youngest revision",
                                    resource->pool);

      if (!SVN_IS_VALID_REVNUM(start_rev))
        start_rev = youngest;
      if (!SVN_IS_VALID_REVNUM(peg_revision))
        peg_revision = youngest;
    }

  /* No END_REV?  We'll use 0. */
  if (!SVN_IS_VALID_REVNUM(end_rev))
    end_rev = 0;

  if (end_rev > start_rev)
    return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0,
                                  "End revision must not be younger than "
                                  "start revision");
  if (start_rev > peg_revision)
    return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0,
                                  "Start revision must not be younger than "
                                  "peg revision");

  /* Build an authz read baton. */
  arb.r = resource->info->r;
  arb.repos = resource->info->repos;

  /* Build the bucket brigade we'll use for output. */
  bb = apr_brigade_create(resource->pool, output->c->bucket_alloc);

  /* Do what we came here for. */
  location_segment_baton.sent_opener = FALSE;
  location_segment_baton.output = output;
  location_segment_baton.bb = bb;
  if ((serr = svn_repos_node_location_segments(resource->info->repos->repos,
                                               abs_path, peg_revision,
                                               start_rev, end_rev,
                                               location_segment_receiver,
                                               &location_segment_baton,
                                               dav_svn__authz_read_func(&arb),
                                               &arb, resource->pool)))
    {
      derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, NULL,
                                  resource->pool);
      goto cleanup;
    }

  if ((serr = maybe_send_opener(&location_segment_baton)))
    {
      derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                  "Error beginning REPORT response.",
                                  resource->pool);
      goto cleanup;
    }

  if ((serr = dav_svn__brigade_puts(bb, output,
                                    "</S:get-location-segments-report>"
                                    DEBUG_CR)))
    {
      derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                  "Error ending REPORT response.",
                                  resource->pool);
      goto cleanup;
    }

 cleanup:
  return dav_svn__final_flush_or_error(resource->info->r, bb, output,
                                       derr, resource->pool);
}