1251881Speter/*
2251881Speter * getlocks.c :  entry point for get_locks RA functions 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 <apr_uri.h>
27251881Speter
28251881Speter#include <serf.h>
29251881Speter
30251881Speter#include "svn_hash.h"
31251881Speter#include "svn_path.h"
32251881Speter#include "svn_pools.h"
33251881Speter#include "svn_ra.h"
34251881Speter#include "svn_dav.h"
35251881Speter#include "svn_time.h"
36251881Speter#include "svn_xml.h"
37251881Speter
38251881Speter#include "private/svn_dav_protocol.h"
39251881Speter#include "private/svn_fspath.h"
40251881Speter#include "svn_private_config.h"
41251881Speter
42251881Speter#include "../libsvn_ra/ra_loader.h"
43251881Speter
44251881Speter#include "ra_serf.h"
45251881Speter
46251881Speter
47251881Speter/*
48251881Speter * This enum represents the current state of our XML parsing for a REPORT.
49251881Speter */
50251881Speterenum {
51251881Speter  INITIAL = 0,
52251881Speter  REPORT,
53251881Speter  LOCK,
54251881Speter  PATH,
55251881Speter  TOKEN,
56251881Speter  OWNER,
57251881Speter  COMMENT,
58251881Speter  CREATION_DATE,
59251881Speter  EXPIRATION_DATE
60251881Speter};
61251881Speter
62251881Spetertypedef struct lock_context_t {
63251881Speter  apr_pool_t *pool;
64251881Speter
65251881Speter  /* target and requested depth of the operation. */
66251881Speter  const char *path;
67251881Speter  svn_depth_t requested_depth;
68251881Speter
69251881Speter  /* return hash */
70251881Speter  apr_hash_t *hash;
71251881Speter
72251881Speter} lock_context_t;
73251881Speter
74251881Speter#define D_ "DAV:"
75251881Speter#define S_ SVN_XML_NAMESPACE
76251881Speterstatic const svn_ra_serf__xml_transition_t getlocks_ttable[] = {
77251881Speter  { INITIAL, S_, "get-locks-report", REPORT,
78251881Speter    FALSE, { NULL }, FALSE },
79251881Speter
80251881Speter  { REPORT, S_, "lock", LOCK,
81251881Speter    FALSE, { NULL }, TRUE },
82251881Speter
83251881Speter  { LOCK, S_, "path", PATH,
84251881Speter    TRUE, { NULL }, TRUE },
85251881Speter
86251881Speter  { LOCK, S_, "token", TOKEN,
87251881Speter    TRUE, { NULL }, TRUE },
88251881Speter
89251881Speter  { LOCK, S_, "owner", OWNER,
90251881Speter    TRUE, { NULL }, TRUE },
91251881Speter
92251881Speter  { LOCK, S_, "comment", COMMENT,
93251881Speter    TRUE, { NULL }, TRUE },
94251881Speter
95251881Speter  { LOCK, S_, SVN_DAV__CREATIONDATE, CREATION_DATE,
96251881Speter    TRUE, { NULL }, TRUE },
97251881Speter
98251881Speter  { LOCK, S_, "expirationdate", EXPIRATION_DATE,
99251881Speter    TRUE, { NULL }, TRUE },
100251881Speter
101251881Speter  { 0 }
102251881Speter};
103251881Speter
104251881Speter
105251881Speter/* Conforms to svn_ra_serf__xml_closed_t  */
106251881Speterstatic svn_error_t *
107251881Spetergetlocks_closed(svn_ra_serf__xml_estate_t *xes,
108251881Speter                void *baton,
109251881Speter                int leaving_state,
110251881Speter                const svn_string_t *cdata,
111251881Speter                apr_hash_t *attrs,
112251881Speter                apr_pool_t *scratch_pool)
113251881Speter{
114251881Speter  lock_context_t *lock_ctx = baton;
115251881Speter
116251881Speter  if (leaving_state == LOCK)
117251881Speter    {
118251881Speter      const char *path = svn_hash_gets(attrs, "path");
119269847Speter      const char *token = svn_hash_gets(attrs, "token");
120251881Speter      svn_boolean_t save_lock = FALSE;
121251881Speter
122251881Speter      /* Filter out unwanted paths.  Since Subversion only allows
123251881Speter         locks on files, we can treat depth=immediates the same as
124251881Speter         depth=files for filtering purposes.  Meaning, we'll keep
125251881Speter         this lock if:
126251881Speter
127251881Speter         a) its path is the very path we queried, or
128251881Speter         b) we've asked for a fully recursive answer, or
129251881Speter         c) we've asked for depth=files or depth=immediates, and this
130251881Speter            lock is on an immediate child of our query path.
131251881Speter      */
132269847Speter      if (! token)
133269847Speter        {
134269847Speter          /* A lock without a token is not a lock; just an answer that there
135269847Speter             is no lock on the node. */
136269847Speter          save_lock = FALSE;
137269847Speter        }
138251881Speter      if (strcmp(lock_ctx->path, path) == 0
139251881Speter          || lock_ctx->requested_depth == svn_depth_infinity)
140251881Speter        {
141251881Speter          save_lock = TRUE;
142251881Speter        }
143251881Speter      else if (lock_ctx->requested_depth == svn_depth_files
144251881Speter               || lock_ctx->requested_depth == svn_depth_immediates)
145251881Speter        {
146251881Speter          const char *relpath = svn_fspath__skip_ancestor(lock_ctx->path,
147251881Speter                                                          path);
148251881Speter          if (relpath && (svn_path_component_count(relpath) == 1))
149251881Speter            save_lock = TRUE;
150251881Speter        }
151251881Speter
152251881Speter      if (save_lock)
153251881Speter        {
154251881Speter          /* We get to put the structure on the stack rather than using
155251881Speter             svn_lock_create(). Bwahahaha....   */
156251881Speter          svn_lock_t lock = { 0 };
157251881Speter          const char *date;
158251881Speter          svn_lock_t *result_lock;
159251881Speter
160251881Speter          /* Note: these "attributes" came from child elements. Some of
161251881Speter             them may have not been sent, so the value will be NULL.  */
162251881Speter
163251881Speter          lock.path = path;
164269847Speter          lock.token = token;
165251881Speter          lock.owner = svn_hash_gets(attrs, "owner");
166251881Speter          lock.comment = svn_hash_gets(attrs, "comment");
167251881Speter
168251881Speter          date = svn_hash_gets(attrs, SVN_DAV__CREATIONDATE);
169251881Speter          if (date)
170251881Speter            SVN_ERR(svn_time_from_cstring(&lock.creation_date, date,
171251881Speter                                          scratch_pool));
172251881Speter
173251881Speter          date = svn_hash_gets(attrs, "expirationdate");
174251881Speter          if (date)
175251881Speter            SVN_ERR(svn_time_from_cstring(&lock.expiration_date, date,
176251881Speter                                          scratch_pool));
177251881Speter
178251881Speter          result_lock = svn_lock_dup(&lock, lock_ctx->pool);
179251881Speter          svn_hash_sets(lock_ctx->hash, result_lock->path, result_lock);
180251881Speter        }
181251881Speter    }
182251881Speter  else
183251881Speter    {
184251881Speter      const char *name;
185251881Speter
186251881Speter      SVN_ERR_ASSERT(cdata != NULL);
187251881Speter
188251881Speter      if (leaving_state == PATH)
189251881Speter        name = "path";
190251881Speter      else if (leaving_state == TOKEN)
191251881Speter        name = "token";
192251881Speter      else if (leaving_state == OWNER)
193251881Speter        name = "owner";
194251881Speter      else if (leaving_state == COMMENT)
195251881Speter        name = "comment";
196251881Speter      else if (leaving_state == CREATION_DATE)
197251881Speter        name = SVN_DAV__CREATIONDATE;
198251881Speter      else if (leaving_state == EXPIRATION_DATE)
199251881Speter        name = "expirationdate";
200251881Speter      else
201251881Speter        SVN_ERR_MALFUNCTION();
202251881Speter
203251881Speter      /* Store the lock information onto the LOCK elemstate.  */
204251881Speter      svn_ra_serf__xml_note(xes, LOCK, name, cdata->data);
205251881Speter    }
206251881Speter
207251881Speter  return SVN_NO_ERROR;
208251881Speter}
209251881Speter
210251881Speter
211251881Speter/* Implements svn_ra_serf__request_body_delegate_t */
212251881Speterstatic svn_error_t *
213251881Spetercreate_getlocks_body(serf_bucket_t **body_bkt,
214251881Speter                     void *baton,
215251881Speter                     serf_bucket_alloc_t *alloc,
216251881Speter                     apr_pool_t *pool)
217251881Speter{
218251881Speter  lock_context_t *lock_ctx = baton;
219251881Speter  serf_bucket_t *buckets;
220251881Speter
221251881Speter  buckets = serf_bucket_aggregate_create(alloc);
222251881Speter
223251881Speter  svn_ra_serf__add_open_tag_buckets(
224251881Speter    buckets, alloc, "S:get-locks-report", "xmlns:S", SVN_XML_NAMESPACE,
225251881Speter    "depth", svn_depth_to_word(lock_ctx->requested_depth), NULL);
226251881Speter  svn_ra_serf__add_close_tag_buckets(buckets, alloc, "S:get-locks-report");
227251881Speter
228251881Speter  *body_bkt = buckets;
229251881Speter  return SVN_NO_ERROR;
230251881Speter}
231251881Speter
232251881Spetersvn_error_t *
233251881Spetersvn_ra_serf__get_locks(svn_ra_session_t *ra_session,
234251881Speter                       apr_hash_t **locks,
235251881Speter                       const char *path,
236251881Speter                       svn_depth_t depth,
237251881Speter                       apr_pool_t *pool)
238251881Speter{
239251881Speter  lock_context_t *lock_ctx;
240251881Speter  svn_ra_serf__session_t *session = ra_session->priv;
241251881Speter  svn_ra_serf__handler_t *handler;
242251881Speter  svn_ra_serf__xml_context_t *xmlctx;
243251881Speter  const char *req_url, *rel_path;
244269847Speter  svn_error_t *err;
245251881Speter
246251881Speter  req_url = svn_path_url_add_component2(session->session_url.path, path, pool);
247251881Speter  SVN_ERR(svn_ra_serf__get_relative_path(&rel_path, req_url, session,
248251881Speter                                         NULL, pool));
249251881Speter
250251881Speter  lock_ctx = apr_pcalloc(pool, sizeof(*lock_ctx));
251251881Speter  lock_ctx->pool = pool;
252251881Speter  lock_ctx->path = apr_pstrcat(pool, "/", rel_path, (char *)NULL);
253251881Speter  lock_ctx->requested_depth = depth;
254251881Speter  lock_ctx->hash = apr_hash_make(pool);
255251881Speter
256251881Speter  xmlctx = svn_ra_serf__xml_context_create(getlocks_ttable,
257251881Speter                                           NULL, getlocks_closed, NULL,
258251881Speter                                           lock_ctx,
259251881Speter                                           pool);
260251881Speter  handler = svn_ra_serf__create_expat_handler(xmlctx, pool);
261251881Speter
262251881Speter  handler->method = "REPORT";
263251881Speter  handler->path = req_url;
264251881Speter  handler->body_type = "text/xml";
265251881Speter  handler->conn = session->conns[0];
266251881Speter  handler->session = session;
267251881Speter
268251881Speter  handler->body_delegate = create_getlocks_body;
269251881Speter  handler->body_delegate_baton = lock_ctx;
270251881Speter
271269847Speter  err = svn_ra_serf__context_run_one(handler, pool);
272269847Speter
273269847Speter  /* Wrap the server generated error for an unsupported report with the
274269847Speter     documented error for this ra function. */
275269847Speter  if (svn_error_find_cause(err, SVN_ERR_UNSUPPORTED_FEATURE))
276269847Speter    err = svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, err, NULL);
277269847Speter
278269847Speter  SVN_ERR(err);
279251881Speter
280251881Speter  /* We get a 404 when a path doesn't exist in HEAD, but it might
281251881Speter     have existed earlier (E.g. 'svn ls http://s/svn/trunk/file@1' */
282251881Speter  if (handler->sline.code != 404)
283251881Speter    {
284253734Speter      SVN_ERR(svn_ra_serf__error_on_status(handler->sline,
285251881Speter                                           handler->path,
286251881Speter                                           handler->location));
287251881Speter    }
288251881Speter
289251881Speter  *locks = lock_ctx->hash;
290251881Speter
291251881Speter  return SVN_NO_ERROR;
292251881Speter}
293