Beispiel #1
0
/* send a response to the client for this baton */
static svn_error_t *
send_response(const dav_svn_repos *repos,
              svn_fs_root_t *root,
              const char *path,
              svn_boolean_t is_dir,
              ap_filter_t *output,
              apr_bucket_brigade *bb,
              apr_pool_t *pool)
{
  const char *href;
  const char *vsn_url;
  apr_status_t status;
  svn_revnum_t rev_to_use;

  href = dav_svn__build_uri(repos, DAV_SVN__BUILD_URI_PUBLIC,
                            SVN_IGNORED_REVNUM, path, 0 /* add_href */, pool);
  rev_to_use = dav_svn__get_safe_cr(root, path, pool);
  vsn_url = dav_svn__build_uri(repos, DAV_SVN__BUILD_URI_VERSION,
                               rev_to_use, path, 0 /* add_href */, pool);
  status = ap_fputstrs(output, bb,
                       "<D:response>" DEBUG_CR
                       "<D:href>",
                       apr_xml_quote_string(pool, href, 1),
                       "</D:href>" DEBUG_CR
                       "<D:propstat><D:prop>" DEBUG_CR,
                       is_dir
                         ? "<D:resourcetype><D:collection/></D:resourcetype>"
                         : "<D:resourcetype/>",
                       DEBUG_CR,
                       "<D:checked-in><D:href>",
                       apr_xml_quote_string(pool, vsn_url, 1),
                       "</D:href></D:checked-in>" DEBUG_CR
                       "</D:prop>" DEBUG_CR
                       "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
                       "</D:propstat>" DEBUG_CR
                       "</D:response>" DEBUG_CR,
                       NULL);
  if (status != APR_SUCCESS)
    return svn_error_wrap_apr(status, "Can't write response to output");

  return SVN_NO_ERROR;
}
Beispiel #2
0
dav_error *
dav_svn__merge_response(ap_filter_t *output,
                        const dav_svn_repos *repos,
                        svn_revnum_t new_rev,
                        char *post_commit_err,
                        apr_xml_elem *prop_elem,
                        svn_boolean_t disable_merge_response,
                        apr_pool_t *pool)
{
  apr_bucket_brigade *bb;
  svn_fs_root_t *root;
  svn_error_t *serr;
  const char *vcc;
  const char *rev;
  svn_string_t *creationdate, *creator_displayname;
  const char *post_commit_err_elem = NULL,
             *post_commit_header_info = NULL;

  serr = svn_fs_revision_root(&root, repos->fs, new_rev, pool);
  if (serr != NULL)
    {
      return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                  "Could not open the FS root for the "
                                  "revision just committed.",
                                  repos->pool);
    }

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

  /* prep some strings */

  /* the HREF for the baseline is actually the VCC */
  vcc = dav_svn__build_uri(repos, DAV_SVN__BUILD_URI_VCC, SVN_IGNORED_REVNUM,
                           NULL, 0 /* add_href */, pool);

  /* the version-name of the baseline is the revision number */
  rev = apr_psprintf(pool, "%ld", new_rev);

  /* get the post-commit hook stderr, if any */
  if (post_commit_err)
    {
      post_commit_header_info = apr_psprintf(pool,
                                             " xmlns:S=\"%s\"",
                                             SVN_XML_NAMESPACE);
      post_commit_err_elem = apr_psprintf(pool,
                                          "<S:post-commit-err>%s"
                                          "</S:post-commit-err>",
                                          post_commit_err);
    }
  else
    {
      post_commit_header_info = "" ;
      post_commit_err_elem = "" ;
    }


  /* get the creationdate and creator-displayname of the new revision, too. */
  serr = svn_fs_revision_prop(&creationdate, repos->fs, new_rev,
                              SVN_PROP_REVISION_DATE, pool);
  if (serr != NULL)
    {
      return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                  "Could not get date of newest revision",
                                  repos->pool);
    }
  serr = svn_fs_revision_prop(&creator_displayname, repos->fs, new_rev,
                              SVN_PROP_REVISION_AUTHOR, pool);
  if (serr != NULL)
    {
      return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                  "Could not get author of newest revision",
                                  repos->pool);
    }


  (void) ap_fputstrs(output, bb,
                     DAV_XML_HEADER DEBUG_CR
                     "<D:merge-response xmlns:D=\"DAV:\"",
                     post_commit_header_info,
                     ">" DEBUG_CR
                     "<D:updated-set>" DEBUG_CR

                     /* generate a response for the new baseline */
                     "<D:response>" DEBUG_CR
                     "<D:href>",
                     apr_xml_quote_string(pool, vcc, 1),
                     "</D:href>" DEBUG_CR
                     "<D:propstat><D:prop>" DEBUG_CR
                     /* ### this is wrong. it's a VCC, not a baseline. but
                        ### we need to tell the client to look at *this*
                        ### resource for the version-name. */
                     "<D:resourcetype><D:baseline/></D:resourcetype>" DEBUG_CR,
                     post_commit_err_elem, DEBUG_CR
                     "<D:version-name>", rev, "</D:version-name>" DEBUG_CR,
                     NULL);
  if (creationdate)
    {
      (void) ap_fputstrs(output, bb,
                         "<D:creationdate>",
                         apr_xml_quote_string(pool, creationdate->data, 1),
                         "</D:creationdate>" DEBUG_CR,
                         NULL);
    }
  if (creator_displayname)
    {
      (void) ap_fputstrs(output, bb,
                         "<D:creator-displayname>",
                         apr_xml_quote_string(pool,
                                              creator_displayname->data, 1),
                         "</D:creator-displayname>" DEBUG_CR,
                         NULL);
    }
  (void) ap_fputstrs(output, bb,
                     "</D:prop>" DEBUG_CR
                     "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
                     "</D:propstat>" DEBUG_CR
                     "</D:response>" DEBUG_CR,

                     NULL);

  /* ONLY have dir_delta drive the editor if the caller asked us to
     generate a full MERGE response.  svn clients can ask us to
     suppress this walk by sending specific request headers. */
  if (! disable_merge_response)
    {
      /* Now we need to generate responses for all the resources which
         changed.  This is done through a delta of the two roots.

         Note that a directory is not marked when open_dir is seen
         (since it typically is used just for changing members in that
         directory); instead, we want for a property change (the only
         reason the client would need to fetch a new directory).

         ### we probably should say something about the dirs, so that
         ### we can pass back the new version URL */

      /* and go make me proud, boy! */
      serr = do_resources(repos, root, new_rev, output, bb, pool);
      if (serr != NULL)
        {
          return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                      "Error constructing resource list.",
                                      repos->pool);
        }
    }

  /* wrap up the merge response */
  (void) ap_fputs(output, bb,
                  "</D:updated-set>" DEBUG_CR
                  "</D:merge-response>" DEBUG_CR);

  /* send whatever is left in the brigade */
  (void) ap_pass_brigade(output, bb);

  return SVN_NO_ERROR;
}
static dav_prop_insert
insert_prop_internal(const dav_resource *resource,
                     int propid,
                     dav_prop_insert what,
                     apr_text_header *phdr,
                     apr_pool_t *scratch_pool,
                     apr_pool_t *result_pool)
{
  const char *value = NULL;
  const char *s;
  const dav_liveprop_spec *info;
  int global_ns;
  svn_error_t *serr;

  /*
  ** Almost none of the SVN provider properties are defined if the
  ** resource does not exist.  We do need to return the one VCC
  ** property and baseline-relative-path on lock-null resources,
  ** however, so that svn clients can run 'svn unlock' and 'svn info'
  ** on these things.
  **
  ** Even though we state that the SVN properties are not defined, the
  ** client cannot store dead values -- we deny that thru the is_writable
  ** hook function.
  */
  if ((! resource->exists)
      && (propid != DAV_PROPID_version_controlled_configuration)
      && (propid != SVN_PROPID_baseline_relative_path))
    return DAV_PROP_INSERT_NOTSUPP;

  /* ### we may want to respond to DAV_PROPID_resourcetype for PRIVATE
     ### resources. need to think on "proper" interaction with mod_dav */

  switch (propid)
    {
    case DAV_PROPID_getlastmodified:
    case DAV_PROPID_creationdate:
      {
        /* In subversion terms, the date attached to a file's CR is
           the true "last modified" time.  However, we're defining
           creationdate in the same way.  IMO, the "creationdate" is
           really the date attached to the revision in which the item
           *first* came into existence; this would found by tracing
           back through the log of the file -- probably via
           svn_fs_revisions_changed.  gstein, is it a bad thing that
           we're currently using 'creationdate' to mean the same thing
           as 'last modified date'?  */
        const char *datestring;
        apr_time_t timeval;
        enum time_format format;

        /* ### for now, our global VCC has no such property. */
        if (resource->type == DAV_RESOURCE_TYPE_PRIVATE
            && (resource->info->restype == DAV_SVN_RESTYPE_VCC
                || resource->info->restype == DAV_SVN_RESTYPE_ME))
          {
            return DAV_PROP_INSERT_NOTSUPP;
          }

        if (propid == DAV_PROPID_creationdate)
          {
            /* Return an ISO8601 date; this is what the svn client
               expects, and rfc2518 demands it. */
            format = time_format_iso8601;
          }
        else /* propid == DAV_PROPID_getlastmodified */
          {
            format = time_format_rfc1123;
          }

        if (0 != get_last_modified_time(&datestring, &timeval,
                                        resource, format, scratch_pool))
          {
            return DAV_PROP_INSERT_NOTDEF;
          }

        value = apr_xml_quote_string(scratch_pool, datestring, 1);
        break;
      }

    case DAV_PROPID_creator_displayname:
      {
        svn_revnum_t committed_rev = SVN_INVALID_REVNUM;
        svn_string_t *last_author = NULL;

        /* ### for now, our global VCC has no such property. */
        if (resource->type == DAV_RESOURCE_TYPE_PRIVATE
            && (resource->info->restype == DAV_SVN_RESTYPE_VCC
                || resource->info->restype == DAV_SVN_RESTYPE_ME))
          {
            return DAV_PROP_INSERT_NOTSUPP;
          }

        if (resource->baselined && resource->type == DAV_RESOURCE_TYPE_VERSION)
          {
            /* A baseline URI. */
            committed_rev = resource->info->root.rev;
          }
        else if (resource->type == DAV_RESOURCE_TYPE_REGULAR
                 || resource->type == DAV_RESOURCE_TYPE_WORKING
                 || resource->type == DAV_RESOURCE_TYPE_VERSION)
          {
            /* Get the CR field out of the node's skel.  Notice that the
               root object might be an ID root -or- a revision root. */
            serr = svn_fs_node_created_rev(&committed_rev,
                                           resource->info->root.root,
                                           resource->info->repos_path,
                                           scratch_pool);
            if (serr != NULL)
              {
                /* ### what to do? */
                svn_error_clear(serr);
                value = "###error###";
                break;
              }
          }
        else
          {
            return DAV_PROP_INSERT_NOTSUPP;
          }

        serr = get_path_revprop(&last_author,
                                resource,
                                committed_rev,
                                SVN_PROP_REVISION_AUTHOR,
                                scratch_pool);
        if (serr)
          {
            /* ### what to do? */
            svn_error_clear(serr);
            value = "###error###";
            break;
          }

        if (last_author == NULL)
          return DAV_PROP_INSERT_NOTDEF;

        value = apr_xml_quote_string(scratch_pool, last_author->data, 1);
        break;
      }

    case DAV_PROPID_getcontentlanguage:
      /* ### need something here */
      return DAV_PROP_INSERT_NOTSUPP;
      break;

    case DAV_PROPID_getcontentlength:
      {
        svn_filesize_t len = 0;

        /* our property, but not defined on collection resources */
        if (resource->collection || resource->baselined)
          return DAV_PROP_INSERT_NOTSUPP;

        serr = svn_fs_file_length(&len, resource->info->root.root,
                                  resource->info->repos_path, scratch_pool);
        if (serr != NULL)
          {
            svn_error_clear(serr);
            value = "0";  /* ### what to do? */
            break;
          }

        value = apr_psprintf(scratch_pool, "%" SVN_FILESIZE_T_FMT, len);
        break;
      }

    case DAV_PROPID_getcontenttype:
      {
        /* The subversion client assumes that any file without an
           svn:mime-type property is of type text/plain.  So it seems
           safe (and consistent) to assume the same on the server.  */
        svn_string_t *pval;
        const char *mime_type = NULL;

        if (resource->baselined && resource->type == DAV_RESOURCE_TYPE_VERSION)
          return DAV_PROP_INSERT_NOTSUPP;

        if (resource->type == DAV_RESOURCE_TYPE_PRIVATE
            && (resource->info->restype == DAV_SVN_RESTYPE_VCC
                || resource->info->restype == DAV_SVN_RESTYPE_ME))
          {
            return DAV_PROP_INSERT_NOTSUPP;
          }

        if (resource->collection) /* defaults for directories */
          {
            if (resource->info->repos->xslt_uri)
              mime_type = "text/xml";
            else
              mime_type = "text/html; charset=UTF-8";
          }
        else
          {
            if ((serr = svn_fs_node_prop(&pval, resource->info->root.root,
                                         resource->info->repos_path,
                                         SVN_PROP_MIME_TYPE, scratch_pool)))
              {
                svn_error_clear(serr);
                pval = NULL;
              }

            if (pval)
              mime_type = pval->data;
            else if ((! resource->info->repos->is_svn_client)
                     && resource->info->r->content_type)
              mime_type = resource->info->r->content_type;
            else
              mime_type = "text/plain";

            if ((serr = svn_mime_type_validate(mime_type, scratch_pool)))
              {
                /* Probably serr->apr == SVN_ERR_BAD_MIME_TYPE, but
                   there's no point even checking.  No matter what the
                   error is, we can't claim to have a mime type for
                   this resource. */
                ap_log_rerror(APLOG_MARK, APLOG_WARNING, serr->apr_err, 
                              resource->info->r, "%s", serr->message);
                svn_error_clear(serr);
                return DAV_PROP_INSERT_NOTDEF;
              }
          }

        value = mime_type;
        break;
      }

    case DAV_PROPID_getetag:
      if (resource->type == DAV_RESOURCE_TYPE_PRIVATE
          && (resource->info->restype == DAV_SVN_RESTYPE_VCC
              || resource->info->restype == DAV_SVN_RESTYPE_ME))
        {
          return DAV_PROP_INSERT_NOTSUPP;
        }

      value = dav_svn__getetag(resource, scratch_pool);
      break;

    case DAV_PROPID_auto_version:
      /* we only support one autoversioning behavior, and thus only
         return this one static value; someday when we support
         locking, there are other possible values/behaviors for this. */
      if (resource->info->repos->autoversioning)
        value = "DAV:checkout-checkin";
      else
        return DAV_PROP_INSERT_NOTDEF;
      break;

    case DAV_PROPID_baseline_collection:
      /* only defined for Baselines */
      /* ### whoops. also defined for a VCC. deal with it later. */
      if (resource->type != DAV_RESOURCE_TYPE_VERSION || !resource->baselined)
        return DAV_PROP_INSERT_NOTSUPP;
      value = dav_svn__build_uri(resource->info->repos, DAV_SVN__BUILD_URI_BC,
                                 resource->info->root.rev, NULL,
                                 1 /* add_href */, scratch_pool);
      break;

    case DAV_PROPID_checked_in:
      /* only defined for VCRs (in the public space and in a BC space) */
      /* ### note that a VCC (a special VCR) is defined as _PRIVATE for now */
      if (resource->type == DAV_RESOURCE_TYPE_PRIVATE
          && (resource->info->restype == DAV_SVN_RESTYPE_VCC
              || resource->info->restype == DAV_SVN_RESTYPE_ME))
        {
          svn_revnum_t revnum;

          serr = svn_fs_youngest_rev(&revnum, resource->info->repos->fs,
                                     scratch_pool);
          if (serr != NULL)
            {
              /* ### what to do? */
              svn_error_clear(serr);
              value = "###error###";
              break;
            }
          s = dav_svn__build_uri(resource->info->repos,
                                 DAV_SVN__BUILD_URI_BASELINE,
                                 revnum, NULL, 0 /* add_href */, scratch_pool);
          value = apr_psprintf(scratch_pool, "<D:href>%s</D:href>",
                               apr_xml_quote_string(scratch_pool, s, 1));
        }
      else if (resource->type != DAV_RESOURCE_TYPE_REGULAR)
        {
          /* not defined for this resource type */
          return DAV_PROP_INSERT_NOTSUPP;
        }
      else
        {
          svn_revnum_t rev_to_use =
            dav_svn__get_safe_cr(resource->info->root.root,
                                 resource->info->repos_path, scratch_pool);

          s = dav_svn__build_uri(resource->info->repos,
                                 DAV_SVN__BUILD_URI_VERSION,
                                 rev_to_use, resource->info->repos_path,
                                0 /* add_href */, scratch_pool);
          value = apr_psprintf(scratch_pool, "<D:href>%s</D:href>",
                               apr_xml_quote_string(scratch_pool, s, 1));
        }
      break;

    case DAV_PROPID_version_controlled_configuration:
      /* only defined for VCRs */
      /* ### VCRs within the BC should not have this property! */
      /* ### note that a VCC (a special VCR) is defined as _PRIVATE for now */
      if (resource->type != DAV_RESOURCE_TYPE_REGULAR)
        return DAV_PROP_INSERT_NOTSUPP;
      value = dav_svn__build_uri(resource->info->repos, DAV_SVN__BUILD_URI_VCC,
                                 SVN_IGNORED_REVNUM, NULL,
                                 1 /* add_href */, scratch_pool);
      break;

    case DAV_PROPID_version_name:
      /* only defined for Version Resources and Baselines */
      /* ### whoops. also defined for VCRs. deal with it later. */
      if ((resource->type != DAV_RESOURCE_TYPE_VERSION)
          && (! resource->versioned))
        return DAV_PROP_INSERT_NOTSUPP;

      if (resource->type == DAV_RESOURCE_TYPE_PRIVATE
          && (resource->info->restype == DAV_SVN_RESTYPE_VCC
              || resource->info->restype == DAV_SVN_RESTYPE_ME))
        {
          return DAV_PROP_INSERT_NOTSUPP;
        }

      if (resource->baselined)
        {
          /* just the revision number for baselines */
          value = apr_psprintf(scratch_pool, "%ld",
                               resource->info->root.rev);
        }
      else
        {
          svn_revnum_t committed_rev = SVN_INVALID_REVNUM;

          /* Get the CR field out of the node's skel.  Notice that the
             root object might be an ID root -or- a revision root. */
          serr = svn_fs_node_created_rev(&committed_rev,
                                         resource->info->root.root,
                                         resource->info->repos_path,
                                         scratch_pool);
          if (serr != NULL)
            {
              /* ### what to do? */
              svn_error_clear(serr);
              value = "###error###";
              break;
            }

          /* Convert the revision into a quoted string */
          s = apr_psprintf(scratch_pool, "%ld", committed_rev);
          value = apr_xml_quote_string(scratch_pool, s, 1);
        }
      break;

    case SVN_PROPID_baseline_relative_path:
      /* only defined for VCRs */
      /* ### VCRs within the BC should not have this property! */
      /* ### note that a VCC (a special VCR) is defined as _PRIVATE for now */
      if (resource->type != DAV_RESOURCE_TYPE_REGULAR)
        return DAV_PROP_INSERT_NOTSUPP;

      /* drop the leading slash, so it is relative */
      s = resource->info->repos_path + 1;
      value = apr_xml_quote_string(scratch_pool, s, 1);
      break;

    case SVN_PROPID_md5_checksum:
      if ((! resource->collection)
          && (! resource->baselined)
          && (resource->type == DAV_RESOURCE_TYPE_REGULAR
              || resource->type == DAV_RESOURCE_TYPE_WORKING
              || resource->type == DAV_RESOURCE_TYPE_VERSION))
        {
          svn_checksum_t *checksum;

          serr = svn_fs_file_checksum(&checksum, svn_checksum_md5,
                                      resource->info->root.root,
                                      resource->info->repos_path, TRUE,
                                      scratch_pool);
          if (serr != NULL)
            {
              /* ### what to do? */
              svn_error_clear(serr);
              value = "###error###";
              break;
            }

          value = svn_checksum_to_cstring(checksum, scratch_pool);

          if (! value)
            return DAV_PROP_INSERT_NOTSUPP;
        }
      else
        return DAV_PROP_INSERT_NOTSUPP;

      break;

    case SVN_PROPID_repository_uuid:
      serr = svn_fs_get_uuid(resource->info->repos->fs, &value, scratch_pool);
      if (serr != NULL)
        {
          /* ### what to do? */
          svn_error_clear(serr);
          value = "###error###";
          break;
        }
      break;

    case SVN_PROPID_deadprop_count:
      {
        unsigned int propcount;
        apr_hash_t *proplist;

        if (resource->type != DAV_RESOURCE_TYPE_REGULAR)
          return DAV_PROP_INSERT_NOTSUPP;

        serr = svn_fs_node_proplist(&proplist,
                                    resource->info->root.root,
                                    resource->info->repos_path, scratch_pool);
        if (serr != NULL)
          {
            /* ### what to do? */
            svn_error_clear(serr);
            value = "###error###";
            break;
          }

        propcount = apr_hash_count(proplist);
        value = apr_psprintf(scratch_pool, "%u", propcount);
        break;
      }

    default:
      /* ### what the heck was this property? */
      return DAV_PROP_INSERT_NOTDEF;
    }

  /* assert: value != NULL */

  /* get the information and global NS index for the property */
  global_ns = dav_get_liveprop_info(propid, &dav_svn__liveprop_group, &info);

  /* assert: info != NULL && info->name != NULL */

  if (what == DAV_PROP_INSERT_NAME
      || (what == DAV_PROP_INSERT_VALUE && *value == '\0')) {
    s = apr_psprintf(result_pool, "<lp%d:%s/>" DEBUG_CR, global_ns,
                     info->name);
  }
  else if (what == DAV_PROP_INSERT_VALUE) {
    s = apr_psprintf(result_pool, "<lp%d:%s>%s</lp%d:%s>" DEBUG_CR,
                     global_ns, info->name, value, global_ns, info->name);
  }
  else {
    /* assert: what == DAV_PROP_INSERT_SUPPORTED */
    s = apr_psprintf(result_pool,
                     "<D:supported-live-property D:name=\"%s\" "
                     "D:namespace=\"%s\"/>" DEBUG_CR,
                     info->name, namespace_uris[info->ns]);
  }
  apr_text_append(result_pool, phdr, s);

  /* we inserted whatever was asked for */
  return what;
}