1251881Speter/*
2251881Speter * property.c : property routines for ra_serf
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter
25251881Speter
26251881Speter#include <serf.h>
27251881Speter
28251881Speter#include "svn_hash.h"
29251881Speter#include "svn_path.h"
30251881Speter#include "svn_base64.h"
31251881Speter#include "svn_xml.h"
32251881Speter#include "svn_props.h"
33251881Speter#include "svn_dirent_uri.h"
34251881Speter
35251881Speter#include "private/svn_dav_protocol.h"
36251881Speter#include "private/svn_fspath.h"
37251881Speter#include "private/svn_string_private.h"
38251881Speter#include "svn_private_config.h"
39251881Speter
40251881Speter#include "ra_serf.h"
41251881Speter
42251881Speter
43251881Speter/* Our current parsing state we're in for the PROPFIND response. */
44251881Spetertypedef enum prop_state_e {
45251881Speter  INITIAL = 0,
46251881Speter  MULTISTATUS,
47251881Speter  RESPONSE,
48251881Speter  HREF,
49251881Speter  PROPSTAT,
50251881Speter  STATUS,
51251881Speter  PROP,
52251881Speter  PROPVAL,
53251881Speter  COLLECTION,
54251881Speter  HREF_VALUE
55251881Speter} prop_state_e;
56251881Speter
57251881Speter
58251881Speter/*
59251881Speter * This structure represents a pending PROPFIND response.
60251881Speter */
61251881Spetertypedef struct propfind_context_t {
62251881Speter  /* pool to issue allocations from */
63251881Speter  apr_pool_t *pool;
64251881Speter
65251881Speter  svn_ra_serf__handler_t *handler;
66251881Speter
67251881Speter  /* associated serf session */
68251881Speter  svn_ra_serf__session_t *sess;
69251881Speter  svn_ra_serf__connection_t *conn;
70251881Speter
71251881Speter  /* the requested path */
72251881Speter  const char *path;
73251881Speter
74251881Speter  /* the requested version (number and string form) */
75251881Speter  svn_revnum_t rev;
76251881Speter  const char *label;
77251881Speter
78251881Speter  /* the request depth */
79251881Speter  const char *depth;
80251881Speter
81251881Speter  /* the list of requested properties */
82251881Speter  const svn_ra_serf__dav_props_t *find_props;
83251881Speter
84251881Speter  /* hash table that will be updated with the properties
85251881Speter   *
86251881Speter   * This can be shared between multiple propfind_context_t
87251881Speter   * structures
88251881Speter   */
89251881Speter  apr_hash_t *ret_props;
90251881Speter
91251881Speter  /* hash table containing all the properties associated with the
92251881Speter   * "current" <propstat> tag.  These will get copied into RET_PROPS
93251881Speter   * if the status code similarly associated indicates that they are
94251881Speter   * "good"; otherwise, they'll get discarded.
95251881Speter   */
96251881Speter  apr_hash_t *ps_props;
97251881Speter
98251881Speter  /* If not-NULL, add us to this list when we're done. */
99251881Speter  svn_ra_serf__list_t **done_list;
100251881Speter
101251881Speter  svn_ra_serf__list_t done_item;
102251881Speter
103251881Speter} propfind_context_t;
104251881Speter
105251881Speter
106251881Speter#define D_ "DAV:"
107251881Speter#define S_ SVN_XML_NAMESPACE
108251881Speterstatic const svn_ra_serf__xml_transition_t propfind_ttable[] = {
109251881Speter  { INITIAL, D_, "multistatus", MULTISTATUS,
110251881Speter    FALSE, { NULL }, TRUE },
111251881Speter
112251881Speter  { MULTISTATUS, D_, "response", RESPONSE,
113251881Speter    FALSE, { NULL }, FALSE },
114251881Speter
115251881Speter  { RESPONSE, D_, "href", HREF,
116251881Speter    TRUE, { NULL }, TRUE },
117251881Speter
118251881Speter  { RESPONSE, D_, "propstat", PROPSTAT,
119251881Speter    FALSE, { NULL }, TRUE },
120251881Speter
121251881Speter  { PROPSTAT, D_, "status", STATUS,
122251881Speter    TRUE, { NULL }, TRUE },
123251881Speter
124251881Speter  { PROPSTAT, D_, "prop", PROP,
125251881Speter    FALSE, { NULL }, FALSE },
126251881Speter
127251881Speter  { PROP, "*", "*", PROPVAL,
128251881Speter    TRUE, { "?V:encoding", NULL }, TRUE },
129251881Speter
130251881Speter  { PROPVAL, D_, "collection", COLLECTION,
131251881Speter    FALSE, { NULL }, TRUE },
132251881Speter
133251881Speter  { PROPVAL, D_, "href", HREF_VALUE,
134251881Speter    TRUE, { NULL }, TRUE },
135251881Speter
136251881Speter  { 0 }
137251881Speter};
138251881Speter
139251881Speter
140251881Speter/* Return the HTTP status code contained in STATUS_LINE, or 0 if
141251881Speter   there's a problem parsing it. */
142251881Speterstatic int parse_status_code(const char *status_line)
143251881Speter{
144251881Speter  /* STATUS_LINE should be of form: "HTTP/1.1 200 OK" */
145251881Speter  if (status_line[0] == 'H' &&
146251881Speter      status_line[1] == 'T' &&
147251881Speter      status_line[2] == 'T' &&
148251881Speter      status_line[3] == 'P' &&
149251881Speter      status_line[4] == '/' &&
150251881Speter      (status_line[5] >= '0' && status_line[5] <= '9') &&
151251881Speter      status_line[6] == '.' &&
152251881Speter      (status_line[7] >= '0' && status_line[7] <= '9') &&
153251881Speter      status_line[8] == ' ')
154251881Speter    {
155251881Speter      char *reason;
156251881Speter
157251881Speter      return apr_strtoi64(status_line + 8, &reason, 10);
158251881Speter    }
159251881Speter  return 0;
160251881Speter}
161251881Speter
162251881Speter
163251881Speter/* Conforms to svn_ra_serf__path_rev_walker_t  */
164251881Speterstatic svn_error_t *
165251881Spetercopy_into_ret_props(void *baton,
166251881Speter                    const char *path, apr_ssize_t path_len,
167251881Speter                    const char *ns, apr_ssize_t ns_len,
168251881Speter                    const char *name, apr_ssize_t name_len,
169251881Speter                    const svn_string_t *val,
170251881Speter                    apr_pool_t *pool)
171251881Speter{
172251881Speter  propfind_context_t *ctx = baton;
173251881Speter
174251881Speter  svn_ra_serf__set_ver_prop(ctx->ret_props, path, ctx->rev, ns, name,
175251881Speter                            val, ctx->pool);
176251881Speter  return SVN_NO_ERROR;
177251881Speter}
178251881Speter
179251881Speter
180251881Speter/* Conforms to svn_ra_serf__xml_opened_t  */
181251881Speterstatic svn_error_t *
182251881Speterpropfind_opened(svn_ra_serf__xml_estate_t *xes,
183251881Speter                void *baton,
184251881Speter                int entered_state,
185251881Speter                const svn_ra_serf__dav_props_t *tag,
186251881Speter                apr_pool_t *scratch_pool)
187251881Speter{
188251881Speter  propfind_context_t *ctx = baton;
189251881Speter
190251881Speter  if (entered_state == PROPVAL)
191251881Speter    {
192251881Speter      svn_ra_serf__xml_note(xes, PROPVAL, "ns", tag->namespace);
193251881Speter      svn_ra_serf__xml_note(xes, PROPVAL, "name", tag->name);
194251881Speter    }
195251881Speter  else if (entered_state == PROPSTAT)
196251881Speter    {
197251881Speter      ctx->ps_props = apr_hash_make(ctx->pool);
198251881Speter    }
199251881Speter
200251881Speter  return SVN_NO_ERROR;
201251881Speter}
202251881Speter
203251881Speter
204251881Speter/* Conforms to svn_ra_serf__xml_closed_t  */
205251881Speterstatic svn_error_t *
206251881Speterpropfind_closed(svn_ra_serf__xml_estate_t *xes,
207251881Speter                void *baton,
208251881Speter                int leaving_state,
209251881Speter                const svn_string_t *cdata,
210251881Speter                apr_hash_t *attrs,
211251881Speter                apr_pool_t *scratch_pool)
212251881Speter{
213251881Speter  propfind_context_t *ctx = baton;
214251881Speter
215251881Speter  if (leaving_state == MULTISTATUS)
216251881Speter    {
217251881Speter      /* We've gathered all the data from the reponse. Add this item
218251881Speter         onto the "done list". External callers will then know this
219251881Speter         request has been completed (tho stray response bytes may still
220251881Speter         arrive).  */
221251881Speter      if (ctx->done_list)
222251881Speter        {
223251881Speter          ctx->done_item.data = ctx->handler;
224251881Speter          ctx->done_item.next = *ctx->done_list;
225251881Speter          *ctx->done_list = &ctx->done_item;
226251881Speter        }
227251881Speter    }
228251881Speter  else if (leaving_state == HREF)
229251881Speter    {
230251881Speter      const char *path;
231251881Speter      const svn_string_t *val_str;
232251881Speter
233251881Speter      if (strcmp(ctx->depth, "1") == 0)
234251881Speter        path = svn_urlpath__canonicalize(cdata->data, scratch_pool);
235251881Speter      else
236251881Speter        path = ctx->path;
237251881Speter
238251881Speter      svn_ra_serf__xml_note(xes, RESPONSE, "path", path);
239251881Speter
240251881Speter      /* Copy the value into the right pool, then save the HREF.  */
241251881Speter      val_str = svn_string_dup(cdata, ctx->pool);
242251881Speter      svn_ra_serf__set_ver_prop(ctx->ret_props,
243251881Speter                                path, ctx->rev, D_, "href", val_str,
244251881Speter                                ctx->pool);
245251881Speter    }
246251881Speter  else if (leaving_state == COLLECTION)
247251881Speter    {
248251881Speter      svn_ra_serf__xml_note(xes, PROPVAL, "altvalue", "collection");
249251881Speter    }
250251881Speter  else if (leaving_state == HREF_VALUE)
251251881Speter    {
252251881Speter      svn_ra_serf__xml_note(xes, PROPVAL, "altvalue", cdata->data);
253251881Speter    }
254251881Speter  else if (leaving_state == STATUS)
255251881Speter    {
256251881Speter      /* Parse the status field, and remember if this is a property
257251881Speter         that we wish to ignore.  (Typically, if it's not a 200, the
258251881Speter         status will be 404 to indicate that a property we
259251881Speter         specifically requested from the server doesn't exist.)  */
260251881Speter      int status = parse_status_code(cdata->data);
261251881Speter      if (status != 200)
262251881Speter        svn_ra_serf__xml_note(xes, PROPSTAT, "ignore-prop", "*");
263251881Speter    }
264251881Speter  else if (leaving_state == PROPVAL)
265251881Speter    {
266251881Speter      const char *encoding = svn_hash_gets(attrs, "V:encoding");
267251881Speter      const svn_string_t *val_str;
268251881Speter      apr_hash_t *gathered;
269251881Speter      const char *path;
270251881Speter      const char *ns;
271251881Speter      const char *name;
272251881Speter      const char *altvalue;
273251881Speter
274251881Speter      if (encoding)
275251881Speter        {
276251881Speter          if (strcmp(encoding, "base64") != 0)
277251881Speter            return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA,
278251881Speter                                     NULL,
279251881Speter                                     _("Got unrecognized encoding '%s'"),
280251881Speter                                     encoding);
281251881Speter
282251881Speter          /* Decode into the right pool.  */
283251881Speter          val_str = svn_base64_decode_string(cdata, ctx->pool);
284251881Speter        }
285251881Speter      else
286251881Speter        {
287251881Speter          /* Copy into the right pool.  */
288251881Speter          val_str = svn_string_dup(cdata, ctx->pool);
289251881Speter        }
290251881Speter
291251881Speter      /* The current path sits on the RESPONSE state. Gather up all the
292251881Speter         state from this PROPVAL to the (grandparent) RESPONSE state,
293251881Speter         and grab the path from there.
294251881Speter
295251881Speter         Now, it would be nice if we could, at this point, know that
296251881Speter         the status code for this property indicated a problem -- then
297251881Speter         we could simply bail out here and ignore the property.
298251881Speter         Sadly, though, we might get the status code *after* we get
299251881Speter         the property value.  So we'll carry on with our processing
300251881Speter         here, setting the property and value as expected.  Once we
301251881Speter         know for sure the status code associate with the property,
302251881Speter         we'll decide its fate.  */
303251881Speter      gathered = svn_ra_serf__xml_gather_since(xes, RESPONSE);
304251881Speter
305251881Speter      /* These will be dup'd into CTX->POOL, as necessary.  */
306251881Speter      path = svn_hash_gets(gathered, "path");
307251881Speter      if (path == NULL)
308251881Speter        path = ctx->path;
309251881Speter
310251881Speter      ns = svn_hash_gets(attrs, "ns");
311251881Speter      name = apr_pstrdup(ctx->pool,
312251881Speter                         svn_hash_gets(attrs, "name"));
313251881Speter
314251881Speter      altvalue = svn_hash_gets(attrs, "altvalue");
315251881Speter      if (altvalue != NULL)
316251881Speter        val_str = svn_string_create(altvalue, ctx->pool);
317251881Speter
318251881Speter      svn_ra_serf__set_ver_prop(ctx->ps_props,
319251881Speter                                path, ctx->rev, ns, name, val_str,
320251881Speter                                ctx->pool);
321251881Speter    }
322251881Speter  else
323251881Speter    {
324251881Speter      apr_hash_t *gathered;
325251881Speter
326251881Speter      SVN_ERR_ASSERT(leaving_state == PROPSTAT);
327251881Speter
328251881Speter      gathered = svn_ra_serf__xml_gather_since(xes, PROPSTAT);
329251881Speter
330251881Speter      /* If we've squirreled away a note that says we want to ignore
331251881Speter         these properties, we'll do so.  Otherwise, we need to copy
332251881Speter         them from the temporary hash into the ctx->ret_props hash. */
333251881Speter      if (! svn_hash_gets(gathered, "ignore-prop"))
334251881Speter        {
335251881Speter          SVN_ERR(svn_ra_serf__walk_all_paths(ctx->ps_props, ctx->rev,
336251881Speter                                              copy_into_ret_props, ctx,
337251881Speter                                              scratch_pool));
338251881Speter        }
339251881Speter
340251881Speter      ctx->ps_props = NULL;
341251881Speter    }
342251881Speter
343251881Speter  return SVN_NO_ERROR;
344251881Speter}
345251881Speter
346251881Speter
347251881Speterconst svn_string_t *
348251881Spetersvn_ra_serf__get_ver_prop_string(apr_hash_t *props,
349251881Speter                                 const char *path,
350251881Speter                                 svn_revnum_t rev,
351251881Speter                                 const char *ns,
352251881Speter                                 const char *name)
353251881Speter{
354251881Speter  apr_hash_t *ver_props, *path_props, *ns_props;
355251881Speter  void *val = NULL;
356251881Speter
357251881Speter  ver_props = apr_hash_get(props, &rev, sizeof(rev));
358251881Speter  if (ver_props)
359251881Speter    {
360251881Speter      path_props = svn_hash_gets(ver_props, path);
361251881Speter
362251881Speter      if (path_props)
363251881Speter        {
364251881Speter          ns_props = svn_hash_gets(path_props, ns);
365251881Speter          if (ns_props)
366251881Speter            {
367251881Speter              val = svn_hash_gets(ns_props, name);
368251881Speter            }
369251881Speter        }
370251881Speter    }
371251881Speter
372251881Speter  return val;
373251881Speter}
374251881Speter
375251881Speterconst char *
376251881Spetersvn_ra_serf__get_ver_prop(apr_hash_t *props,
377251881Speter                          const char *path,
378251881Speter                          svn_revnum_t rev,
379251881Speter                          const char *ns,
380251881Speter                          const char *name)
381251881Speter{
382251881Speter  const svn_string_t *val;
383251881Speter
384251881Speter  val = svn_ra_serf__get_ver_prop_string(props, path, rev, ns, name);
385251881Speter
386251881Speter  if (val)
387251881Speter    {
388251881Speter      return val->data;
389251881Speter    }
390251881Speter
391251881Speter  return NULL;
392251881Speter}
393251881Speter
394251881Speterconst svn_string_t *
395251881Spetersvn_ra_serf__get_prop_string(apr_hash_t *props,
396251881Speter                             const char *path,
397251881Speter                             const char *ns,
398251881Speter                             const char *name)
399251881Speter{
400251881Speter  return svn_ra_serf__get_ver_prop_string(props, path, SVN_INVALID_REVNUM,
401251881Speter                                          ns, name);
402251881Speter}
403251881Speter
404251881Speterconst char *
405251881Spetersvn_ra_serf__get_prop(apr_hash_t *props,
406251881Speter                      const char *path,
407251881Speter                      const char *ns,
408251881Speter                      const char *name)
409251881Speter{
410251881Speter  return svn_ra_serf__get_ver_prop(props, path, SVN_INVALID_REVNUM, ns, name);
411251881Speter}
412251881Speter
413251881Spetervoid
414251881Spetersvn_ra_serf__set_ver_prop(apr_hash_t *props,
415251881Speter                          const char *path, svn_revnum_t rev,
416251881Speter                          const char *ns, const char *name,
417251881Speter                          const svn_string_t *val, apr_pool_t *pool)
418251881Speter{
419251881Speter  apr_hash_t *ver_props, *path_props, *ns_props;
420251881Speter
421251881Speter  ver_props = apr_hash_get(props, &rev, sizeof(rev));
422251881Speter  if (!ver_props)
423251881Speter    {
424251881Speter      ver_props = apr_hash_make(pool);
425251881Speter      apr_hash_set(props, apr_pmemdup(pool, &rev, sizeof(rev)), sizeof(rev),
426251881Speter                   ver_props);
427251881Speter    }
428251881Speter
429251881Speter  path_props = svn_hash_gets(ver_props, path);
430251881Speter
431251881Speter  if (!path_props)
432251881Speter    {
433251881Speter      path_props = apr_hash_make(pool);
434251881Speter      path = apr_pstrdup(pool, path);
435251881Speter      svn_hash_sets(ver_props, path, path_props);
436251881Speter
437251881Speter      /* todo: we know that we'll fail the next check, but fall through
438251881Speter       * for now for simplicity's sake.
439251881Speter       */
440251881Speter    }
441251881Speter
442251881Speter  ns_props = svn_hash_gets(path_props, ns);
443251881Speter  if (!ns_props)
444251881Speter    {
445251881Speter      ns_props = apr_hash_make(pool);
446251881Speter      ns = apr_pstrdup(pool, ns);
447251881Speter      svn_hash_sets(path_props, ns, ns_props);
448251881Speter    }
449251881Speter
450251881Speter  svn_hash_sets(ns_props, name, val);
451251881Speter}
452251881Speter
453251881Spetervoid
454251881Spetersvn_ra_serf__set_prop(apr_hash_t *props,
455251881Speter                      const char *path,
456251881Speter                      const char *ns, const char *name,
457251881Speter                      const svn_string_t *val, apr_pool_t *pool)
458251881Speter{
459251881Speter  svn_ra_serf__set_ver_prop(props, path, SVN_INVALID_REVNUM, ns, name,
460251881Speter                            val, pool);
461251881Speter}
462251881Speter
463251881Speter
464251881Speterstatic svn_error_t *
465251881Spetersetup_propfind_headers(serf_bucket_t *headers,
466251881Speter                        void *setup_baton,
467251881Speter                        apr_pool_t *pool)
468251881Speter{
469251881Speter  propfind_context_t *ctx = setup_baton;
470251881Speter
471251881Speter  serf_bucket_headers_setn(headers, "Depth", ctx->depth);
472251881Speter  if (ctx->label)
473251881Speter    {
474251881Speter      serf_bucket_headers_setn(headers, "Label", ctx->label);
475251881Speter    }
476251881Speter
477251881Speter  return SVN_NO_ERROR;
478251881Speter}
479251881Speter
480251881Speter#define PROPFIND_HEADER "<?xml version=\"1.0\" encoding=\"utf-8\"?><propfind xmlns=\"DAV:\">"
481251881Speter#define PROPFIND_TRAILER "</propfind>"
482251881Speter
483251881Speterstatic svn_error_t *
484251881Spetercreate_propfind_body(serf_bucket_t **bkt,
485251881Speter                     void *setup_baton,
486251881Speter                     serf_bucket_alloc_t *alloc,
487251881Speter                     apr_pool_t *pool)
488251881Speter{
489251881Speter  propfind_context_t *ctx = setup_baton;
490251881Speter
491251881Speter  serf_bucket_t *body_bkt, *tmp;
492251881Speter  const svn_ra_serf__dav_props_t *prop;
493251881Speter  svn_boolean_t requested_allprop = FALSE;
494251881Speter
495251881Speter  body_bkt = serf_bucket_aggregate_create(alloc);
496251881Speter
497251881Speter  prop = ctx->find_props;
498251881Speter  while (prop && prop->namespace)
499251881Speter    {
500251881Speter      /* special case the allprop case. */
501251881Speter      if (strcmp(prop->name, "allprop") == 0)
502251881Speter        {
503251881Speter          requested_allprop = TRUE;
504251881Speter        }
505251881Speter
506251881Speter      /* <*propname* xmlns="*propns*" /> */
507251881Speter      tmp = SERF_BUCKET_SIMPLE_STRING_LEN("<", 1, alloc);
508251881Speter      serf_bucket_aggregate_append(body_bkt, tmp);
509251881Speter
510251881Speter      tmp = SERF_BUCKET_SIMPLE_STRING(prop->name, alloc);
511251881Speter      serf_bucket_aggregate_append(body_bkt, tmp);
512251881Speter
513251881Speter      tmp = SERF_BUCKET_SIMPLE_STRING_LEN(" xmlns=\"",
514251881Speter                                          sizeof(" xmlns=\"")-1,
515251881Speter                                          alloc);
516251881Speter      serf_bucket_aggregate_append(body_bkt, tmp);
517251881Speter
518251881Speter      tmp = SERF_BUCKET_SIMPLE_STRING(prop->namespace, alloc);
519251881Speter      serf_bucket_aggregate_append(body_bkt, tmp);
520251881Speter
521251881Speter      tmp = SERF_BUCKET_SIMPLE_STRING_LEN("\"/>", sizeof("\"/>")-1,
522251881Speter                                          alloc);
523251881Speter      serf_bucket_aggregate_append(body_bkt, tmp);
524251881Speter
525251881Speter      prop++;
526251881Speter    }
527251881Speter
528251881Speter  /* If we're not doing an allprop, add <prop> tags. */
529251881Speter  if (!requested_allprop)
530251881Speter    {
531251881Speter      tmp = SERF_BUCKET_SIMPLE_STRING_LEN("<prop>",
532251881Speter                                          sizeof("<prop>")-1,
533251881Speter                                          alloc);
534251881Speter      serf_bucket_aggregate_prepend(body_bkt, tmp);
535251881Speter    }
536251881Speter
537251881Speter  tmp = SERF_BUCKET_SIMPLE_STRING_LEN(PROPFIND_HEADER,
538251881Speter                                      sizeof(PROPFIND_HEADER)-1,
539251881Speter                                      alloc);
540251881Speter
541251881Speter  serf_bucket_aggregate_prepend(body_bkt, tmp);
542251881Speter
543251881Speter  if (!requested_allprop)
544251881Speter    {
545251881Speter      tmp = SERF_BUCKET_SIMPLE_STRING_LEN("</prop>",
546251881Speter                                          sizeof("</prop>")-1,
547251881Speter                                          alloc);
548251881Speter      serf_bucket_aggregate_append(body_bkt, tmp);
549251881Speter    }
550251881Speter
551251881Speter  tmp = SERF_BUCKET_SIMPLE_STRING_LEN(PROPFIND_TRAILER,
552251881Speter                                      sizeof(PROPFIND_TRAILER)-1,
553251881Speter                                      alloc);
554251881Speter  serf_bucket_aggregate_append(body_bkt, tmp);
555251881Speter
556251881Speter  *bkt = body_bkt;
557251881Speter  return SVN_NO_ERROR;
558251881Speter}
559251881Speter
560251881Speter
561251881Spetersvn_error_t *
562251881Spetersvn_ra_serf__deliver_props(svn_ra_serf__handler_t **propfind_handler,
563251881Speter                           apr_hash_t *ret_props,
564251881Speter                           svn_ra_serf__session_t *sess,
565251881Speter                           svn_ra_serf__connection_t *conn,
566251881Speter                           const char *path,
567251881Speter                           svn_revnum_t rev,
568251881Speter                           const char *depth,
569251881Speter                           const svn_ra_serf__dav_props_t *find_props,
570251881Speter                           svn_ra_serf__list_t **done_list,
571251881Speter                           apr_pool_t *pool)
572251881Speter{
573251881Speter  propfind_context_t *new_prop_ctx;
574251881Speter  svn_ra_serf__handler_t *handler;
575251881Speter  svn_ra_serf__xml_context_t *xmlctx;
576251881Speter
577251881Speter  new_prop_ctx = apr_pcalloc(pool, sizeof(*new_prop_ctx));
578251881Speter
579251881Speter  new_prop_ctx->pool = apr_hash_pool_get(ret_props);
580251881Speter  new_prop_ctx->path = path;
581251881Speter  new_prop_ctx->find_props = find_props;
582251881Speter  new_prop_ctx->ret_props = ret_props;
583251881Speter  new_prop_ctx->depth = depth;
584251881Speter  new_prop_ctx->sess = sess;
585251881Speter  new_prop_ctx->conn = conn;
586251881Speter  new_prop_ctx->rev = rev;
587251881Speter  new_prop_ctx->done_list = done_list;
588251881Speter
589251881Speter  if (SVN_IS_VALID_REVNUM(rev))
590251881Speter    {
591251881Speter      new_prop_ctx->label = apr_ltoa(pool, rev);
592251881Speter    }
593251881Speter  else
594251881Speter    {
595251881Speter      new_prop_ctx->label = NULL;
596251881Speter    }
597251881Speter
598251881Speter  xmlctx = svn_ra_serf__xml_context_create(propfind_ttable,
599251881Speter                                           propfind_opened,
600251881Speter                                           propfind_closed,
601251881Speter                                           NULL,
602251881Speter                                           new_prop_ctx,
603251881Speter                                           pool);
604251881Speter  handler = svn_ra_serf__create_expat_handler(xmlctx, pool);
605251881Speter
606251881Speter  handler->method = "PROPFIND";
607251881Speter  handler->path = path;
608251881Speter  handler->body_delegate = create_propfind_body;
609251881Speter  handler->body_type = "text/xml";
610251881Speter  handler->body_delegate_baton = new_prop_ctx;
611251881Speter  handler->header_delegate = setup_propfind_headers;
612251881Speter  handler->header_delegate_baton = new_prop_ctx;
613251881Speter
614251881Speter  handler->session = new_prop_ctx->sess;
615251881Speter  handler->conn = new_prop_ctx->conn;
616251881Speter
617251881Speter  new_prop_ctx->handler = handler;
618251881Speter
619251881Speter  *propfind_handler = handler;
620251881Speter
621251881Speter  return SVN_NO_ERROR;
622251881Speter}
623251881Speter
624251881Speter
625251881Speter/*
626251881Speter * This helper function will block until the PROP_CTX indicates that is done
627251881Speter * or another error is returned.
628251881Speter */
629251881Spetersvn_error_t *
630251881Spetersvn_ra_serf__wait_for_props(svn_ra_serf__handler_t *handler,
631251881Speter                            apr_pool_t *scratch_pool)
632251881Speter{
633251881Speter  svn_error_t *err;
634251881Speter  svn_error_t *err2;
635251881Speter
636251881Speter  err = svn_ra_serf__context_run_one(handler, scratch_pool);
637251881Speter
638253734Speter  err2 = svn_ra_serf__error_on_status(handler->sline,
639251881Speter                                      handler->path,
640251881Speter                                      handler->location);
641251881Speter
642251881Speter  return svn_error_compose_create(err2, err);
643251881Speter}
644251881Speter
645251881Speter/*
646251881Speter * This is a blocking version of deliver_props.
647251881Speter */
648251881Spetersvn_error_t *
649251881Spetersvn_ra_serf__retrieve_props(apr_hash_t **results,
650251881Speter                            svn_ra_serf__session_t *sess,
651251881Speter                            svn_ra_serf__connection_t *conn,
652251881Speter                            const char *url,
653251881Speter                            svn_revnum_t rev,
654251881Speter                            const char *depth,
655251881Speter                            const svn_ra_serf__dav_props_t *props,
656251881Speter                            apr_pool_t *result_pool,
657251881Speter                            apr_pool_t *scratch_pool)
658251881Speter{
659251881Speter  svn_ra_serf__handler_t *handler;
660251881Speter
661251881Speter  *results = apr_hash_make(result_pool);
662251881Speter
663251881Speter  SVN_ERR(svn_ra_serf__deliver_props(&handler, *results, sess, conn, url,
664251881Speter                                     rev, depth, props, NULL, result_pool));
665251881Speter  SVN_ERR(svn_ra_serf__wait_for_props(handler, scratch_pool));
666251881Speter
667251881Speter  return SVN_NO_ERROR;
668251881Speter}
669251881Speter
670251881Speter
671251881Spetersvn_error_t *
672251881Spetersvn_ra_serf__fetch_node_props(apr_hash_t **results,
673251881Speter                              svn_ra_serf__connection_t *conn,
674251881Speter                              const char *url,
675251881Speter                              svn_revnum_t revision,
676251881Speter                              const svn_ra_serf__dav_props_t *which_props,
677251881Speter                              apr_pool_t *result_pool,
678251881Speter                              apr_pool_t *scratch_pool)
679251881Speter{
680251881Speter  apr_hash_t *multiprops;
681251881Speter  apr_hash_t *ver_props;
682251881Speter
683251881Speter  /* Note: a couple extra hash tables and whatnot get into RESULT_POOL.
684251881Speter     Not a big deal at this point. Theoretically, we could fetch all
685251881Speter     props into SCRATCH_POOL, then copy just the REVISION/URL props
686251881Speter     into RESULT_POOL. Too much work for too little gain...  */
687251881Speter  SVN_ERR(svn_ra_serf__retrieve_props(&multiprops, conn->session, conn,
688251881Speter                                      url, revision, "0", which_props,
689251881Speter                                      result_pool, scratch_pool));
690251881Speter
691251881Speter  ver_props = apr_hash_get(multiprops, &revision, sizeof(revision));
692251881Speter  if (ver_props != NULL)
693251881Speter    {
694251881Speter      *results = svn_hash_gets(ver_props, url);
695251881Speter      if (*results != NULL)
696251881Speter        return SVN_NO_ERROR;
697251881Speter    }
698251881Speter
699251881Speter  return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL,
700251881Speter                          _("The PROPFIND response did not include "
701251881Speter                            "the requested properties"));
702251881Speter}
703251881Speter
704251881Speter
705251881Spetersvn_error_t *
706251881Spetersvn_ra_serf__walk_node_props(apr_hash_t *props,
707251881Speter                             svn_ra_serf__walker_visitor_t walker,
708251881Speter                             void *baton,
709251881Speter                             apr_pool_t *scratch_pool)
710251881Speter{
711251881Speter  apr_pool_t *iterpool;
712251881Speter  apr_hash_index_t *ns_hi;
713251881Speter
714251881Speter  iterpool = svn_pool_create(scratch_pool);
715251881Speter  for (ns_hi = apr_hash_first(scratch_pool, props); ns_hi;
716251881Speter       ns_hi = apr_hash_next(ns_hi))
717251881Speter    {
718251881Speter      void *ns_val;
719251881Speter      const void *ns_name;
720251881Speter      apr_hash_index_t *name_hi;
721251881Speter
722251881Speter      /* NOTE: We do not clear ITERPOOL in this loop. Generally, there are
723251881Speter           very few namespaces, so this loop will not have many iterations.
724251881Speter           Instead, ITERPOOL is used for the inner loop.  */
725251881Speter
726251881Speter      apr_hash_this(ns_hi, &ns_name, NULL, &ns_val);
727251881Speter
728251881Speter      for (name_hi = apr_hash_first(scratch_pool, ns_val); name_hi;
729251881Speter           name_hi = apr_hash_next(name_hi))
730251881Speter        {
731251881Speter          void *prop_val;
732251881Speter          const void *prop_name;
733251881Speter
734251881Speter          /* See note above, regarding clearing of this pool.  */
735251881Speter          svn_pool_clear(iterpool);
736251881Speter
737251881Speter          apr_hash_this(name_hi, &prop_name, NULL, &prop_val);
738251881Speter
739251881Speter          SVN_ERR(walker(baton, ns_name, prop_name, prop_val, iterpool));
740251881Speter        }
741251881Speter    }
742251881Speter  svn_pool_destroy(iterpool);
743251881Speter
744251881Speter  return SVN_NO_ERROR;
745251881Speter}
746251881Speter
747251881Speter
748251881Spetersvn_error_t *
749251881Spetersvn_ra_serf__walk_all_props(apr_hash_t *props,
750251881Speter                            const char *name,
751251881Speter                            svn_revnum_t rev,
752251881Speter                            svn_ra_serf__walker_visitor_t walker,
753251881Speter                            void *baton,
754251881Speter                            apr_pool_t *scratch_pool)
755251881Speter{
756251881Speter  apr_hash_t *ver_props;
757251881Speter  apr_hash_t *path_props;
758251881Speter
759251881Speter  ver_props = apr_hash_get(props, &rev, sizeof(rev));
760251881Speter  if (!ver_props)
761251881Speter    return SVN_NO_ERROR;
762251881Speter
763251881Speter  path_props = svn_hash_gets(ver_props, name);
764251881Speter  if (!path_props)
765251881Speter    return SVN_NO_ERROR;
766251881Speter
767251881Speter  return svn_error_trace(svn_ra_serf__walk_node_props(path_props,
768251881Speter                                                      walker, baton,
769251881Speter                                                      scratch_pool));
770251881Speter}
771251881Speter
772251881Speter
773251881Spetersvn_error_t *
774251881Spetersvn_ra_serf__walk_all_paths(apr_hash_t *props,
775251881Speter                            svn_revnum_t rev,
776251881Speter                            svn_ra_serf__path_rev_walker_t walker,
777251881Speter                            void *baton,
778251881Speter                            apr_pool_t *pool)
779251881Speter{
780251881Speter  apr_hash_index_t *path_hi;
781251881Speter  apr_hash_t *ver_props;
782251881Speter
783251881Speter  ver_props = apr_hash_get(props, &rev, sizeof(rev));
784251881Speter
785251881Speter  if (!ver_props)
786251881Speter    {
787251881Speter      return SVN_NO_ERROR;
788251881Speter    }
789251881Speter
790251881Speter  for (path_hi = apr_hash_first(pool, ver_props); path_hi;
791251881Speter       path_hi = apr_hash_next(path_hi))
792251881Speter    {
793251881Speter      void *path_props;
794251881Speter      const void *path_name;
795251881Speter      apr_ssize_t path_len;
796251881Speter      apr_hash_index_t *ns_hi;
797251881Speter
798251881Speter      apr_hash_this(path_hi, &path_name, &path_len, &path_props);
799251881Speter      for (ns_hi = apr_hash_first(pool, path_props); ns_hi;
800251881Speter           ns_hi = apr_hash_next(ns_hi))
801251881Speter        {
802251881Speter          void *ns_val;
803251881Speter          const void *ns_name;
804251881Speter          apr_ssize_t ns_len;
805251881Speter          apr_hash_index_t *name_hi;
806251881Speter          apr_hash_this(ns_hi, &ns_name, &ns_len, &ns_val);
807251881Speter          for (name_hi = apr_hash_first(pool, ns_val); name_hi;
808251881Speter               name_hi = apr_hash_next(name_hi))
809251881Speter            {
810251881Speter              void *prop_val;
811251881Speter              const void *prop_name;
812251881Speter              apr_ssize_t prop_len;
813251881Speter
814251881Speter              apr_hash_this(name_hi, &prop_name, &prop_len, &prop_val);
815251881Speter              /* use a subpool? */
816251881Speter              SVN_ERR(walker(baton, path_name, path_len, ns_name, ns_len,
817251881Speter                             prop_name, prop_len, prop_val, pool));
818251881Speter            }
819251881Speter        }
820251881Speter    }
821251881Speter
822251881Speter  return SVN_NO_ERROR;
823251881Speter}
824251881Speter
825251881Speter
826251881Speterconst char *
827251881Spetersvn_ra_serf__svnname_from_wirename(const char *ns,
828251881Speter                                   const char *name,
829251881Speter                                   apr_pool_t *result_pool)
830251881Speter{
831251881Speter  if (*ns == '\0' || strcmp(ns, SVN_DAV_PROP_NS_CUSTOM) == 0)
832251881Speter    return apr_pstrdup(result_pool, name);
833251881Speter
834251881Speter  if (strcmp(ns, SVN_DAV_PROP_NS_SVN) == 0)
835251881Speter    return apr_pstrcat(result_pool, SVN_PROP_PREFIX, name, (char *)NULL);
836251881Speter
837251881Speter  if (strcmp(ns, SVN_PROP_PREFIX) == 0)
838251881Speter    return apr_pstrcat(result_pool, SVN_PROP_PREFIX, name, (char *)NULL);
839251881Speter
840251881Speter  if (strcmp(name, SVN_DAV__VERSION_NAME) == 0)
841251881Speter    return SVN_PROP_ENTRY_COMMITTED_REV;
842251881Speter
843251881Speter  if (strcmp(name, SVN_DAV__CREATIONDATE) == 0)
844251881Speter    return SVN_PROP_ENTRY_COMMITTED_DATE;
845251881Speter
846251881Speter  if (strcmp(name, "creator-displayname") == 0)
847251881Speter    return SVN_PROP_ENTRY_LAST_AUTHOR;
848251881Speter
849251881Speter  if (strcmp(name, "repository-uuid") == 0)
850251881Speter    return SVN_PROP_ENTRY_UUID;
851251881Speter
852251881Speter  if (strcmp(name, "lock-token") == 0)
853251881Speter    return SVN_PROP_ENTRY_LOCK_TOKEN;
854251881Speter
855251881Speter  if (strcmp(name, "checked-in") == 0)
856251881Speter    return SVN_RA_SERF__WC_CHECKED_IN_URL;
857251881Speter
858251881Speter  if (strcmp(ns, "DAV:") == 0 || strcmp(ns, SVN_DAV_PROP_NS_DAV) == 0)
859251881Speter    {
860251881Speter      /* Here DAV: properties not yet converted to svn: properties should be
861251881Speter         ignored. */
862251881Speter      return NULL;
863251881Speter    }
864251881Speter
865251881Speter  /* An unknown namespace, must be a custom property. */
866251881Speter  return apr_pstrcat(result_pool, ns, name, (char *)NULL);
867251881Speter}
868251881Speter
869251881Speter
870251881Speter/* Conforms to svn_ra_serf__walker_visitor_t  */
871251881Speterstatic svn_error_t *
872251881Speterset_flat_props(void *baton,
873251881Speter               const char *ns,
874251881Speter               const char *name,
875251881Speter               const svn_string_t *value,
876251881Speter               apr_pool_t *pool)
877251881Speter{
878251881Speter  apr_hash_t *props = baton;
879251881Speter  apr_pool_t *result_pool = apr_hash_pool_get(props);
880251881Speter  const char *prop_name;
881251881Speter
882251881Speter  /* ### is VAL in the proper pool?  */
883251881Speter
884251881Speter  prop_name = svn_ra_serf__svnname_from_wirename(ns, name, result_pool);
885251881Speter  if (prop_name != NULL)
886251881Speter    svn_hash_sets(props, prop_name, value);
887251881Speter
888251881Speter  return SVN_NO_ERROR;
889251881Speter}
890251881Speter
891251881Speter
892251881Spetersvn_error_t *
893251881Spetersvn_ra_serf__flatten_props(apr_hash_t **flat_props,
894251881Speter                           apr_hash_t *props,
895251881Speter                           apr_pool_t *result_pool,
896251881Speter                           apr_pool_t *scratch_pool)
897251881Speter{
898251881Speter  *flat_props = apr_hash_make(result_pool);
899251881Speter
900251881Speter  return svn_error_trace(svn_ra_serf__walk_node_props(
901251881Speter                            props,
902251881Speter                            set_flat_props,
903251881Speter                            *flat_props /* baton */,
904251881Speter                            scratch_pool));
905251881Speter}
906251881Speter
907251881Speter
908251881Speterstatic svn_error_t *
909251881Speterselect_revprops(void *baton,
910251881Speter                const char *ns,
911251881Speter                const char *name,
912251881Speter                const svn_string_t *val,
913251881Speter                apr_pool_t *scratch_pool)
914251881Speter{
915251881Speter  apr_hash_t *revprops = baton;
916251881Speter  apr_pool_t *result_pool = apr_hash_pool_get(revprops);
917251881Speter  const char *prop_name;
918251881Speter
919251881Speter  /* ### copy NAME into the RESULT_POOL?  */
920251881Speter  /* ### copy VAL into the RESULT_POOL?  */
921251881Speter
922251881Speter  if (strcmp(ns, SVN_DAV_PROP_NS_CUSTOM) == 0)
923251881Speter    prop_name = name;
924251881Speter  else if (strcmp(ns, SVN_DAV_PROP_NS_SVN) == 0)
925251881Speter    prop_name = apr_pstrcat(result_pool, SVN_PROP_PREFIX, name, (char *)NULL);
926251881Speter  else if (strcmp(ns, SVN_PROP_PREFIX) == 0)
927251881Speter    prop_name = apr_pstrcat(result_pool, SVN_PROP_PREFIX, name, (char *)NULL);
928251881Speter  else if (strcmp(ns, "") == 0)
929251881Speter    prop_name = name;
930251881Speter  else
931251881Speter    {
932251881Speter      /* do nothing for now? */
933251881Speter      return SVN_NO_ERROR;
934251881Speter    }
935251881Speter
936251881Speter  svn_hash_sets(revprops, prop_name, val);
937251881Speter
938251881Speter  return SVN_NO_ERROR;
939251881Speter}
940251881Speter
941251881Speter
942251881Spetersvn_error_t *
943251881Spetersvn_ra_serf__select_revprops(apr_hash_t **revprops,
944251881Speter                             const char *name,
945251881Speter                             svn_revnum_t rev,
946251881Speter                             apr_hash_t *all_revprops,
947251881Speter                             apr_pool_t *result_pool,
948251881Speter                             apr_pool_t *scratch_pool)
949251881Speter{
950251881Speter  *revprops = apr_hash_make(result_pool);
951251881Speter
952251881Speter  return svn_error_trace(svn_ra_serf__walk_all_props(
953251881Speter                            all_revprops, name, rev,
954251881Speter                            select_revprops, *revprops,
955251881Speter                            scratch_pool));
956251881Speter}
957251881Speter
958251881Speter
959251881Speter/*
960251881Speter * Contact the server (using CONN) to calculate baseline
961251881Speter * information for BASELINE_URL at REVISION (which may be
962251881Speter * SVN_INVALID_REVNUM to query the HEAD revision).
963251881Speter *
964251881Speter * If ACTUAL_REVISION is non-NULL, set *ACTUAL_REVISION to revision
965251881Speter * retrieved from the server as part of this process (which should
966251881Speter * match REVISION when REVISION is valid).  Set *BASECOLL_URL_P to the
967251881Speter * baseline collection URL.
968251881Speter */
969251881Speterstatic svn_error_t *
970251881Speterretrieve_baseline_info(svn_revnum_t *actual_revision,
971251881Speter                       const char **basecoll_url_p,
972251881Speter                       svn_ra_serf__connection_t *conn,
973251881Speter                       const char *baseline_url,
974251881Speter                       svn_revnum_t revision,
975251881Speter                       apr_pool_t *result_pool,
976251881Speter                       apr_pool_t *scratch_pool)
977251881Speter{
978251881Speter  apr_hash_t *props;
979251881Speter  apr_hash_t *dav_props;
980251881Speter  const char *basecoll_url;
981251881Speter
982251881Speter  SVN_ERR(svn_ra_serf__fetch_node_props(&props, conn,
983251881Speter                                        baseline_url, revision,
984251881Speter                                        baseline_props,
985251881Speter                                        scratch_pool, scratch_pool));
986251881Speter  dav_props = apr_hash_get(props, "DAV:", 4);
987251881Speter  /* If DAV_PROPS is NULL, then svn_prop_get_value() will return NULL.  */
988251881Speter
989251881Speter  basecoll_url = svn_prop_get_value(dav_props, "baseline-collection");
990251881Speter  if (!basecoll_url)
991251881Speter    {
992251881Speter      return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL,
993251881Speter                              _("The PROPFIND response did not include "
994251881Speter                                "the requested baseline-collection value"));
995251881Speter    }
996251881Speter  *basecoll_url_p = svn_urlpath__canonicalize(basecoll_url, result_pool);
997251881Speter
998251881Speter  if (actual_revision)
999251881Speter    {
1000251881Speter      const char *version_name;
1001251881Speter
1002251881Speter      version_name = svn_prop_get_value(dav_props, SVN_DAV__VERSION_NAME);
1003251881Speter      if (!version_name)
1004251881Speter        return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL,
1005251881Speter                                _("The PROPFIND response did not include "
1006251881Speter                                  "the requested version-name value"));
1007251881Speter
1008251881Speter      *actual_revision = SVN_STR_TO_REV(version_name);
1009251881Speter    }
1010251881Speter
1011251881Speter  return SVN_NO_ERROR;
1012251881Speter}
1013251881Speter
1014251881Speter
1015251881Speter/* For HTTPv1 servers, do a PROPFIND dance on the VCC to fetch the youngest
1016251881Speter   revnum. If BASECOLL_URL is non-NULL, then the corresponding baseline
1017251881Speter   collection URL is also returned.
1018251881Speter
1019251881Speter   Do the work over CONN.
1020251881Speter
1021251881Speter   *BASECOLL_URL (if requested) will be allocated in RESULT_POOL. All
1022251881Speter   temporary allocations will be made in SCRATCH_POOL.  */
1023251881Speterstatic svn_error_t *
1024251881Speterv1_get_youngest_revnum(svn_revnum_t *youngest,
1025251881Speter                       const char **basecoll_url,
1026251881Speter                       svn_ra_serf__connection_t *conn,
1027251881Speter                       const char *vcc_url,
1028251881Speter                       apr_pool_t *result_pool,
1029251881Speter                       apr_pool_t *scratch_pool)
1030251881Speter{
1031251881Speter  const char *baseline_url;
1032251881Speter  const char *bc_url;
1033251881Speter
1034251881Speter  /* Fetching DAV:checked-in from the VCC (with no Label: to specify a
1035251881Speter     revision) will return the latest Baseline resource's URL.  */
1036251881Speter  SVN_ERR(svn_ra_serf__fetch_dav_prop(&baseline_url, conn, vcc_url,
1037251881Speter                                      SVN_INVALID_REVNUM,
1038251881Speter                                      "checked-in",
1039251881Speter                                      scratch_pool, scratch_pool));
1040251881Speter  if (!baseline_url)
1041251881Speter    {
1042251881Speter      return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
1043251881Speter                              _("The OPTIONS response did not include "
1044251881Speter                                "the requested checked-in value"));
1045251881Speter    }
1046251881Speter  baseline_url = svn_urlpath__canonicalize(baseline_url, scratch_pool);
1047251881Speter
1048251881Speter  /* From the Baseline resource, we can fetch the DAV:baseline-collection
1049251881Speter     and DAV:version-name properties. The latter is the revision number,
1050251881Speter     which is formally the name used in Label: headers.  */
1051251881Speter
1052251881Speter  /* First check baseline information cache. */
1053251881Speter  SVN_ERR(svn_ra_serf__blncache_get_baseline_info(&bc_url,
1054251881Speter                                                  youngest,
1055251881Speter                                                  conn->session->blncache,
1056251881Speter                                                  baseline_url,
1057251881Speter                                                  scratch_pool));
1058251881Speter  if (!bc_url)
1059251881Speter    {
1060251881Speter      SVN_ERR(retrieve_baseline_info(youngest, &bc_url, conn,
1061251881Speter                                     baseline_url, SVN_INVALID_REVNUM,
1062251881Speter                                     scratch_pool, scratch_pool));
1063251881Speter      SVN_ERR(svn_ra_serf__blncache_set(conn->session->blncache,
1064251881Speter                                        baseline_url, *youngest,
1065251881Speter                                        bc_url, scratch_pool));
1066251881Speter    }
1067251881Speter
1068251881Speter  if (basecoll_url != NULL)
1069251881Speter    *basecoll_url = apr_pstrdup(result_pool, bc_url);
1070251881Speter
1071251881Speter  return SVN_NO_ERROR;
1072251881Speter}
1073251881Speter
1074251881Speter
1075251881Spetersvn_error_t *
1076251881Spetersvn_ra_serf__get_youngest_revnum(svn_revnum_t *youngest,
1077251881Speter                                 svn_ra_serf__session_t *session,
1078251881Speter                                 apr_pool_t *scratch_pool)
1079251881Speter{
1080251881Speter  const char *vcc_url;
1081251881Speter
1082251881Speter  if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session))
1083251881Speter    return svn_error_trace(svn_ra_serf__v2_get_youngest_revnum(
1084251881Speter                             youngest, session->conns[0], scratch_pool));
1085251881Speter
1086251881Speter  SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, NULL, scratch_pool));
1087251881Speter
1088251881Speter  return svn_error_trace(v1_get_youngest_revnum(youngest, NULL,
1089251881Speter                                                session->conns[0], vcc_url,
1090251881Speter                                                scratch_pool, scratch_pool));
1091251881Speter}
1092251881Speter
1093251881Speter
1094251881Speter/* Set *BC_URL to the baseline collection url for REVISION. If REVISION
1095251881Speter   is SVN_INVALID_REVNUM, then the youngest revnum ("HEAD") is used.
1096251881Speter
1097251881Speter   *REVNUM_USED will be set to the revision used.
1098251881Speter
1099251881Speter   Uses the specified CONN, which is part of SESSION.
1100251881Speter
1101251881Speter   All allocations (results and temporary) are performed in POOL.  */
1102251881Speterstatic svn_error_t *
1103251881Speterget_baseline_info(const char **bc_url,
1104251881Speter                  svn_revnum_t *revnum_used,
1105251881Speter                  svn_ra_serf__session_t *session,
1106251881Speter                  svn_ra_serf__connection_t *conn,
1107251881Speter                  svn_revnum_t revision,
1108251881Speter                  apr_pool_t *pool)
1109251881Speter{
1110251881Speter  /* If we detected HTTP v2 support on the server, we can construct
1111251881Speter     the baseline collection URL ourselves, and fetch the latest
1112251881Speter     revision (if needed) with an OPTIONS request.  */
1113251881Speter  if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session))
1114251881Speter    {
1115251881Speter      if (SVN_IS_VALID_REVNUM(revision))
1116251881Speter        {
1117251881Speter          *revnum_used = revision;
1118251881Speter        }
1119251881Speter      else
1120251881Speter        {
1121251881Speter          SVN_ERR(svn_ra_serf__v2_get_youngest_revnum(
1122251881Speter                    revnum_used, conn, pool));
1123251881Speter          if (! SVN_IS_VALID_REVNUM(*revnum_used))
1124251881Speter            return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
1125251881Speter                                    _("The OPTIONS response did not include "
1126251881Speter                                      "the youngest revision"));
1127251881Speter        }
1128251881Speter
1129251881Speter      *bc_url = apr_psprintf(pool, "%s/%ld",
1130251881Speter                             session->rev_root_stub, *revnum_used);
1131251881Speter    }
1132251881Speter
1133251881Speter  /* Otherwise, we fall back to the old VCC_URL PROPFIND hunt.  */
1134251881Speter  else
1135251881Speter    {
1136251881Speter      const char *vcc_url;
1137251881Speter
1138251881Speter      SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, conn, pool));
1139251881Speter
1140251881Speter      if (SVN_IS_VALID_REVNUM(revision))
1141251881Speter        {
1142251881Speter          /* First check baseline information cache. */
1143251881Speter          SVN_ERR(svn_ra_serf__blncache_get_bc_url(bc_url,
1144251881Speter                                                   session->blncache,
1145251881Speter                                                   revision, pool));
1146251881Speter          if (!*bc_url)
1147251881Speter            {
1148251881Speter              SVN_ERR(retrieve_baseline_info(NULL, bc_url, conn,
1149251881Speter                                             vcc_url, revision, pool, pool));
1150251881Speter              SVN_ERR(svn_ra_serf__blncache_set(session->blncache, NULL,
1151251881Speter                                                revision, *bc_url, pool));
1152251881Speter            }
1153251881Speter
1154251881Speter          *revnum_used = revision;
1155251881Speter        }
1156251881Speter      else
1157251881Speter        {
1158251881Speter          SVN_ERR(v1_get_youngest_revnum(revnum_used, bc_url,
1159251881Speter                                         conn, vcc_url,
1160251881Speter                                         pool, pool));
1161251881Speter        }
1162251881Speter    }
1163251881Speter
1164251881Speter  return SVN_NO_ERROR;
1165251881Speter}
1166251881Speter
1167251881Speter
1168251881Spetersvn_error_t *
1169251881Spetersvn_ra_serf__get_stable_url(const char **stable_url,
1170251881Speter                            svn_revnum_t *latest_revnum,
1171251881Speter                            svn_ra_serf__session_t *session,
1172251881Speter                            svn_ra_serf__connection_t *conn,
1173251881Speter                            const char *url,
1174251881Speter                            svn_revnum_t revision,
1175251881Speter                            apr_pool_t *result_pool,
1176251881Speter                            apr_pool_t *scratch_pool)
1177251881Speter{
1178251881Speter  const char *basecoll_url;
1179251881Speter  const char *repos_relpath;
1180251881Speter  svn_revnum_t revnum_used;
1181251881Speter
1182251881Speter  /* No URL? No sweat. We'll use the session URL.  */
1183251881Speter  if (! url)
1184251881Speter    url = session->session_url.path;
1185251881Speter
1186251881Speter  /* If the caller didn't provide a specific connection for us to use,
1187251881Speter     we'll use the default connection.  */
1188251881Speter  if (! conn)
1189251881Speter    conn = session->conns[0];
1190251881Speter
1191251881Speter  SVN_ERR(get_baseline_info(&basecoll_url, &revnum_used,
1192251881Speter                            session, conn, revision, scratch_pool));
1193251881Speter  SVN_ERR(svn_ra_serf__get_relative_path(&repos_relpath, url,
1194251881Speter                                         session, conn, scratch_pool));
1195251881Speter
1196251881Speter  *stable_url = svn_path_url_add_component2(basecoll_url, repos_relpath,
1197251881Speter                                            result_pool);
1198251881Speter  if (latest_revnum)
1199251881Speter    *latest_revnum = revnum_used;
1200251881Speter
1201251881Speter  return SVN_NO_ERROR;
1202251881Speter}
1203251881Speter
1204251881Speter
1205251881Spetersvn_error_t *
1206251881Spetersvn_ra_serf__get_resource_type(svn_node_kind_t *kind,
1207251881Speter                               apr_hash_t *props)
1208251881Speter{
1209251881Speter  apr_hash_t *dav_props;
1210251881Speter  const char *res_type;
1211251881Speter
1212251881Speter  dav_props = apr_hash_get(props, "DAV:", 4);
1213251881Speter  res_type = svn_prop_get_value(dav_props, "resourcetype");
1214251881Speter  if (!res_type)
1215251881Speter    {
1216251881Speter      /* How did this happen? */
1217251881Speter      return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL,
1218251881Speter                              _("The PROPFIND response did not include the "
1219251881Speter                                "requested resourcetype value"));
1220251881Speter    }
1221251881Speter
1222251881Speter  if (strcmp(res_type, "collection") == 0)
1223251881Speter    {
1224251881Speter      *kind = svn_node_dir;
1225251881Speter    }
1226251881Speter  else
1227251881Speter    {
1228251881Speter      *kind = svn_node_file;
1229251881Speter    }
1230251881Speter
1231251881Speter  return SVN_NO_ERROR;
1232251881Speter}
1233251881Speter
1234251881Speter
1235251881Spetersvn_error_t *
1236251881Spetersvn_ra_serf__fetch_dav_prop(const char **value,
1237251881Speter                            svn_ra_serf__connection_t *conn,
1238251881Speter                            const char *url,
1239251881Speter                            svn_revnum_t revision,
1240251881Speter                            const char *propname,
1241251881Speter                            apr_pool_t *result_pool,
1242251881Speter                            apr_pool_t *scratch_pool)
1243251881Speter{
1244251881Speter  apr_hash_t *props;
1245251881Speter  apr_hash_t *dav_props;
1246251881Speter
1247251881Speter  SVN_ERR(svn_ra_serf__fetch_node_props(&props, conn, url, revision,
1248251881Speter                                        checked_in_props,
1249251881Speter                                        scratch_pool, scratch_pool));
1250251881Speter  dav_props = apr_hash_get(props, "DAV:", 4);
1251251881Speter  if (dav_props == NULL)
1252251881Speter    return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL,
1253251881Speter                            _("The PROPFIND response did not include "
1254251881Speter                              "the requested 'DAV:' properties"));
1255251881Speter
1256251881Speter  /* We wouldn't get here if the resource was not found (404), so the
1257251881Speter     property should be present.
1258251881Speter
1259251881Speter     Note: it is okay to call apr_pstrdup() with NULL.  */
1260251881Speter  *value = apr_pstrdup(result_pool, svn_prop_get_value(dav_props, propname));
1261251881Speter
1262251881Speter  return SVN_NO_ERROR;
1263251881Speter}
1264