1251881Speter/*
2251881Speter * marshal.c :  Marshalling routines for Subversion protocol
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 <assert.h>
27251881Speter#include <stdlib.h>
28251881Speter
29251881Speter#define APR_WANT_STRFUNC
30251881Speter#include <apr_want.h>
31251881Speter#include <apr_general.h>
32251881Speter#include <apr_lib.h>
33251881Speter#include <apr_strings.h>
34251881Speter
35251881Speter#include "svn_hash.h"
36251881Speter#include "svn_types.h"
37251881Speter#include "svn_string.h"
38251881Speter#include "svn_error.h"
39251881Speter#include "svn_pools.h"
40251881Speter#include "svn_ra_svn.h"
41251881Speter#include "svn_private_config.h"
42251881Speter#include "svn_ctype.h"
43251881Speter#include "svn_time.h"
44251881Speter
45251881Speter#include "ra_svn.h"
46251881Speter
47251881Speter#include "private/svn_string_private.h"
48251881Speter#include "private/svn_dep_compat.h"
49251881Speter#include "private/svn_error_private.h"
50251881Speter
51251881Speter#define svn_iswhitespace(c) ((c) == ' ' || (c) == '\n')
52251881Speter
53251881Speter/* If we receive data that *claims* to be followed by a very long string,
54251881Speter * we should not trust that claim right away. But everything up to 1 MB
55251881Speter * should be too small to be instrumental for a DOS attack. */
56251881Speter
57251881Speter#define SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD (0x100000)
58251881Speter
59251881Speter/* Return the APR socket timeout to be used for the connection depending
60251881Speter * on whether there is a blockage handler or zero copy has been activated. */
61251881Speterstatic apr_interval_time_t
62251881Speterget_timeout(svn_ra_svn_conn_t *conn)
63251881Speter{
64251881Speter  return conn->block_handler ? 0 : -1;
65251881Speter}
66251881Speter
67251881Speter/* --- CONNECTION INITIALIZATION --- */
68251881Speter
69251881Spetersvn_ra_svn_conn_t *svn_ra_svn_create_conn3(apr_socket_t *sock,
70251881Speter                                           apr_file_t *in_file,
71251881Speter                                           apr_file_t *out_file,
72251881Speter                                           int compression_level,
73251881Speter                                           apr_size_t zero_copy_limit,
74251881Speter                                           apr_size_t error_check_interval,
75251881Speter                                           apr_pool_t *pool)
76251881Speter{
77251881Speter  svn_ra_svn_conn_t *conn;
78251881Speter  void *mem = apr_palloc(pool, sizeof(*conn) + SVN_RA_SVN__PAGE_SIZE);
79251881Speter  conn = (void*)APR_ALIGN((apr_uintptr_t)mem, SVN_RA_SVN__PAGE_SIZE);
80251881Speter
81251881Speter  assert((sock && !in_file && !out_file) || (!sock && in_file && out_file));
82251881Speter#ifdef SVN_HAVE_SASL
83251881Speter  conn->sock = sock;
84251881Speter  conn->encrypted = FALSE;
85251881Speter#endif
86251881Speter  conn->session = NULL;
87251881Speter  conn->read_ptr = conn->read_buf;
88251881Speter  conn->read_end = conn->read_buf;
89251881Speter  conn->write_pos = 0;
90251881Speter  conn->written_since_error_check = 0;
91251881Speter  conn->error_check_interval = error_check_interval;
92251881Speter  conn->may_check_for_error = error_check_interval == 0;
93251881Speter  conn->block_handler = NULL;
94251881Speter  conn->block_baton = NULL;
95251881Speter  conn->capabilities = apr_hash_make(pool);
96251881Speter  conn->compression_level = compression_level;
97251881Speter  conn->zero_copy_limit = zero_copy_limit;
98251881Speter  conn->pool = pool;
99251881Speter
100251881Speter  if (sock != NULL)
101251881Speter    {
102251881Speter      apr_sockaddr_t *sa;
103251881Speter      conn->stream = svn_ra_svn__stream_from_sock(sock, pool);
104251881Speter      if (!(apr_socket_addr_get(&sa, APR_REMOTE, sock) == APR_SUCCESS
105251881Speter            && apr_sockaddr_ip_get(&conn->remote_ip, sa) == APR_SUCCESS))
106251881Speter        conn->remote_ip = NULL;
107251881Speter      svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn));
108251881Speter    }
109251881Speter  else
110251881Speter    {
111251881Speter      conn->stream = svn_ra_svn__stream_from_files(in_file, out_file, pool);
112251881Speter      conn->remote_ip = NULL;
113251881Speter    }
114251881Speter
115251881Speter  return conn;
116251881Speter}
117251881Speter
118251881Spetersvn_ra_svn_conn_t *svn_ra_svn_create_conn2(apr_socket_t *sock,
119251881Speter                                           apr_file_t *in_file,
120251881Speter                                           apr_file_t *out_file,
121251881Speter                                           int compression_level,
122251881Speter                                           apr_pool_t *pool)
123251881Speter{
124251881Speter  return svn_ra_svn_create_conn3(sock, in_file, out_file,
125251881Speter                                 compression_level, 0, 0, pool);
126251881Speter}
127251881Speter
128251881Speter/* backward-compatible implementation using the default compression level */
129251881Spetersvn_ra_svn_conn_t *svn_ra_svn_create_conn(apr_socket_t *sock,
130251881Speter                                          apr_file_t *in_file,
131251881Speter                                          apr_file_t *out_file,
132251881Speter                                          apr_pool_t *pool)
133251881Speter{
134251881Speter  return svn_ra_svn_create_conn3(sock, in_file, out_file,
135251881Speter                                 SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, 0, 0,
136251881Speter                                 pool);
137251881Speter}
138251881Speter
139251881Spetersvn_error_t *svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn,
140251881Speter                                         const apr_array_header_t *list)
141251881Speter{
142251881Speter  int i;
143251881Speter  svn_ra_svn_item_t *item;
144251881Speter  const char *word;
145251881Speter
146251881Speter  for (i = 0; i < list->nelts; i++)
147251881Speter    {
148251881Speter      item = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
149251881Speter      if (item->kind != SVN_RA_SVN_WORD)
150251881Speter        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
151251881Speter                                _("Capability entry is not a word"));
152251881Speter      word = apr_pstrdup(conn->pool, item->u.word);
153251881Speter      svn_hash_sets(conn->capabilities, word, word);
154251881Speter    }
155251881Speter  return SVN_NO_ERROR;
156251881Speter}
157251881Speter
158251881Spetersvn_error_t *
159251881Spetersvn_ra_svn__set_shim_callbacks(svn_ra_svn_conn_t *conn,
160251881Speter                               svn_delta_shim_callbacks_t *shim_callbacks)
161251881Speter{
162251881Speter  conn->shim_callbacks = shim_callbacks;
163251881Speter  return SVN_NO_ERROR;
164251881Speter}
165251881Speter
166251881Spetersvn_boolean_t svn_ra_svn_has_capability(svn_ra_svn_conn_t *conn,
167251881Speter                                        const char *capability)
168251881Speter{
169251881Speter  return (svn_hash_gets(conn->capabilities, capability) != NULL);
170251881Speter}
171251881Speter
172251881Speterint
173251881Spetersvn_ra_svn_compression_level(svn_ra_svn_conn_t *conn)
174251881Speter{
175251881Speter  return conn->compression_level;
176251881Speter}
177251881Speter
178251881Speterapr_size_t
179251881Spetersvn_ra_svn_zero_copy_limit(svn_ra_svn_conn_t *conn)
180251881Speter{
181251881Speter  return conn->zero_copy_limit;
182251881Speter}
183251881Speter
184251881Speterconst char *svn_ra_svn_conn_remote_host(svn_ra_svn_conn_t *conn)
185251881Speter{
186251881Speter  return conn->remote_ip;
187251881Speter}
188251881Speter
189251881Spetervoid
190251881Spetersvn_ra_svn__set_block_handler(svn_ra_svn_conn_t *conn,
191251881Speter                              ra_svn_block_handler_t handler,
192251881Speter                              void *baton)
193251881Speter{
194251881Speter  conn->block_handler = handler;
195251881Speter  conn->block_baton = baton;
196251881Speter  svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn));
197251881Speter}
198251881Speter
199251881Spetersvn_boolean_t svn_ra_svn__input_waiting(svn_ra_svn_conn_t *conn,
200251881Speter                                        apr_pool_t *pool)
201251881Speter{
202251881Speter  return svn_ra_svn__stream_pending(conn->stream);
203251881Speter}
204251881Speter
205251881Speter/* --- WRITE BUFFER MANAGEMENT --- */
206251881Speter
207251881Speter/* Write data to socket or output file as appropriate. */
208251881Speterstatic svn_error_t *writebuf_output(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
209251881Speter                                    const char *data, apr_size_t len)
210251881Speter{
211251881Speter  const char *end = data + len;
212251881Speter  apr_size_t count;
213251881Speter  apr_pool_t *subpool = NULL;
214251881Speter  svn_ra_svn__session_baton_t *session = conn->session;
215251881Speter
216251881Speter  while (data < end)
217251881Speter    {
218251881Speter      count = end - data;
219251881Speter
220251881Speter      if (session && session->callbacks && session->callbacks->cancel_func)
221251881Speter        SVN_ERR((session->callbacks->cancel_func)(session->callbacks_baton));
222251881Speter
223251881Speter      SVN_ERR(svn_ra_svn__stream_write(conn->stream, data, &count));
224251881Speter      if (count == 0)
225251881Speter        {
226251881Speter          if (!subpool)
227251881Speter            subpool = svn_pool_create(pool);
228251881Speter          else
229251881Speter            svn_pool_clear(subpool);
230251881Speter          SVN_ERR(conn->block_handler(conn, subpool, conn->block_baton));
231251881Speter        }
232251881Speter      data += count;
233251881Speter
234251881Speter      if (session)
235251881Speter        {
236251881Speter          const svn_ra_callbacks2_t *cb = session->callbacks;
237251881Speter          session->bytes_written += count;
238251881Speter
239251881Speter          if (cb && cb->progress_func)
240251881Speter            (cb->progress_func)(session->bytes_written + session->bytes_read,
241251881Speter                                -1, cb->progress_baton, subpool);
242251881Speter        }
243251881Speter    }
244251881Speter
245251881Speter  conn->written_since_error_check += len;
246251881Speter  conn->may_check_for_error
247251881Speter    = conn->written_since_error_check >= conn->error_check_interval;
248251881Speter
249251881Speter  if (subpool)
250251881Speter    svn_pool_destroy(subpool);
251251881Speter  return SVN_NO_ERROR;
252251881Speter}
253251881Speter
254251881Speter/* Write data from the write buffer out to the socket. */
255251881Speterstatic svn_error_t *writebuf_flush(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
256251881Speter{
257251881Speter  apr_size_t write_pos = conn->write_pos;
258251881Speter
259251881Speter  /* Clear conn->write_pos first in case the block handler does a read. */
260251881Speter  conn->write_pos = 0;
261251881Speter  SVN_ERR(writebuf_output(conn, pool, conn->write_buf, write_pos));
262251881Speter  return SVN_NO_ERROR;
263251881Speter}
264251881Speter
265251881Speterstatic svn_error_t *writebuf_write(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
266251881Speter                                   const char *data, apr_size_t len)
267251881Speter{
268251881Speter  /* data >= 8k is sent immediately */
269251881Speter  if (len >= sizeof(conn->write_buf) / 2)
270251881Speter    {
271251881Speter      if (conn->write_pos > 0)
272251881Speter        SVN_ERR(writebuf_flush(conn, pool));
273251881Speter
274251881Speter      return writebuf_output(conn, pool, data, len);
275251881Speter    }
276251881Speter
277251881Speter  /* ensure room for the data to add */
278251881Speter  if (conn->write_pos + len > sizeof(conn->write_buf))
279251881Speter    SVN_ERR(writebuf_flush(conn, pool));
280251881Speter
281251881Speter  /* buffer the new data block as well */
282251881Speter  memcpy(conn->write_buf + conn->write_pos, data, len);
283251881Speter  conn->write_pos += len;
284251881Speter
285251881Speter  return SVN_NO_ERROR;
286251881Speter}
287251881Speter
288251881Speterstatic svn_error_t *
289251881Speterwritebuf_write_short_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
290251881Speter                            const char *data, apr_size_t len)
291251881Speter{
292251881Speter  apr_size_t left = sizeof(conn->write_buf) - conn->write_pos;
293251881Speter  if (len <= left)
294251881Speter  {
295251881Speter    memcpy(conn->write_buf + conn->write_pos, data, len);
296251881Speter    conn->write_pos += len;
297251881Speter    return SVN_NO_ERROR;
298251881Speter  }
299251881Speter  else
300251881Speter    return writebuf_write(conn, pool, data, len);
301251881Speter}
302251881Speter
303251881Speterstatic APR_INLINE svn_error_t *
304251881Speterwritebuf_writechar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char data)
305251881Speter{
306251881Speter  if (conn->write_pos < sizeof(conn->write_buf))
307251881Speter  {
308251881Speter    conn->write_buf[conn->write_pos] = data;
309251881Speter    conn->write_pos++;
310251881Speter
311251881Speter    return SVN_NO_ERROR;
312251881Speter  }
313251881Speter  else
314251881Speter  {
315251881Speter    char temp = data;
316251881Speter    return writebuf_write(conn, pool, &temp, 1);
317251881Speter  }
318251881Speter}
319251881Speter
320251881Speter/* --- READ BUFFER MANAGEMENT --- */
321251881Speter
322251881Speter/* Read bytes into DATA until either the read buffer is empty or
323251881Speter * we reach END. */
324251881Speterstatic char *readbuf_drain(svn_ra_svn_conn_t *conn, char *data, char *end)
325251881Speter{
326251881Speter  apr_ssize_t buflen, copylen;
327251881Speter
328251881Speter  buflen = conn->read_end - conn->read_ptr;
329251881Speter  copylen = (buflen < end - data) ? buflen : end - data;
330251881Speter  memcpy(data, conn->read_ptr, copylen);
331251881Speter  conn->read_ptr += copylen;
332251881Speter  return data + copylen;
333251881Speter}
334251881Speter
335251881Speter/* Read data from socket or input file as appropriate. */
336251881Speterstatic svn_error_t *readbuf_input(svn_ra_svn_conn_t *conn, char *data,
337251881Speter                                  apr_size_t *len, apr_pool_t *pool)
338251881Speter{
339251881Speter  svn_ra_svn__session_baton_t *session = conn->session;
340251881Speter
341251881Speter  if (session && session->callbacks && session->callbacks->cancel_func)
342251881Speter    SVN_ERR((session->callbacks->cancel_func)(session->callbacks_baton));
343251881Speter
344251881Speter  SVN_ERR(svn_ra_svn__stream_read(conn->stream, data, len));
345251881Speter  if (*len == 0)
346251881Speter    return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL);
347251881Speter
348251881Speter  if (session)
349251881Speter    {
350251881Speter      const svn_ra_callbacks2_t *cb = session->callbacks;
351251881Speter      session->bytes_read += *len;
352251881Speter
353251881Speter      if (cb && cb->progress_func)
354251881Speter        (cb->progress_func)(session->bytes_read + session->bytes_written,
355251881Speter                            -1, cb->progress_baton, pool);
356251881Speter    }
357251881Speter
358251881Speter  return SVN_NO_ERROR;
359251881Speter}
360251881Speter
361251881Speter/* Treat the next LEN input bytes from CONN as "read" */
362251881Speterstatic svn_error_t *readbuf_skip(svn_ra_svn_conn_t *conn, apr_uint64_t len)
363251881Speter{
364251881Speter  do
365251881Speter  {
366251881Speter    apr_size_t buflen = conn->read_end - conn->read_ptr;
367251881Speter    apr_size_t copylen = (buflen < len) ? buflen : (apr_size_t)len;
368251881Speter    conn->read_ptr += copylen;
369251881Speter    len -= copylen;
370251881Speter    if (len == 0)
371251881Speter      break;
372251881Speter
373251881Speter    buflen = sizeof(conn->read_buf);
374251881Speter    SVN_ERR(svn_ra_svn__stream_read(conn->stream, conn->read_buf, &buflen));
375251881Speter    if (buflen == 0)
376251881Speter      return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL);
377251881Speter
378251881Speter    conn->read_end = conn->read_buf + buflen;
379251881Speter    conn->read_ptr = conn->read_buf;
380251881Speter  }
381251881Speter  while (len > 0);
382251881Speter
383251881Speter  return SVN_NO_ERROR;
384251881Speter}
385251881Speter
386251881Speter/* Read data from the socket into the read buffer, which must be empty. */
387251881Speterstatic svn_error_t *readbuf_fill(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
388251881Speter{
389251881Speter  apr_size_t len;
390251881Speter
391251881Speter  SVN_ERR_ASSERT(conn->read_ptr == conn->read_end);
392251881Speter  SVN_ERR(writebuf_flush(conn, pool));
393251881Speter  len = sizeof(conn->read_buf);
394251881Speter  SVN_ERR(readbuf_input(conn, conn->read_buf, &len, pool));
395251881Speter  conn->read_ptr = conn->read_buf;
396251881Speter  conn->read_end = conn->read_buf + len;
397251881Speter  return SVN_NO_ERROR;
398251881Speter}
399251881Speter
400251881Speterstatic APR_INLINE svn_error_t *
401251881Speterreadbuf_getchar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char *result)
402251881Speter{
403251881Speter  if (conn->read_ptr == conn->read_end)
404251881Speter    SVN_ERR(readbuf_fill(conn, pool));
405251881Speter  *result = *conn->read_ptr++;
406251881Speter  return SVN_NO_ERROR;
407251881Speter}
408251881Speter
409251881Speterstatic svn_error_t *readbuf_getchar_skip_whitespace(svn_ra_svn_conn_t *conn,
410251881Speter                                                    apr_pool_t *pool,
411251881Speter                                                    char *result)
412251881Speter{
413251881Speter  do
414251881Speter    SVN_ERR(readbuf_getchar(conn, pool, result));
415251881Speter  while (svn_iswhitespace(*result));
416251881Speter  return SVN_NO_ERROR;
417251881Speter}
418251881Speter
419251881Speter/* Read the next LEN bytes from CONN and copy them to *DATA. */
420251881Speterstatic svn_error_t *readbuf_read(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
421251881Speter                                 char *data, apr_size_t len)
422251881Speter{
423251881Speter  char *end = data + len;
424251881Speter  apr_size_t count;
425251881Speter
426251881Speter  /* Copy in an appropriate amount of data from the buffer. */
427251881Speter  data = readbuf_drain(conn, data, end);
428251881Speter
429251881Speter  /* Read large chunks directly into buffer. */
430251881Speter  while (end - data > (apr_ssize_t)sizeof(conn->read_buf))
431251881Speter    {
432251881Speter      SVN_ERR(writebuf_flush(conn, pool));
433251881Speter      count = end - data;
434251881Speter      SVN_ERR(readbuf_input(conn, data, &count, pool));
435251881Speter      data += count;
436251881Speter    }
437251881Speter
438251881Speter  while (end > data)
439251881Speter    {
440251881Speter      /* The remaining amount to read is small; fill the buffer and
441251881Speter       * copy from that. */
442251881Speter      SVN_ERR(readbuf_fill(conn, pool));
443251881Speter      data = readbuf_drain(conn, data, end);
444251881Speter    }
445251881Speter
446251881Speter  return SVN_NO_ERROR;
447251881Speter}
448251881Speter
449251881Speterstatic svn_error_t *readbuf_skip_leading_garbage(svn_ra_svn_conn_t *conn,
450251881Speter                                                 apr_pool_t *pool)
451251881Speter{
452251881Speter  char buf[256];  /* Must be smaller than sizeof(conn->read_buf) - 1. */
453251881Speter  const char *p, *end;
454251881Speter  apr_size_t len;
455251881Speter  svn_boolean_t lparen = FALSE;
456251881Speter
457251881Speter  SVN_ERR_ASSERT(conn->read_ptr == conn->read_end);
458251881Speter  while (1)
459251881Speter    {
460251881Speter      /* Read some data directly from the connection input source. */
461251881Speter      len = sizeof(buf);
462251881Speter      SVN_ERR(readbuf_input(conn, buf, &len, pool));
463251881Speter      end = buf + len;
464251881Speter
465251881Speter      /* Scan the data for '(' WS with a very simple state machine. */
466251881Speter      for (p = buf; p < end; p++)
467251881Speter        {
468251881Speter          if (lparen && svn_iswhitespace(*p))
469251881Speter            break;
470251881Speter          else
471251881Speter            lparen = (*p == '(');
472251881Speter        }
473251881Speter      if (p < end)
474251881Speter        break;
475251881Speter    }
476251881Speter
477251881Speter  /* p now points to the whitespace just after the left paren.  Fake
478251881Speter   * up the left paren and then copy what we have into the read
479251881Speter   * buffer. */
480251881Speter  conn->read_buf[0] = '(';
481251881Speter  memcpy(conn->read_buf + 1, p, end - p);
482251881Speter  conn->read_ptr = conn->read_buf;
483251881Speter  conn->read_end = conn->read_buf + 1 + (end - p);
484251881Speter  return SVN_NO_ERROR;
485251881Speter}
486251881Speter
487251881Speter/* --- WRITING DATA ITEMS --- */
488251881Speter
489251881Speterstatic svn_error_t *write_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
490251881Speter                                 apr_uint64_t number, char follow)
491251881Speter{
492251881Speter  apr_size_t written;
493251881Speter
494251881Speter  /* SVN_INT64_BUFFER_SIZE includes space for a terminating NUL that
495251881Speter   * svn__ui64toa will always append. */
496251881Speter  if (conn->write_pos + SVN_INT64_BUFFER_SIZE >= sizeof(conn->write_buf))
497251881Speter    SVN_ERR(writebuf_flush(conn, pool));
498251881Speter
499251881Speter  written = svn__ui64toa(conn->write_buf + conn->write_pos, number);
500251881Speter  conn->write_buf[conn->write_pos + written] = follow;
501251881Speter  conn->write_pos += written + 1;
502251881Speter
503251881Speter  return SVN_NO_ERROR;
504251881Speter}
505251881Speter
506251881Spetersvn_error_t *
507251881Spetersvn_ra_svn__write_number(svn_ra_svn_conn_t *conn,
508251881Speter                         apr_pool_t *pool,
509251881Speter                         apr_uint64_t number)
510251881Speter{
511251881Speter  return write_number(conn, pool, number, ' ');
512251881Speter}
513251881Speter
514251881Spetersvn_error_t *
515251881Spetersvn_ra_svn__write_string(svn_ra_svn_conn_t *conn,
516251881Speter                         apr_pool_t *pool,
517251881Speter                         const svn_string_t *str)
518251881Speter{
519251881Speter  if (str->len < 10)
520251881Speter    {
521251881Speter      SVN_ERR(writebuf_writechar(conn, pool, (char)(str->len + '0')));
522251881Speter      SVN_ERR(writebuf_writechar(conn, pool, ':'));
523251881Speter    }
524251881Speter  else
525251881Speter    SVN_ERR(write_number(conn, pool, str->len, ':'));
526251881Speter
527251881Speter  SVN_ERR(writebuf_write(conn, pool, str->data, str->len));
528251881Speter  SVN_ERR(writebuf_writechar(conn, pool, ' '));
529251881Speter  return SVN_NO_ERROR;
530251881Speter}
531251881Speter
532251881Spetersvn_error_t *
533251881Spetersvn_ra_svn__write_cstring(svn_ra_svn_conn_t *conn,
534251881Speter                          apr_pool_t *pool,
535251881Speter                          const char *s)
536251881Speter{
537251881Speter  apr_size_t len = strlen(s);
538251881Speter
539251881Speter  if (len < 10)
540251881Speter    {
541251881Speter      SVN_ERR(writebuf_writechar(conn, pool, (char)(len + '0')));
542251881Speter      SVN_ERR(writebuf_writechar(conn, pool, ':'));
543251881Speter    }
544251881Speter  else
545251881Speter    SVN_ERR(write_number(conn, pool, len, ':'));
546251881Speter
547251881Speter  SVN_ERR(writebuf_write(conn, pool, s, len));
548251881Speter  SVN_ERR(writebuf_writechar(conn, pool, ' '));
549251881Speter
550251881Speter  return SVN_NO_ERROR;
551251881Speter}
552251881Speter
553251881Spetersvn_error_t *
554251881Spetersvn_ra_svn__write_word(svn_ra_svn_conn_t *conn,
555251881Speter                       apr_pool_t *pool,
556251881Speter                       const char *word)
557251881Speter{
558251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, word, strlen(word)));
559251881Speter  SVN_ERR(writebuf_writechar(conn, pool, ' '));
560251881Speter
561251881Speter  return SVN_NO_ERROR;
562251881Speter}
563251881Speter
564251881Spetersvn_error_t *
565251881Spetersvn_ra_svn__write_proplist(svn_ra_svn_conn_t *conn,
566251881Speter                           apr_pool_t *pool,
567251881Speter                           apr_hash_t *props)
568251881Speter{
569251881Speter  apr_pool_t *iterpool;
570251881Speter  apr_hash_index_t *hi;
571251881Speter  const void *key;
572251881Speter  void *val;
573251881Speter  const char *propname;
574251881Speter  svn_string_t *propval;
575251881Speter
576251881Speter  if (props)
577251881Speter    {
578251881Speter      iterpool = svn_pool_create(pool);
579251881Speter      for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
580251881Speter        {
581251881Speter          svn_pool_clear(iterpool);
582251881Speter          apr_hash_this(hi, &key, NULL, &val);
583251881Speter          propname = key;
584251881Speter          propval = val;
585251881Speter          SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "cs",
586251881Speter                                          propname, propval));
587251881Speter        }
588251881Speter      svn_pool_destroy(iterpool);
589251881Speter    }
590251881Speter
591251881Speter  return SVN_NO_ERROR;
592251881Speter}
593251881Speter
594251881Spetersvn_error_t *
595251881Spetersvn_ra_svn__start_list(svn_ra_svn_conn_t *conn,
596251881Speter                       apr_pool_t *pool)
597251881Speter{
598251881Speter  if (conn->write_pos + 2 <= sizeof(conn->write_buf))
599251881Speter    {
600251881Speter      conn->write_buf[conn->write_pos] = '(';
601251881Speter      conn->write_buf[conn->write_pos+1] = ' ';
602251881Speter      conn->write_pos += 2;
603251881Speter      return SVN_NO_ERROR;
604251881Speter    }
605251881Speter
606251881Speter  return writebuf_write(conn, pool, "( ", 2);
607251881Speter}
608251881Speter
609251881Spetersvn_error_t *
610251881Spetersvn_ra_svn__end_list(svn_ra_svn_conn_t *conn,
611251881Speter                     apr_pool_t *pool)
612251881Speter{
613251881Speter  if (conn->write_pos + 2 <= sizeof(conn->write_buf))
614251881Speter  {
615251881Speter    conn->write_buf[conn->write_pos] = ')';
616251881Speter    conn->write_buf[conn->write_pos+1] = ' ';
617251881Speter    conn->write_pos += 2;
618251881Speter    return SVN_NO_ERROR;
619251881Speter  }
620251881Speter
621251881Speter  return writebuf_write(conn, pool, ") ", 2);
622251881Speter}
623251881Speter
624251881Spetersvn_error_t *
625251881Spetersvn_ra_svn__flush(svn_ra_svn_conn_t *conn,
626251881Speter                  apr_pool_t *pool)
627251881Speter{
628251881Speter  SVN_ERR(writebuf_flush(conn, pool));
629251881Speter  conn->may_check_for_error = TRUE;
630251881Speter
631251881Speter  return SVN_NO_ERROR;
632251881Speter}
633251881Speter
634251881Speter/* --- WRITING TUPLES --- */
635251881Speter
636251881Speterstatic svn_error_t *
637251881Spetervwrite_tuple_cstring(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
638251881Speter{
639251881Speter  const char *cstr = va_arg(*ap, const char *);
640251881Speter  SVN_ERR_ASSERT(cstr);
641251881Speter  return svn_ra_svn__write_cstring(conn, pool, cstr);
642251881Speter}
643251881Speter
644251881Speterstatic svn_error_t *
645251881Spetervwrite_tuple_cstring_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
646251881Speter{
647251881Speter  const char *cstr = va_arg(*ap, const char *);
648251881Speter  return cstr ? svn_ra_svn__write_cstring(conn, pool, cstr) : SVN_NO_ERROR;
649251881Speter}
650251881Speter
651251881Speterstatic svn_error_t *
652251881Spetervwrite_tuple_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
653251881Speter{
654251881Speter  const svn_string_t *str = va_arg(*ap, const svn_string_t *);
655251881Speter  SVN_ERR_ASSERT(str);
656251881Speter  return svn_ra_svn__write_string(conn, pool, str);
657251881Speter}
658251881Speter
659251881Speterstatic svn_error_t *
660251881Spetervwrite_tuple_string_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
661251881Speter{
662251881Speter  const svn_string_t *str = va_arg(*ap, const svn_string_t *);
663251881Speter  return str ? svn_ra_svn__write_string(conn, pool, str) : SVN_NO_ERROR;
664251881Speter}
665251881Speter
666251881Speterstatic svn_error_t *
667251881Spetervwrite_tuple_word(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
668251881Speter{
669251881Speter  const char *cstr = va_arg(*ap, const char *);
670251881Speter  SVN_ERR_ASSERT(cstr);
671251881Speter  return svn_ra_svn__write_word(conn, pool, cstr);
672251881Speter}
673251881Speter
674251881Speterstatic svn_error_t *
675251881Spetervwrite_tuple_word_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
676251881Speter{
677251881Speter  const char *cstr = va_arg(*ap, const char *);
678251881Speter  return cstr ? svn_ra_svn__write_word(conn, pool, cstr) : SVN_NO_ERROR;
679251881Speter}
680251881Speter
681251881Speterstatic svn_error_t *
682251881Spetervwrite_tuple_revision(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
683251881Speter{
684251881Speter  svn_revnum_t rev = va_arg(*ap, svn_revnum_t);
685251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
686251881Speter  return svn_ra_svn__write_number(conn, pool, rev);
687251881Speter}
688251881Speter
689251881Speterstatic svn_error_t *
690251881Spetervwrite_tuple_revision_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
691251881Speter{
692251881Speter  svn_revnum_t rev = va_arg(*ap, svn_revnum_t);
693251881Speter  return SVN_IS_VALID_REVNUM(rev)
694251881Speter       ? svn_ra_svn__write_number(conn, pool, rev)
695251881Speter       : SVN_NO_ERROR;
696251881Speter}
697251881Speter
698251881Speterstatic svn_error_t *
699251881Spetervwrite_tuple_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
700251881Speter{
701251881Speter  return svn_ra_svn__write_number(conn, pool, va_arg(*ap, apr_uint64_t));
702251881Speter}
703251881Speter
704251881Speterstatic svn_error_t *
705251881Spetervwrite_tuple_boolean(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
706251881Speter{
707251881Speter  const char *cstr = va_arg(*ap, svn_boolean_t) ? "true" : "false";
708251881Speter  return svn_ra_svn__write_word(conn, pool, cstr);
709251881Speter}
710251881Speter
711251881Speterstatic svn_error_t *
712251881Speterwrite_tuple_cstring(svn_ra_svn_conn_t *conn,
713251881Speter                    apr_pool_t *pool,
714251881Speter                    const char *cstr)
715251881Speter{
716251881Speter  SVN_ERR_ASSERT(cstr);
717251881Speter  return svn_ra_svn__write_cstring(conn, pool, cstr);
718251881Speter}
719251881Speter
720251881Speterstatic svn_error_t *
721251881Speterwrite_tuple_cstring_opt(svn_ra_svn_conn_t *conn,
722251881Speter                        apr_pool_t *pool,
723251881Speter                        const char *cstr)
724251881Speter{
725251881Speter  return cstr ? svn_ra_svn__write_cstring(conn, pool, cstr) : SVN_NO_ERROR;
726251881Speter}
727251881Speter
728251881Speterstatic svn_error_t *
729251881Speterwrite_tuple_string(svn_ra_svn_conn_t *conn,
730251881Speter                   apr_pool_t *pool,
731251881Speter                   const svn_string_t *str)
732251881Speter{
733251881Speter  SVN_ERR_ASSERT(str);
734251881Speter  return svn_ra_svn__write_string(conn, pool, str);
735251881Speter}
736251881Speter
737251881Speterstatic svn_error_t *
738251881Speterwrite_tuple_string_opt(svn_ra_svn_conn_t *conn,
739251881Speter                       apr_pool_t *pool,
740251881Speter                       const svn_string_t *str)
741251881Speter{
742251881Speter  return str ? svn_ra_svn__write_string(conn, pool, str) : SVN_NO_ERROR;
743251881Speter}
744251881Speter
745251881Speterstatic svn_error_t *
746251881Speterwrite_tuple_start_list(svn_ra_svn_conn_t *conn,
747251881Speter                       apr_pool_t *pool)
748251881Speter{
749251881Speter  return svn_ra_svn__start_list(conn, pool);
750251881Speter}
751251881Speter
752251881Speterstatic svn_error_t *
753251881Speterwrite_tuple_end_list(svn_ra_svn_conn_t *conn,
754251881Speter                     apr_pool_t *pool)
755251881Speter{
756251881Speter  return svn_ra_svn__end_list(conn, pool);
757251881Speter}
758251881Speter
759251881Speterstatic svn_error_t *
760251881Speterwrite_tuple_revision(svn_ra_svn_conn_t *conn,
761251881Speter                     apr_pool_t *pool,
762251881Speter                     svn_revnum_t rev)
763251881Speter{
764251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
765251881Speter  return svn_ra_svn__write_number(conn, pool, rev);
766251881Speter}
767251881Speter
768251881Speterstatic svn_error_t *
769251881Speterwrite_tuple_revision_opt(svn_ra_svn_conn_t *conn,
770251881Speter                         apr_pool_t *pool,
771251881Speter                         svn_revnum_t rev)
772251881Speter{
773251881Speter  return SVN_IS_VALID_REVNUM(rev)
774251881Speter       ? svn_ra_svn__write_number(conn, pool, rev)
775251881Speter       : SVN_NO_ERROR;
776251881Speter}
777251881Speter
778251881Speterstatic svn_error_t *
779251881Speterwrite_tuple_boolean(svn_ra_svn_conn_t *conn,
780251881Speter                    apr_pool_t *pool,
781251881Speter                    svn_boolean_t value)
782251881Speter{
783251881Speter  const char *cstr = value ? "true" : "false";
784251881Speter  return svn_ra_svn__write_word(conn, pool, cstr);
785251881Speter}
786251881Speter
787251881Speterstatic svn_error_t *
788251881Speterwrite_tuple_depth(svn_ra_svn_conn_t *conn,
789251881Speter                  apr_pool_t *pool,
790251881Speter                  svn_depth_t depth)
791251881Speter{
792251881Speter  return svn_ra_svn__write_word(conn, pool, svn_depth_to_word(depth));
793251881Speter}
794251881Speter
795251881Speter
796251881Speterstatic svn_error_t *
797251881Speterwrite_cmd_add_node(svn_ra_svn_conn_t *conn,
798251881Speter                   apr_pool_t *pool,
799251881Speter                   const char *path,
800251881Speter                   const char *parent_token,
801251881Speter                   const char *token,
802251881Speter                   const char *copy_path,
803251881Speter                   svn_revnum_t copy_rev)
804251881Speter{
805251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
806251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, parent_token));
807251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, token));
808251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
809251881Speter  SVN_ERR(write_tuple_cstring_opt(conn, pool, copy_path));
810251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, copy_rev));
811251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
812251881Speter
813251881Speter  return SVN_NO_ERROR;
814251881Speter}
815251881Speter
816251881Speterstatic svn_error_t *
817251881Speterwrite_cmd_open_node(svn_ra_svn_conn_t *conn,
818251881Speter                    apr_pool_t *pool,
819251881Speter                    const char *path,
820251881Speter                    const char *parent_token,
821251881Speter                    const char *token,
822251881Speter                    svn_revnum_t rev)
823251881Speter{
824251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
825251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, parent_token));
826251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, token));
827251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
828251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
829251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
830251881Speter
831251881Speter  return SVN_NO_ERROR;
832251881Speter}
833251881Speter
834251881Speterstatic svn_error_t *
835251881Speterwrite_cmd_change_node_prop(svn_ra_svn_conn_t *conn,
836251881Speter                           apr_pool_t *pool,
837251881Speter                           const char *token,
838251881Speter                           const char *name,
839251881Speter                           const svn_string_t *value)
840251881Speter{
841251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, token));
842251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, name));
843251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
844251881Speter  SVN_ERR(write_tuple_string_opt(conn, pool, value));
845251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
846251881Speter
847251881Speter  return SVN_NO_ERROR;
848251881Speter}
849251881Speter
850251881Speterstatic svn_error_t *
851251881Speterwrite_cmd_absent_node(svn_ra_svn_conn_t *conn,
852251881Speter                      apr_pool_t *pool,
853251881Speter                      const char *path,
854251881Speter                      const char *token)
855251881Speter{
856251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
857251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, token));
858251881Speter
859251881Speter  return SVN_NO_ERROR;
860251881Speter}
861251881Speter
862251881Speter
863251881Speter
864251881Speter
865251881Speterstatic svn_error_t *vwrite_tuple(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
866251881Speter                                 const char *fmt, va_list *ap)
867251881Speter{
868251881Speter  svn_boolean_t opt = FALSE;
869251881Speter
870251881Speter  if (*fmt == '!')
871251881Speter    fmt++;
872251881Speter  else
873251881Speter    SVN_ERR(svn_ra_svn__start_list(conn, pool));
874251881Speter  for (; *fmt; fmt++)
875251881Speter    {
876251881Speter      if (*fmt == 'c')
877251881Speter        SVN_ERR(opt ? vwrite_tuple_cstring_opt(conn, pool, ap)
878251881Speter                    : vwrite_tuple_cstring(conn, pool, ap));
879251881Speter      else if (*fmt == 's')
880251881Speter        SVN_ERR(opt ? vwrite_tuple_string_opt(conn, pool, ap)
881251881Speter                    : vwrite_tuple_string(conn, pool, ap));
882251881Speter      else if (*fmt == '(' && !opt)
883251881Speter        SVN_ERR(write_tuple_start_list(conn, pool));
884251881Speter      else if (*fmt == ')')
885251881Speter        {
886251881Speter          SVN_ERR(write_tuple_end_list(conn, pool));
887251881Speter          opt = FALSE;
888251881Speter        }
889251881Speter      else if (*fmt == '?')
890251881Speter        opt = TRUE;
891251881Speter      else if (*fmt == 'w')
892251881Speter        SVN_ERR(opt ? vwrite_tuple_word_opt(conn, pool, ap)
893251881Speter                    : vwrite_tuple_word(conn, pool, ap));
894251881Speter      else if (*fmt == 'r')
895251881Speter        SVN_ERR(opt ? vwrite_tuple_revision_opt(conn, pool, ap)
896251881Speter                    : vwrite_tuple_revision(conn, pool, ap));
897251881Speter      else if (*fmt == 'n' && !opt)
898251881Speter        SVN_ERR(vwrite_tuple_number(conn, pool, ap));
899251881Speter      else if (*fmt == 'b' && !opt)
900251881Speter        SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
901251881Speter      else if (*fmt == '!' && !*(fmt + 1))
902251881Speter        return SVN_NO_ERROR;
903251881Speter      else
904251881Speter        SVN_ERR_MALFUNCTION();
905251881Speter    }
906251881Speter  SVN_ERR(svn_ra_svn__end_list(conn, pool));
907251881Speter  return SVN_NO_ERROR;
908251881Speter}
909251881Speter
910251881Spetersvn_error_t *
911251881Spetersvn_ra_svn__write_tuple(svn_ra_svn_conn_t *conn,
912251881Speter                        apr_pool_t *pool,
913251881Speter                        const char *fmt, ...)
914251881Speter{
915251881Speter  svn_error_t *err;
916251881Speter  va_list ap;
917251881Speter
918251881Speter  va_start(ap, fmt);
919251881Speter  err = vwrite_tuple(conn, pool, fmt, &ap);
920251881Speter  va_end(ap);
921251881Speter  return err;
922251881Speter}
923251881Speter
924251881Speter/* --- READING DATA ITEMS --- */
925251881Speter
926251881Speter/* Read LEN bytes from CONN into already-allocated structure ITEM.
927251881Speter * Afterwards, *ITEM is of type 'SVN_RA_SVN_STRING', and its string
928251881Speter * data is allocated in POOL. */
929251881Speterstatic svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
930251881Speter                                svn_ra_svn_item_t *item, apr_uint64_t len64)
931251881Speter{
932251881Speter  svn_stringbuf_t *stringbuf;
933251881Speter  apr_size_t len = (apr_size_t)len64;
934251881Speter  apr_size_t readbuf_len;
935251881Speter  char *dest;
936251881Speter
937251881Speter  /* We can't store strings longer than the maximum size of apr_size_t,
938251881Speter   * so check for wrapping */
939251881Speter  if (len64 > APR_SIZE_MAX)
940251881Speter    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
941251881Speter                            _("String length larger than maximum"));
942251881Speter
943251881Speter  /* Read the string in chunks.  The chunk size is large enough to avoid
944251881Speter   * re-allocation in typical cases, and small enough to ensure we do not
945251881Speter   * pre-allocate an unreasonable amount of memory if (perhaps due to
946251881Speter   * network data corruption or a DOS attack), we receive a bogus claim that
947251881Speter   * a very long string is going to follow.  In that case, we start small
948251881Speter   * and wait for all that data to actually show up.  This does not fully
949251881Speter   * prevent DOS attacks but makes them harder (you have to actually send
950251881Speter   * gigabytes of data). */
951251881Speter  readbuf_len = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD
952251881Speter                    ? len
953251881Speter                    : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD;
954251881Speter  stringbuf = svn_stringbuf_create_ensure(readbuf_len, pool);
955251881Speter  dest = stringbuf->data;
956251881Speter
957251881Speter  /* Read remaining string data directly into the string structure.
958251881Speter   * Do it iteratively, if necessary.  */
959251881Speter  while (readbuf_len)
960251881Speter    {
961251881Speter      SVN_ERR(readbuf_read(conn, pool, dest, readbuf_len));
962251881Speter
963251881Speter      stringbuf->len += readbuf_len;
964251881Speter      len -= readbuf_len;
965251881Speter
966251881Speter      /* Early exit. In most cases, strings can be read in the first
967251881Speter       * iteration. */
968251881Speter      if (len == 0)
969251881Speter        break;
970251881Speter
971251881Speter      /* Prepare next iteration: determine length of chunk to read
972251881Speter       * and re-alloc the string buffer. */
973251881Speter      readbuf_len
974251881Speter        = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD
975251881Speter              ? len
976251881Speter              : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD;
977251881Speter
978251881Speter      svn_stringbuf_ensure(stringbuf, stringbuf->len + readbuf_len);
979251881Speter      dest = stringbuf->data + stringbuf->len;
980251881Speter    }
981251881Speter
982251881Speter  /* zero-terminate the string */
983251881Speter  stringbuf->data[stringbuf->len] = '\0';
984251881Speter
985251881Speter  /* Return the string properly wrapped into an RA_SVN item. */
986251881Speter  item->kind = SVN_RA_SVN_STRING;
987251881Speter  item->u.string = svn_stringbuf__morph_into_string(stringbuf);
988251881Speter
989251881Speter  return SVN_NO_ERROR;
990251881Speter}
991251881Speter
992251881Speter/* Given the first non-whitespace character FIRST_CHAR, read an item
993251881Speter * into the already allocated structure ITEM.  LEVEL should be set
994251881Speter * to 0 for the first call and is used to enforce a recurssion limit
995251881Speter * on the parser. */
996251881Speterstatic svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
997251881Speter                              svn_ra_svn_item_t *item, char first_char,
998251881Speter                              int level)
999251881Speter{
1000251881Speter  char c = first_char;
1001251881Speter  apr_uint64_t val;
1002251881Speter  svn_stringbuf_t *str;
1003251881Speter  svn_ra_svn_item_t *listitem;
1004251881Speter
1005251881Speter  if (++level >= 64)
1006251881Speter    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1007251881Speter                            _("Too many nested items"));
1008251881Speter
1009251881Speter
1010251881Speter  /* Determine the item type and read it in.  Make sure that c is the
1011251881Speter   * first character at the end of the item so we can test to make
1012251881Speter   * sure it's whitespace. */
1013251881Speter  if (svn_ctype_isdigit(c))
1014251881Speter    {
1015251881Speter      /* It's a number or a string.  Read the number part, either way. */
1016251881Speter      val = c - '0';
1017251881Speter      while (1)
1018251881Speter        {
1019251881Speter          apr_uint64_t prev_val = val;
1020251881Speter          SVN_ERR(readbuf_getchar(conn, pool, &c));
1021251881Speter          if (!svn_ctype_isdigit(c))
1022251881Speter            break;
1023251881Speter          val = val * 10 + (c - '0');
1024251881Speter          /* val wrapped past maximum value? */
1025251881Speter          if (prev_val >= (APR_UINT64_MAX / 10) && (val / 10) != prev_val)
1026251881Speter            return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1027251881Speter                                    _("Number is larger than maximum"));
1028251881Speter        }
1029251881Speter      if (c == ':')
1030251881Speter        {
1031251881Speter          /* It's a string. */
1032251881Speter          SVN_ERR(read_string(conn, pool, item, val));
1033251881Speter          SVN_ERR(readbuf_getchar(conn, pool, &c));
1034251881Speter        }
1035251881Speter      else
1036251881Speter        {
1037251881Speter          /* It's a number. */
1038251881Speter          item->kind = SVN_RA_SVN_NUMBER;
1039251881Speter          item->u.number = val;
1040251881Speter        }
1041251881Speter    }
1042251881Speter  else if (svn_ctype_isalpha(c))
1043251881Speter    {
1044251881Speter      /* It's a word. */
1045251881Speter      str = svn_stringbuf_create_ensure(16, pool);
1046251881Speter      svn_stringbuf_appendbyte(str, c);
1047251881Speter      while (1)
1048251881Speter        {
1049251881Speter          SVN_ERR(readbuf_getchar(conn, pool, &c));
1050251881Speter          if (!svn_ctype_isalnum(c) && c != '-')
1051251881Speter            break;
1052251881Speter          svn_stringbuf_appendbyte(str, c);
1053251881Speter        }
1054251881Speter      item->kind = SVN_RA_SVN_WORD;
1055251881Speter      item->u.word = str->data;
1056251881Speter    }
1057251881Speter  else if (c == '(')
1058251881Speter    {
1059251881Speter      /* Read in the list items. */
1060251881Speter      item->kind = SVN_RA_SVN_LIST;
1061251881Speter      item->u.list = apr_array_make(pool, 4, sizeof(svn_ra_svn_item_t));
1062251881Speter      while (1)
1063251881Speter        {
1064251881Speter          SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1065251881Speter          if (c == ')')
1066251881Speter            break;
1067251881Speter          listitem = apr_array_push(item->u.list);
1068251881Speter          SVN_ERR(read_item(conn, pool, listitem, c, level));
1069251881Speter        }
1070251881Speter      SVN_ERR(readbuf_getchar(conn, pool, &c));
1071251881Speter    }
1072251881Speter
1073251881Speter  if (!svn_iswhitespace(c))
1074251881Speter    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1075251881Speter                            _("Malformed network data"));
1076251881Speter  return SVN_NO_ERROR;
1077251881Speter}
1078251881Speter
1079251881Speter/* Given the first non-whitespace character FIRST_CHAR, read the first
1080251881Speter * command (word) encountered in CONN into *ITEM.  If ITEM is NULL, skip
1081251881Speter * to the end of the current list.  Use POOL for allocations. */
1082251881Speterstatic svn_error_t *
1083251881Speterread_command_only(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1084251881Speter                  const char **item, char first_char)
1085251881Speter{
1086251881Speter  char c = first_char;
1087251881Speter
1088251881Speter  /* Determine the item type and read it in.  Make sure that c is the
1089251881Speter  * first character at the end of the item so we can test to make
1090251881Speter  * sure it's whitespace. */
1091251881Speter  if (svn_ctype_isdigit(c))
1092251881Speter    {
1093251881Speter      /* It's a number or a string.  Read the number part, either way. */
1094251881Speter      apr_uint64_t val, prev_val=0;
1095251881Speter      val = c - '0';
1096251881Speter      while (1)
1097251881Speter        {
1098251881Speter          prev_val = val;
1099251881Speter          SVN_ERR(readbuf_getchar(conn, pool, &c));
1100251881Speter          if (!svn_ctype_isdigit(c))
1101251881Speter            break;
1102251881Speter          val = val * 10 + (c - '0');
1103251881Speter          if (prev_val >= (APR_UINT64_MAX / 10)) /* > maximum value? */
1104251881Speter            return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1105251881Speter                                    _("Number is larger than maximum"));
1106251881Speter        }
1107251881Speter      if (c == ':')
1108251881Speter        {
1109251881Speter          /* It's a string. */
1110251881Speter          SVN_ERR(readbuf_skip(conn, val));
1111251881Speter          SVN_ERR(readbuf_getchar(conn, pool, &c));
1112251881Speter        }
1113251881Speter    }
1114251881Speter  else if (svn_ctype_isalpha(c))
1115251881Speter    {
1116251881Speter      /* It's a word. */
1117251881Speter      if (item)
1118251881Speter        {
1119251881Speter          /* This is the word we want to read */
1120251881Speter
1121251881Speter          char *buf = apr_palloc(pool, 32);
1122251881Speter          apr_size_t len = 1;
1123251881Speter          buf[0] = c;
1124251881Speter
1125251881Speter          while (1)
1126251881Speter            {
1127251881Speter              SVN_ERR(readbuf_getchar(conn, pool, &c));
1128251881Speter              if (!svn_ctype_isalnum(c) && c != '-')
1129251881Speter                break;
1130251881Speter              buf[len] = c;
1131251881Speter              if (++len == 32)
1132251881Speter                return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1133251881Speter                                        _("Word too long"));
1134251881Speter            }
1135251881Speter          buf[len] = 0;
1136251881Speter          *item = buf;
1137251881Speter        }
1138251881Speter      else
1139251881Speter        {
1140251881Speter          /* we don't need the actual word, just skip it */
1141251881Speter          do
1142251881Speter          {
1143251881Speter            SVN_ERR(readbuf_getchar(conn, pool, &c));
1144251881Speter          }
1145251881Speter          while (svn_ctype_isalnum(c) || c == '-');
1146251881Speter        }
1147251881Speter    }
1148251881Speter  else if (c == '(')
1149251881Speter    {
1150251881Speter      /* Read in the list items. */
1151251881Speter      while (1)
1152251881Speter        {
1153251881Speter          SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1154251881Speter          if (c == ')')
1155251881Speter            break;
1156251881Speter
1157251881Speter          if (item && *item == NULL)
1158251881Speter            SVN_ERR(read_command_only(conn, pool, item, c));
1159251881Speter          else
1160251881Speter            SVN_ERR(read_command_only(conn, pool, NULL, c));
1161251881Speter        }
1162251881Speter      SVN_ERR(readbuf_getchar(conn, pool, &c));
1163251881Speter    }
1164251881Speter
1165251881Speter  return SVN_NO_ERROR;
1166251881Speter}
1167251881Speter
1168251881Spetersvn_error_t *
1169251881Spetersvn_ra_svn__read_item(svn_ra_svn_conn_t *conn,
1170251881Speter                      apr_pool_t *pool,
1171251881Speter                      svn_ra_svn_item_t **item)
1172251881Speter{
1173251881Speter  char c;
1174251881Speter
1175251881Speter  /* Allocate space, read the first character, and then do the rest of
1176251881Speter   * the work.  This makes sense because of the way lists are read. */
1177251881Speter  *item = apr_palloc(pool, sizeof(**item));
1178251881Speter  SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1179251881Speter  return read_item(conn, pool, *item, c, 0);
1180251881Speter}
1181251881Speter
1182251881Spetersvn_error_t *
1183251881Spetersvn_ra_svn__skip_leading_garbage(svn_ra_svn_conn_t *conn,
1184251881Speter                                 apr_pool_t *pool)
1185251881Speter{
1186251881Speter  return readbuf_skip_leading_garbage(conn, pool);
1187251881Speter}
1188251881Speter
1189251881Speter/* --- READING AND PARSING TUPLES --- */
1190251881Speter
1191251881Speter/* Parse a tuple of svn_ra_svn_item_t *'s.  Advance *FMT to the end of the
1192251881Speter * tuple specification and advance AP by the corresponding arguments. */
1193251881Speterstatic svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *pool,
1194251881Speter                                 const char **fmt, va_list *ap)
1195251881Speter{
1196251881Speter  int count, nesting_level;
1197251881Speter  svn_ra_svn_item_t *elt;
1198251881Speter
1199251881Speter  for (count = 0; **fmt && count < items->nelts; (*fmt)++, count++)
1200251881Speter    {
1201251881Speter      /* '?' just means the tuple may stop; skip past it. */
1202251881Speter      if (**fmt == '?')
1203251881Speter        (*fmt)++;
1204251881Speter      elt = &APR_ARRAY_IDX(items, count, svn_ra_svn_item_t);
1205251881Speter      if (**fmt == 'n' && elt->kind == SVN_RA_SVN_NUMBER)
1206251881Speter        *va_arg(*ap, apr_uint64_t *) = elt->u.number;
1207251881Speter      else if (**fmt == 'r' && elt->kind == SVN_RA_SVN_NUMBER)
1208251881Speter        *va_arg(*ap, svn_revnum_t *) = (svn_revnum_t) elt->u.number;
1209251881Speter      else if (**fmt == 's' && elt->kind == SVN_RA_SVN_STRING)
1210251881Speter        *va_arg(*ap, svn_string_t **) = elt->u.string;
1211251881Speter      else if (**fmt == 'c' && elt->kind == SVN_RA_SVN_STRING)
1212251881Speter        *va_arg(*ap, const char **) = elt->u.string->data;
1213251881Speter      else if (**fmt == 'w' && elt->kind == SVN_RA_SVN_WORD)
1214251881Speter        *va_arg(*ap, const char **) = elt->u.word;
1215251881Speter      else if (**fmt == 'b' && elt->kind == SVN_RA_SVN_WORD)
1216251881Speter        {
1217251881Speter          if (strcmp(elt->u.word, "true") == 0)
1218251881Speter            *va_arg(*ap, svn_boolean_t *) = TRUE;
1219251881Speter          else if (strcmp(elt->u.word, "false") == 0)
1220251881Speter            *va_arg(*ap, svn_boolean_t *) = FALSE;
1221251881Speter          else
1222251881Speter            break;
1223251881Speter        }
1224251881Speter      else if (**fmt == 'B' && elt->kind == SVN_RA_SVN_WORD)
1225251881Speter        {
1226251881Speter          if (strcmp(elt->u.word, "true") == 0)
1227251881Speter            *va_arg(*ap, apr_uint64_t *) = TRUE;
1228251881Speter          else if (strcmp(elt->u.word, "false") == 0)
1229251881Speter            *va_arg(*ap, apr_uint64_t *) = FALSE;
1230251881Speter          else
1231251881Speter            break;
1232251881Speter        }
1233251881Speter      else if (**fmt == 'l' && elt->kind == SVN_RA_SVN_LIST)
1234251881Speter        *va_arg(*ap, apr_array_header_t **) = elt->u.list;
1235251881Speter      else if (**fmt == '(' && elt->kind == SVN_RA_SVN_LIST)
1236251881Speter        {
1237251881Speter          (*fmt)++;
1238251881Speter          SVN_ERR(vparse_tuple(elt->u.list, pool, fmt, ap));
1239251881Speter        }
1240251881Speter      else if (**fmt == ')')
1241251881Speter        return SVN_NO_ERROR;
1242251881Speter      else
1243251881Speter        break;
1244251881Speter    }
1245251881Speter  if (**fmt == '?')
1246251881Speter    {
1247251881Speter      nesting_level = 0;
1248251881Speter      for (; **fmt; (*fmt)++)
1249251881Speter        {
1250251881Speter          switch (**fmt)
1251251881Speter            {
1252251881Speter            case '?':
1253251881Speter              break;
1254251881Speter            case 'r':
1255251881Speter              *va_arg(*ap, svn_revnum_t *) = SVN_INVALID_REVNUM;
1256251881Speter              break;
1257251881Speter            case 's':
1258251881Speter              *va_arg(*ap, svn_string_t **) = NULL;
1259251881Speter              break;
1260251881Speter            case 'c':
1261251881Speter            case 'w':
1262251881Speter              *va_arg(*ap, const char **) = NULL;
1263251881Speter              break;
1264251881Speter            case 'l':
1265251881Speter              *va_arg(*ap, apr_array_header_t **) = NULL;
1266251881Speter              break;
1267251881Speter            case 'B':
1268251881Speter            case 'n':
1269251881Speter              *va_arg(*ap, apr_uint64_t *) = SVN_RA_SVN_UNSPECIFIED_NUMBER;
1270251881Speter              break;
1271251881Speter            case '(':
1272251881Speter              nesting_level++;
1273251881Speter              break;
1274251881Speter            case ')':
1275251881Speter              if (--nesting_level < 0)
1276251881Speter                return SVN_NO_ERROR;
1277251881Speter              break;
1278251881Speter            default:
1279251881Speter              SVN_ERR_MALFUNCTION();
1280251881Speter            }
1281251881Speter        }
1282251881Speter    }
1283251881Speter  if (**fmt && **fmt != ')')
1284251881Speter    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1285251881Speter                            _("Malformed network data"));
1286251881Speter  return SVN_NO_ERROR;
1287251881Speter}
1288251881Speter
1289251881Spetersvn_error_t *
1290251881Spetersvn_ra_svn__parse_tuple(const apr_array_header_t *list,
1291251881Speter                        apr_pool_t *pool,
1292251881Speter                        const char *fmt, ...)
1293251881Speter{
1294251881Speter  svn_error_t *err;
1295251881Speter  va_list ap;
1296251881Speter
1297251881Speter  va_start(ap, fmt);
1298251881Speter  err = vparse_tuple(list, pool, &fmt, &ap);
1299251881Speter  va_end(ap);
1300251881Speter  return err;
1301251881Speter}
1302251881Speter
1303251881Spetersvn_error_t *
1304251881Spetersvn_ra_svn__read_tuple(svn_ra_svn_conn_t *conn,
1305251881Speter                       apr_pool_t *pool,
1306251881Speter                       const char *fmt, ...)
1307251881Speter{
1308251881Speter  va_list ap;
1309251881Speter  svn_ra_svn_item_t *item;
1310251881Speter  svn_error_t *err;
1311251881Speter
1312251881Speter  SVN_ERR(svn_ra_svn__read_item(conn, pool, &item));
1313251881Speter  if (item->kind != SVN_RA_SVN_LIST)
1314251881Speter    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1315251881Speter                            _("Malformed network data"));
1316251881Speter  va_start(ap, fmt);
1317251881Speter  err = vparse_tuple(item->u.list, pool, &fmt, &ap);
1318251881Speter  va_end(ap);
1319251881Speter  return err;
1320251881Speter}
1321251881Speter
1322251881Spetersvn_error_t *
1323251881Spetersvn_ra_svn__read_command_only(svn_ra_svn_conn_t *conn,
1324251881Speter                              apr_pool_t *pool,
1325251881Speter                              const char **command)
1326251881Speter{
1327251881Speter  char c;
1328251881Speter  SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1329251881Speter
1330251881Speter  *command = NULL;
1331251881Speter  return read_command_only(conn, pool, command, c);
1332251881Speter}
1333251881Speter
1334251881Speter
1335251881Spetersvn_error_t *
1336251881Spetersvn_ra_svn__parse_proplist(const apr_array_header_t *list,
1337251881Speter                           apr_pool_t *pool,
1338251881Speter                           apr_hash_t **props)
1339251881Speter{
1340251881Speter  char *name;
1341251881Speter  svn_string_t *value;
1342251881Speter  svn_ra_svn_item_t *elt;
1343251881Speter  int i;
1344251881Speter
1345251881Speter  *props = apr_hash_make(pool);
1346251881Speter  for (i = 0; i < list->nelts; i++)
1347251881Speter    {
1348251881Speter      elt = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
1349251881Speter      if (elt->kind != SVN_RA_SVN_LIST)
1350251881Speter        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1351251881Speter                                _("Proplist element not a list"));
1352251881Speter      SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, pool, "cs",
1353251881Speter                                      &name, &value));
1354251881Speter      svn_hash_sets(*props, name, value);
1355251881Speter    }
1356251881Speter
1357251881Speter  return SVN_NO_ERROR;
1358251881Speter}
1359251881Speter
1360251881Speter
1361251881Speter/* --- READING AND WRITING COMMANDS AND RESPONSES --- */
1362251881Speter
1363251881Spetersvn_error_t *svn_ra_svn__locate_real_error_child(svn_error_t *err)
1364251881Speter{
1365251881Speter  svn_error_t *this_link;
1366251881Speter
1367251881Speter  SVN_ERR_ASSERT(err);
1368251881Speter
1369251881Speter  for (this_link = err;
1370251881Speter       this_link && (this_link->apr_err == SVN_ERR_RA_SVN_CMD_ERR);
1371251881Speter       this_link = this_link->child)
1372251881Speter    ;
1373251881Speter
1374251881Speter  SVN_ERR_ASSERT(this_link);
1375251881Speter  return this_link;
1376251881Speter}
1377251881Speter
1378251881Spetersvn_error_t *svn_ra_svn__handle_failure_status(const apr_array_header_t *params,
1379251881Speter                                               apr_pool_t *pool)
1380251881Speter{
1381251881Speter  const char *message, *file;
1382251881Speter  svn_error_t *err = NULL;
1383251881Speter  svn_ra_svn_item_t *elt;
1384251881Speter  int i;
1385251881Speter  apr_uint64_t apr_err, line;
1386251881Speter  apr_pool_t *subpool = svn_pool_create(pool);
1387251881Speter
1388251881Speter  if (params->nelts == 0)
1389251881Speter    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1390251881Speter                            _("Empty error list"));
1391251881Speter
1392251881Speter  /* Rebuild the error list from the end, to avoid reversing the order. */
1393251881Speter  for (i = params->nelts - 1; i >= 0; i--)
1394251881Speter    {
1395251881Speter      svn_pool_clear(subpool);
1396251881Speter      elt = &APR_ARRAY_IDX(params, i, svn_ra_svn_item_t);
1397251881Speter      if (elt->kind != SVN_RA_SVN_LIST)
1398251881Speter        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1399251881Speter                                _("Malformed error list"));
1400251881Speter      SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, subpool, "nccn",
1401251881Speter                                      &apr_err, &message, &file, &line));
1402251881Speter      /* The message field should have been optional, but we can't
1403251881Speter         easily change that, so "" means a nonexistent message. */
1404251881Speter      if (!*message)
1405251881Speter        message = NULL;
1406251881Speter
1407251881Speter      /* Skip over links in the error chain that were intended only to
1408251881Speter         exist on the server (to wrap real errors intended for the
1409251881Speter         client) but accidentally got included in the server's actual
1410251881Speter         response. */
1411251881Speter      if ((apr_status_t)apr_err != SVN_ERR_RA_SVN_CMD_ERR)
1412251881Speter        {
1413251881Speter          err = svn_error_create((apr_status_t)apr_err, err, message);
1414251881Speter          err->file = apr_pstrdup(err->pool, file);
1415251881Speter          err->line = (long)line;
1416251881Speter        }
1417251881Speter    }
1418251881Speter
1419251881Speter  svn_pool_destroy(subpool);
1420251881Speter
1421251881Speter  /* If we get here, then we failed to find a real error in the error
1422251881Speter     chain that the server proported to be sending us.  That's bad. */
1423251881Speter  if (! err)
1424251881Speter    err = svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1425251881Speter                           _("Malformed error list"));
1426251881Speter
1427251881Speter  return err;
1428251881Speter}
1429251881Speter
1430251881Spetersvn_error_t *
1431251881Spetersvn_ra_svn__read_cmd_response(svn_ra_svn_conn_t *conn,
1432251881Speter                              apr_pool_t *pool,
1433251881Speter                              const char *fmt, ...)
1434251881Speter{
1435251881Speter  va_list ap;
1436251881Speter  const char *status;
1437251881Speter  apr_array_header_t *params;
1438251881Speter  svn_error_t *err;
1439251881Speter
1440251881Speter  SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "wl", &status, &params));
1441251881Speter  if (strcmp(status, "success") == 0)
1442251881Speter    {
1443251881Speter      va_start(ap, fmt);
1444251881Speter      err = vparse_tuple(params, pool, &fmt, &ap);
1445251881Speter      va_end(ap);
1446251881Speter      return err;
1447251881Speter    }
1448251881Speter  else if (strcmp(status, "failure") == 0)
1449251881Speter    {
1450251881Speter      return svn_ra_svn__handle_failure_status(params, pool);
1451251881Speter    }
1452251881Speter
1453251881Speter  return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1454251881Speter                           _("Unknown status '%s' in command response"),
1455251881Speter                           status);
1456251881Speter}
1457251881Speter
1458251881Spetersvn_error_t *
1459251881Spetersvn_ra_svn__handle_commands2(svn_ra_svn_conn_t *conn,
1460251881Speter                             apr_pool_t *pool,
1461251881Speter                             const svn_ra_svn_cmd_entry_t *commands,
1462251881Speter                             void *baton,
1463251881Speter                             svn_boolean_t error_on_disconnect)
1464251881Speter{
1465251881Speter  apr_pool_t *subpool = svn_pool_create(pool);
1466251881Speter  apr_pool_t *iterpool = svn_pool_create(subpool);
1467251881Speter  const char *cmdname;
1468251881Speter  const svn_ra_svn_cmd_entry_t *command;
1469251881Speter  svn_error_t *err, *write_err;
1470251881Speter  apr_array_header_t *params;
1471251881Speter  apr_hash_t *cmd_hash = apr_hash_make(subpool);
1472251881Speter
1473251881Speter  for (command = commands; command->cmdname; command++)
1474251881Speter    svn_hash_sets(cmd_hash, command->cmdname, command);
1475251881Speter
1476251881Speter  while (1)
1477251881Speter    {
1478251881Speter      svn_pool_clear(iterpool);
1479251881Speter      err = svn_ra_svn__read_tuple(conn, iterpool, "wl", &cmdname, &params);
1480251881Speter      if (err)
1481251881Speter        {
1482251881Speter          if (!error_on_disconnect
1483251881Speter              && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED)
1484251881Speter            {
1485251881Speter              svn_error_clear(err);
1486251881Speter              svn_pool_destroy(subpool);
1487251881Speter              return SVN_NO_ERROR;
1488251881Speter            }
1489251881Speter          return err;
1490251881Speter        }
1491251881Speter      command = svn_hash_gets(cmd_hash, cmdname);
1492251881Speter
1493251881Speter      if (command)
1494251881Speter        err = (*command->handler)(conn, iterpool, params, baton);
1495251881Speter      else
1496251881Speter        {
1497251881Speter          err = svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL,
1498251881Speter                                  _("Unknown editor command '%s'"), cmdname);
1499251881Speter          err = svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, err, NULL);
1500251881Speter        }
1501251881Speter
1502251881Speter      if (err && err->apr_err == SVN_ERR_RA_SVN_CMD_ERR)
1503251881Speter        {
1504251881Speter          write_err = svn_ra_svn__write_cmd_failure(
1505251881Speter                          conn, iterpool,
1506251881Speter                          svn_ra_svn__locate_real_error_child(err));
1507251881Speter          svn_error_clear(err);
1508251881Speter          if (write_err)
1509251881Speter            return write_err;
1510251881Speter        }
1511251881Speter      else if (err)
1512251881Speter        return err;
1513251881Speter
1514251881Speter      if (command && command->terminate)
1515251881Speter        break;
1516251881Speter    }
1517251881Speter  svn_pool_destroy(iterpool);
1518251881Speter  svn_pool_destroy(subpool);
1519251881Speter  return SVN_NO_ERROR;
1520251881Speter}
1521251881Speter
1522251881Spetersvn_error_t *
1523251881Spetersvn_ra_svn__write_cmd_target_rev(svn_ra_svn_conn_t *conn,
1524251881Speter                                 apr_pool_t *pool,
1525251881Speter                                 svn_revnum_t rev)
1526251881Speter{
1527251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( target-rev ( ", 15));
1528251881Speter  SVN_ERR(write_tuple_revision(conn, pool, rev));
1529251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1530251881Speter
1531251881Speter  return SVN_NO_ERROR;
1532251881Speter}
1533251881Speter
1534251881Spetersvn_error_t *
1535251881Spetersvn_ra_svn__write_cmd_open_root(svn_ra_svn_conn_t *conn,
1536251881Speter                                apr_pool_t *pool,
1537251881Speter                                svn_revnum_t rev,
1538251881Speter                                const char *token)
1539251881Speter{
1540251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( open-root ( ", 14));
1541251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
1542251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
1543251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
1544251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, token));
1545251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1546251881Speter
1547251881Speter  return SVN_NO_ERROR;
1548251881Speter}
1549251881Speter
1550251881Spetersvn_error_t *
1551251881Spetersvn_ra_svn__write_cmd_delete_entry(svn_ra_svn_conn_t *conn,
1552251881Speter                                   apr_pool_t *pool,
1553251881Speter                                   const char *path,
1554251881Speter                                   svn_revnum_t rev,
1555251881Speter                                   const char *token)
1556251881Speter{
1557251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( delete-entry ( ", 17));
1558251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
1559251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
1560251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
1561251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
1562251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, token));
1563251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1564251881Speter
1565251881Speter  return SVN_NO_ERROR;
1566251881Speter}
1567251881Speter
1568251881Spetersvn_error_t *
1569251881Spetersvn_ra_svn__write_cmd_add_dir(svn_ra_svn_conn_t *conn,
1570251881Speter                              apr_pool_t *pool,
1571251881Speter                              const char *path,
1572251881Speter                              const char *parent_token,
1573251881Speter                              const char *token,
1574251881Speter                              const char *copy_path,
1575251881Speter                              svn_revnum_t copy_rev)
1576251881Speter{
1577251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( add-dir ( ", 12));
1578251881Speter  SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token,
1579251881Speter                              copy_path, copy_rev));
1580251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1581251881Speter
1582251881Speter  return SVN_NO_ERROR;
1583251881Speter}
1584251881Speter
1585251881Spetersvn_error_t *
1586251881Spetersvn_ra_svn__write_cmd_open_dir(svn_ra_svn_conn_t *conn,
1587251881Speter                               apr_pool_t *pool,
1588251881Speter                               const char *path,
1589251881Speter                               const char *parent_token,
1590251881Speter                               const char *token,
1591251881Speter                               svn_revnum_t rev)
1592251881Speter{
1593251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( open-dir ( ", 13));
1594251881Speter  SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev));
1595251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1596251881Speter
1597251881Speter  return SVN_NO_ERROR;
1598251881Speter}
1599251881Speter
1600251881Spetersvn_error_t *
1601251881Spetersvn_ra_svn__write_cmd_change_dir_prop(svn_ra_svn_conn_t *conn,
1602251881Speter                                      apr_pool_t *pool,
1603251881Speter                                      const char *token,
1604251881Speter                                      const char *name,
1605251881Speter                                      const svn_string_t *value)
1606251881Speter{
1607251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( change-dir-prop ( ", 20));
1608251881Speter  SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value));
1609251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1610251881Speter
1611251881Speter  return SVN_NO_ERROR;
1612251881Speter}
1613251881Speter
1614251881Spetersvn_error_t *
1615251881Spetersvn_ra_svn__write_cmd_close_dir(svn_ra_svn_conn_t *conn,
1616251881Speter                                apr_pool_t *pool,
1617251881Speter                                const char *token)
1618251881Speter{
1619251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( close-dir ( ", 14));
1620251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, token));
1621251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1622251881Speter
1623251881Speter  return SVN_NO_ERROR;
1624251881Speter}
1625251881Speter
1626251881Spetersvn_error_t *
1627251881Spetersvn_ra_svn__write_cmd_absent_dir(svn_ra_svn_conn_t *conn,
1628251881Speter                                 apr_pool_t *pool,
1629251881Speter                                 const char *path,
1630251881Speter                                 const char *parent_token)
1631251881Speter{
1632251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( absent-dir ( ", 15));
1633251881Speter  SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token));
1634251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1635251881Speter
1636251881Speter  return SVN_NO_ERROR;
1637251881Speter}
1638251881Speter
1639251881Spetersvn_error_t *
1640251881Spetersvn_ra_svn__write_cmd_add_file(svn_ra_svn_conn_t *conn,
1641251881Speter                               apr_pool_t *pool,
1642251881Speter                               const char *path,
1643251881Speter                               const char *parent_token,
1644251881Speter                               const char *token,
1645251881Speter                               const char *copy_path,
1646251881Speter                               svn_revnum_t copy_rev)
1647251881Speter{
1648251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( add-file ( ", 13));
1649251881Speter  SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token,
1650251881Speter                              copy_path, copy_rev));
1651251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1652251881Speter
1653251881Speter  return SVN_NO_ERROR;
1654251881Speter}
1655251881Speter
1656251881Spetersvn_error_t *
1657251881Spetersvn_ra_svn__write_cmd_open_file(svn_ra_svn_conn_t *conn,
1658251881Speter                                apr_pool_t *pool,
1659251881Speter                                const char *path,
1660251881Speter                                const char *parent_token,
1661251881Speter                                const char *token,
1662251881Speter                                svn_revnum_t rev)
1663251881Speter{
1664251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( open-file ( ", 14));
1665251881Speter  SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev));
1666251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1667251881Speter
1668251881Speter  return SVN_NO_ERROR;
1669251881Speter}
1670251881Speter
1671251881Spetersvn_error_t *
1672251881Spetersvn_ra_svn__write_cmd_change_file_prop(svn_ra_svn_conn_t *conn,
1673251881Speter                                       apr_pool_t *pool,
1674251881Speter                                       const char *token,
1675251881Speter                                       const char *name,
1676251881Speter                                       const svn_string_t *value)
1677251881Speter{
1678251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( change-file-prop ( ", 21));
1679251881Speter  SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value));
1680251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1681251881Speter
1682251881Speter  return SVN_NO_ERROR;
1683251881Speter}
1684251881Speter
1685251881Spetersvn_error_t *
1686251881Spetersvn_ra_svn__write_cmd_close_file(svn_ra_svn_conn_t *conn,
1687251881Speter                                 apr_pool_t *pool,
1688251881Speter                                 const char *token,
1689251881Speter                                 const char *text_checksum)
1690251881Speter{
1691251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( close-file ( ", 15));
1692251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, token));
1693251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
1694251881Speter  SVN_ERR(write_tuple_cstring_opt(conn, pool, text_checksum));
1695251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
1696251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1697251881Speter
1698251881Speter  return SVN_NO_ERROR;
1699251881Speter}
1700251881Speter
1701251881Spetersvn_error_t *
1702251881Spetersvn_ra_svn__write_cmd_absent_file(svn_ra_svn_conn_t *conn,
1703251881Speter                                  apr_pool_t *pool,
1704251881Speter                                  const char *path,
1705251881Speter                                  const char *parent_token)
1706251881Speter{
1707251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( absent-file ( ", 16));
1708251881Speter  SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token));
1709251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1710251881Speter
1711251881Speter  return SVN_NO_ERROR;
1712251881Speter}
1713251881Speter
1714251881Spetersvn_error_t *
1715251881Spetersvn_ra_svn__write_cmd_textdelta_chunk(svn_ra_svn_conn_t *conn,
1716251881Speter                                      apr_pool_t *pool,
1717251881Speter                                      const char *token,
1718251881Speter                                      const svn_string_t *chunk)
1719251881Speter{
1720251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( textdelta-chunk ( ", 20));
1721251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, token));
1722251881Speter  SVN_ERR(write_tuple_string(conn, pool, chunk));
1723251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1724251881Speter
1725251881Speter  return SVN_NO_ERROR;
1726251881Speter}
1727251881Speter
1728251881Spetersvn_error_t *
1729251881Spetersvn_ra_svn__write_cmd_textdelta_end(svn_ra_svn_conn_t *conn,
1730251881Speter                                    apr_pool_t *pool,
1731251881Speter                                    const char *token)
1732251881Speter{
1733251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( textdelta-end ( ", 18));
1734251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, token));
1735251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1736251881Speter
1737251881Speter  return SVN_NO_ERROR;
1738251881Speter}
1739251881Speter
1740251881Spetersvn_error_t *
1741251881Spetersvn_ra_svn__write_cmd_apply_textdelta(svn_ra_svn_conn_t *conn,
1742251881Speter                                      apr_pool_t *pool,
1743251881Speter                                      const char *token,
1744251881Speter                                      const char *base_checksum)
1745251881Speter{
1746251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( apply-textdelta ( ", 20));
1747251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, token));
1748251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
1749251881Speter  SVN_ERR(write_tuple_cstring_opt(conn, pool, base_checksum));
1750251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
1751251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1752251881Speter
1753251881Speter  return SVN_NO_ERROR;
1754251881Speter}
1755251881Speter
1756251881Spetersvn_error_t *
1757251881Spetersvn_ra_svn__write_cmd_close_edit(svn_ra_svn_conn_t *conn,
1758251881Speter                                 apr_pool_t *pool)
1759251881Speter{
1760251881Speter  return writebuf_write_short_string(conn, pool, "( close-edit ( ) ) ", 19);
1761251881Speter}
1762251881Speter
1763251881Spetersvn_error_t *
1764251881Spetersvn_ra_svn__write_cmd_abort_edit(svn_ra_svn_conn_t *conn,
1765251881Speter                                 apr_pool_t *pool)
1766251881Speter{
1767251881Speter  return writebuf_write_short_string(conn, pool, "( abort-edit ( ) ) ", 19);
1768251881Speter}
1769251881Speter
1770251881Spetersvn_error_t *
1771251881Spetersvn_ra_svn__write_cmd_set_path(svn_ra_svn_conn_t *conn,
1772251881Speter                               apr_pool_t *pool,
1773251881Speter                               const char *path,
1774251881Speter                               svn_revnum_t rev,
1775251881Speter                               svn_boolean_t start_empty,
1776251881Speter                               const char *lock_token,
1777251881Speter                               svn_depth_t depth)
1778251881Speter{
1779251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( set-path ( ", 13));
1780251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
1781251881Speter  SVN_ERR(write_tuple_revision(conn, pool, rev));
1782251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, start_empty));
1783251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
1784251881Speter  SVN_ERR(write_tuple_cstring_opt(conn, pool, lock_token));
1785251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
1786251881Speter  SVN_ERR(write_tuple_depth(conn, pool, depth));
1787251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1788251881Speter
1789251881Speter  return SVN_NO_ERROR;
1790251881Speter}
1791251881Speter
1792251881Spetersvn_error_t *
1793251881Spetersvn_ra_svn__write_cmd_delete_path(svn_ra_svn_conn_t *conn,
1794251881Speter                                  apr_pool_t *pool,
1795251881Speter                                  const char *path)
1796251881Speter{
1797251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( delete-path ( ", 16));
1798251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
1799251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1800251881Speter
1801251881Speter  return SVN_NO_ERROR;
1802251881Speter}
1803251881Speter
1804251881Spetersvn_error_t *
1805251881Spetersvn_ra_svn__write_cmd_link_path(svn_ra_svn_conn_t *conn,
1806251881Speter                                apr_pool_t *pool,
1807251881Speter                                const char *path,
1808251881Speter                                const char *url,
1809251881Speter                                svn_revnum_t rev,
1810251881Speter                                svn_boolean_t start_empty,
1811251881Speter                                const char *lock_token,
1812251881Speter                                svn_depth_t depth)
1813251881Speter{
1814251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( link-path ( ", 14));
1815251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
1816251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, url));
1817251881Speter  SVN_ERR(write_tuple_revision(conn, pool, rev));
1818251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, start_empty));
1819251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
1820251881Speter  SVN_ERR(write_tuple_cstring_opt(conn, pool,lock_token));
1821251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
1822251881Speter  SVN_ERR(write_tuple_depth(conn, pool, depth));
1823251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1824251881Speter
1825251881Speter  return SVN_NO_ERROR;
1826251881Speter}
1827251881Speter
1828251881Spetersvn_error_t *
1829251881Spetersvn_ra_svn__write_cmd_finish_report(svn_ra_svn_conn_t *conn,
1830251881Speter                                    apr_pool_t *pool)
1831251881Speter{
1832251881Speter  return writebuf_write_short_string(conn, pool, "( finish-report ( ) ) ", 22);
1833251881Speter}
1834251881Speter
1835251881Spetersvn_error_t *
1836251881Spetersvn_ra_svn__write_cmd_abort_report(svn_ra_svn_conn_t *conn,
1837251881Speter                                   apr_pool_t *pool)
1838251881Speter{
1839251881Speter  return writebuf_write_short_string(conn, pool, "( abort-report ( ) ) ", 21);
1840251881Speter}
1841251881Speter
1842251881Spetersvn_error_t *
1843251881Spetersvn_ra_svn__write_cmd_reparent(svn_ra_svn_conn_t *conn,
1844251881Speter                               apr_pool_t *pool,
1845251881Speter                               const char *url)
1846251881Speter{
1847251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( reparent ( ", 13));
1848251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, url));
1849251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1850251881Speter
1851251881Speter  return SVN_NO_ERROR;
1852251881Speter}
1853251881Speter
1854251881Spetersvn_error_t *
1855251881Spetersvn_ra_svn__write_cmd_get_latest_rev(svn_ra_svn_conn_t *conn,
1856251881Speter                                   apr_pool_t *pool)
1857251881Speter{
1858251881Speter  return writebuf_write_short_string(conn, pool, "( get-latest-rev ( ) ) ", 23);
1859251881Speter}
1860251881Speter
1861251881Spetersvn_error_t *
1862251881Spetersvn_ra_svn__write_cmd_get_dated_rev(svn_ra_svn_conn_t *conn,
1863251881Speter                                    apr_pool_t *pool,
1864251881Speter                                    apr_time_t tm)
1865251881Speter{
1866251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( get-dated-rev ( ", 18));
1867251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, svn_time_to_cstring(tm, pool)));
1868251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1869251881Speter
1870251881Speter  return SVN_NO_ERROR;
1871251881Speter}
1872251881Speter
1873251881Spetersvn_error_t *
1874251881Spetersvn_ra_svn__write_cmd_change_rev_prop2(svn_ra_svn_conn_t *conn,
1875251881Speter                                       apr_pool_t *pool,
1876251881Speter                                       svn_revnum_t rev,
1877251881Speter                                       const char *name,
1878251881Speter                                       const svn_string_t *value,
1879251881Speter                                       svn_boolean_t dont_care,
1880251881Speter                                       const svn_string_t *old_value)
1881251881Speter{
1882251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( change-rev-prop2 ( ", 21));
1883251881Speter  SVN_ERR(write_tuple_revision(conn, pool, rev));
1884251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, name));
1885251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
1886251881Speter  SVN_ERR(write_tuple_string_opt(conn, pool, value));
1887251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
1888251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
1889251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, dont_care));
1890251881Speter  SVN_ERR(write_tuple_string_opt(conn, pool, old_value));
1891251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
1892251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1893251881Speter
1894251881Speter  return SVN_NO_ERROR;
1895251881Speter}
1896251881Speter
1897251881Spetersvn_error_t *
1898251881Spetersvn_ra_svn__write_cmd_change_rev_prop(svn_ra_svn_conn_t *conn,
1899251881Speter                                      apr_pool_t *pool,
1900251881Speter                                      svn_revnum_t rev,
1901251881Speter                                      const char *name,
1902251881Speter                                      const svn_string_t *value)
1903251881Speter{
1904251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( change-rev-prop ( ", 20));
1905251881Speter  SVN_ERR(write_tuple_revision(conn, pool, rev));
1906251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, name));
1907251881Speter  SVN_ERR(write_tuple_string_opt(conn, pool, value));
1908251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1909251881Speter
1910251881Speter  return SVN_NO_ERROR;
1911251881Speter}
1912251881Speter
1913251881Spetersvn_error_t *
1914251881Spetersvn_ra_svn__write_cmd_rev_proplist(svn_ra_svn_conn_t *conn,
1915251881Speter                                   apr_pool_t *pool,
1916251881Speter                                   svn_revnum_t rev)
1917251881Speter{
1918251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( rev-proplist ( ", 17));
1919251881Speter  SVN_ERR(write_tuple_revision(conn, pool, rev));
1920251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1921251881Speter
1922251881Speter  return SVN_NO_ERROR;
1923251881Speter}
1924251881Speter
1925251881Spetersvn_error_t *
1926251881Spetersvn_ra_svn__write_cmd_rev_prop(svn_ra_svn_conn_t *conn,
1927251881Speter                               apr_pool_t *pool,
1928251881Speter                               svn_revnum_t rev,
1929251881Speter                               const char *name)
1930251881Speter{
1931251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( rev-prop ( ", 13));
1932251881Speter  SVN_ERR(write_tuple_revision(conn, pool, rev));
1933251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, name));
1934251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1935251881Speter
1936251881Speter  return SVN_NO_ERROR;
1937251881Speter}
1938251881Speter
1939251881Spetersvn_error_t *
1940251881Spetersvn_ra_svn__write_cmd_get_file(svn_ra_svn_conn_t *conn,
1941251881Speter                               apr_pool_t *pool,
1942251881Speter                               const char *path,
1943251881Speter                               svn_revnum_t rev,
1944251881Speter                               svn_boolean_t props,
1945251881Speter                               svn_boolean_t stream)
1946251881Speter{
1947251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( get-file ( ", 13));
1948251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
1949251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
1950251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
1951251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
1952251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, props));
1953251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, stream));
1954251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1955251881Speter
1956251881Speter  return SVN_NO_ERROR;
1957251881Speter}
1958251881Speter
1959251881Spetersvn_error_t *
1960251881Spetersvn_ra_svn__write_cmd_update(svn_ra_svn_conn_t *conn,
1961251881Speter                             apr_pool_t *pool,
1962251881Speter                             svn_revnum_t rev,
1963251881Speter                             const char *target,
1964251881Speter                             svn_boolean_t recurse,
1965251881Speter                             svn_depth_t depth,
1966251881Speter                             svn_boolean_t send_copyfrom_args,
1967251881Speter                             svn_boolean_t ignore_ancestry)
1968251881Speter{
1969251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( update ( ", 11));
1970251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
1971251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
1972251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
1973251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, target));
1974251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, recurse));
1975251881Speter  SVN_ERR(write_tuple_depth(conn, pool, depth));
1976251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args));
1977251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
1978251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1979251881Speter
1980251881Speter  return SVN_NO_ERROR;
1981251881Speter}
1982251881Speter
1983251881Spetersvn_error_t *
1984251881Spetersvn_ra_svn__write_cmd_switch(svn_ra_svn_conn_t *conn,
1985251881Speter                             apr_pool_t *pool,
1986251881Speter                             svn_revnum_t rev,
1987251881Speter                             const char *target,
1988251881Speter                             svn_boolean_t recurse,
1989251881Speter                             const char *switch_url,
1990251881Speter                             svn_depth_t depth,
1991251881Speter                             svn_boolean_t send_copyfrom_args,
1992251881Speter                             svn_boolean_t ignore_ancestry)
1993251881Speter{
1994251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( switch ( ", 11));
1995251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
1996251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
1997251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
1998251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, target));
1999251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2000251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, switch_url));
2001251881Speter  SVN_ERR(write_tuple_depth(conn, pool, depth));
2002251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args));
2003251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2004251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2005251881Speter
2006251881Speter  return SVN_NO_ERROR;
2007251881Speter}
2008251881Speter
2009251881Spetersvn_error_t *
2010251881Spetersvn_ra_svn__write_cmd_status(svn_ra_svn_conn_t *conn,
2011251881Speter                             apr_pool_t *pool,
2012251881Speter                             const char *target,
2013251881Speter                             svn_boolean_t recurse,
2014251881Speter                             svn_revnum_t rev,
2015251881Speter                             svn_depth_t depth)
2016251881Speter{
2017251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( status ( ", 11));
2018251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, target));
2019251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2020251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2021251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2022251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2023251881Speter  SVN_ERR(write_tuple_depth(conn, pool, depth));
2024251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2025251881Speter
2026251881Speter  return SVN_NO_ERROR;
2027251881Speter}
2028251881Speter
2029251881Spetersvn_error_t *
2030251881Spetersvn_ra_svn__write_cmd_diff(svn_ra_svn_conn_t *conn,
2031251881Speter                           apr_pool_t *pool,
2032251881Speter                           svn_revnum_t rev,
2033251881Speter                           const char *target,
2034251881Speter                           svn_boolean_t recurse,
2035251881Speter                           svn_boolean_t ignore_ancestry,
2036251881Speter                           const char *versus_url,
2037251881Speter                           svn_boolean_t text_deltas,
2038251881Speter                           svn_depth_t depth)
2039251881Speter{
2040251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( diff ( ", 9));
2041251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2042251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2043251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2044251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, target));
2045251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2046251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2047251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, versus_url));
2048251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, text_deltas));
2049251881Speter  SVN_ERR(write_tuple_depth(conn, pool, depth));
2050251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2051251881Speter
2052251881Speter  return SVN_NO_ERROR;
2053251881Speter}
2054251881Speter
2055251881Spetersvn_error_t *
2056251881Spetersvn_ra_svn__write_cmd_check_path(svn_ra_svn_conn_t *conn,
2057251881Speter                                 apr_pool_t *pool,
2058251881Speter                                 const char *path,
2059251881Speter                                 svn_revnum_t rev)
2060251881Speter{
2061251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( check-path ( ", 15));
2062251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
2063251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2064251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2065251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2066251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2067251881Speter
2068251881Speter  return SVN_NO_ERROR;
2069251881Speter}
2070251881Speter
2071251881Spetersvn_error_t *
2072251881Spetersvn_ra_svn__write_cmd_stat(svn_ra_svn_conn_t *conn,
2073251881Speter                           apr_pool_t *pool,
2074251881Speter                           const char *path,
2075251881Speter                           svn_revnum_t rev)
2076251881Speter{
2077251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( stat ( ", 9));
2078251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
2079251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2080251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2081251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2082251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2083251881Speter
2084251881Speter  return SVN_NO_ERROR;
2085251881Speter}
2086251881Speter
2087251881Spetersvn_error_t *
2088251881Spetersvn_ra_svn__write_cmd_get_file_revs(svn_ra_svn_conn_t *conn,
2089251881Speter                                    apr_pool_t *pool,
2090251881Speter                                    const char *path,
2091251881Speter                                    svn_revnum_t start,
2092251881Speter                                    svn_revnum_t end,
2093251881Speter                                    svn_boolean_t include_merged_revisions)
2094251881Speter{
2095251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( get-file-revs ( ", 18));
2096251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
2097251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2098251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, start));
2099251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2100251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2101251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, end));
2102251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2103251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, include_merged_revisions));
2104251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2105251881Speter
2106251881Speter  return SVN_NO_ERROR;
2107251881Speter}
2108251881Speter
2109251881Spetersvn_error_t *
2110251881Spetersvn_ra_svn__write_cmd_lock(svn_ra_svn_conn_t *conn,
2111251881Speter                           apr_pool_t *pool,
2112251881Speter                           const char *path,
2113251881Speter                           const char *comment,
2114251881Speter                           svn_boolean_t steal_lock,
2115251881Speter                           svn_revnum_t revnum)
2116251881Speter{
2117251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( lock ( ", 9));
2118251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
2119251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2120251881Speter  SVN_ERR(write_tuple_cstring_opt(conn, pool, comment));
2121251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2122251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, steal_lock));
2123251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2124251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, revnum));
2125251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2126251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2127251881Speter
2128251881Speter  return SVN_NO_ERROR;
2129251881Speter}
2130251881Speter
2131251881Spetersvn_error_t *
2132251881Spetersvn_ra_svn__write_cmd_unlock(svn_ra_svn_conn_t *conn,
2133251881Speter                             apr_pool_t *pool,
2134251881Speter                             const char *path,
2135251881Speter                             const char *token,
2136251881Speter                             svn_boolean_t break_lock)
2137251881Speter{
2138251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( unlock ( ", 11));
2139251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
2140251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2141251881Speter  SVN_ERR(write_tuple_cstring_opt(conn, pool, token));
2142251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2143251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, break_lock));
2144251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2145251881Speter
2146251881Speter  return SVN_NO_ERROR;
2147251881Speter}
2148251881Speter
2149251881Spetersvn_error_t *
2150251881Spetersvn_ra_svn__write_cmd_get_lock(svn_ra_svn_conn_t *conn,
2151251881Speter                               apr_pool_t *pool,
2152251881Speter                               const char *path)
2153251881Speter{
2154251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( get-lock ( ", 13));
2155251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
2156251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2157251881Speter
2158251881Speter  return SVN_NO_ERROR;
2159251881Speter}
2160251881Speter
2161251881Spetersvn_error_t *
2162251881Spetersvn_ra_svn__write_cmd_get_locks(svn_ra_svn_conn_t *conn,
2163251881Speter                                apr_pool_t *pool,
2164251881Speter                                const char *path,
2165251881Speter                                svn_depth_t depth)
2166251881Speter{
2167251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( get-locks ( ", 14));
2168251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
2169251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2170251881Speter  SVN_ERR(write_tuple_depth(conn, pool, depth));
2171251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2172251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2173251881Speter
2174251881Speter  return SVN_NO_ERROR;
2175251881Speter}
2176251881Speter
2177251881Spetersvn_error_t *
2178251881Spetersvn_ra_svn__write_cmd_replay(svn_ra_svn_conn_t *conn,
2179251881Speter                             apr_pool_t *pool,
2180251881Speter                             svn_revnum_t rev,
2181251881Speter                             svn_revnum_t low_water_mark,
2182251881Speter                             svn_boolean_t send_deltas)
2183251881Speter{
2184251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( replay ( ", 11));
2185251881Speter  SVN_ERR(write_tuple_revision(conn, pool, rev));
2186251881Speter  SVN_ERR(write_tuple_revision(conn, pool, low_water_mark));
2187251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, send_deltas));
2188251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2189251881Speter
2190251881Speter  return SVN_NO_ERROR;
2191251881Speter}
2192251881Speter
2193251881Spetersvn_error_t *
2194251881Spetersvn_ra_svn__write_cmd_replay_range(svn_ra_svn_conn_t *conn,
2195251881Speter                                   apr_pool_t *pool,
2196251881Speter                                   svn_revnum_t start_revision,
2197251881Speter                                   svn_revnum_t end_revision,
2198251881Speter                                   svn_revnum_t low_water_mark,
2199251881Speter                                   svn_boolean_t send_deltas)
2200251881Speter{
2201251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( replay-range ( ", 17));
2202251881Speter  SVN_ERR(write_tuple_revision(conn, pool, start_revision));
2203251881Speter  SVN_ERR(write_tuple_revision(conn, pool, end_revision));
2204251881Speter  SVN_ERR(write_tuple_revision(conn, pool, low_water_mark));
2205251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, send_deltas));
2206251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2207251881Speter
2208251881Speter  return SVN_NO_ERROR;
2209251881Speter}
2210251881Speter
2211251881Spetersvn_error_t *
2212251881Spetersvn_ra_svn__write_cmd_get_deleted_rev(svn_ra_svn_conn_t *conn,
2213251881Speter                                      apr_pool_t *pool,
2214251881Speter                                      const char *path,
2215251881Speter                                      svn_revnum_t peg_revision,
2216251881Speter                                      svn_revnum_t end_revision)
2217251881Speter{
2218251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( get-deleted-rev ( ", 20));
2219251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
2220251881Speter  SVN_ERR(write_tuple_revision(conn, pool, peg_revision));
2221251881Speter  SVN_ERR(write_tuple_revision(conn, pool, end_revision));
2222251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2223251881Speter
2224251881Speter  return SVN_NO_ERROR;
2225251881Speter}
2226251881Speter
2227251881Spetersvn_error_t *
2228251881Spetersvn_ra_svn__write_cmd_get_iprops(svn_ra_svn_conn_t *conn,
2229251881Speter                                 apr_pool_t *pool,
2230251881Speter                                 const char *path,
2231251881Speter                                 svn_revnum_t revision)
2232251881Speter{
2233251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( get-iprops ( ", 15));
2234251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
2235251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2236251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, revision));
2237251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2238251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2239251881Speter
2240251881Speter  return SVN_NO_ERROR;
2241251881Speter}
2242251881Speter
2243251881Spetersvn_error_t *
2244251881Spetersvn_ra_svn__write_cmd_finish_replay(svn_ra_svn_conn_t *conn,
2245251881Speter                                    apr_pool_t *pool)
2246251881Speter{
2247251881Speter  return writebuf_write_short_string(conn, pool, "( finish-replay ( ) ) ", 22);
2248251881Speter}
2249251881Speter
2250251881Spetersvn_error_t *svn_ra_svn__write_cmd_response(svn_ra_svn_conn_t *conn,
2251251881Speter                                            apr_pool_t *pool,
2252251881Speter                                            const char *fmt, ...)
2253251881Speter{
2254251881Speter  va_list ap;
2255251881Speter  svn_error_t *err;
2256251881Speter
2257251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( success ", 10));
2258251881Speter  va_start(ap, fmt);
2259251881Speter  err = vwrite_tuple(conn, pool, fmt, &ap);
2260251881Speter  va_end(ap);
2261251881Speter  return err ? svn_error_trace(err) : svn_ra_svn__end_list(conn, pool);
2262251881Speter}
2263251881Speter
2264251881Spetersvn_error_t *svn_ra_svn__write_cmd_failure(svn_ra_svn_conn_t *conn,
2265251881Speter                                           apr_pool_t *pool, svn_error_t *err)
2266251881Speter{
2267251881Speter  char buffer[128];
2268251881Speter  SVN_ERR(writebuf_write_short_string(conn, pool, "( failure ( ", 12));
2269251881Speter  for (; err; err = err->child)
2270251881Speter    {
2271251881Speter      const char *msg;
2272251881Speter
2273251881Speter#ifdef SVN_ERR__TRACING
2274251881Speter      if (svn_error__is_tracing_link(err))
2275251881Speter        msg = err->message;
2276251881Speter      else
2277251881Speter#endif
2278251881Speter        msg = svn_err_best_message(err, buffer, sizeof(buffer));
2279251881Speter
2280251881Speter      /* The message string should have been optional, but we can't
2281251881Speter         easily change that, so marshal nonexistent messages as "". */
2282251881Speter      SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "nccn",
2283251881Speter                                      (apr_uint64_t) err->apr_err,
2284251881Speter                                      msg ? msg : "",
2285251881Speter                                      err->file ? err->file : "",
2286251881Speter                                      (apr_uint64_t) err->line));
2287251881Speter    }
2288251881Speter  return writebuf_write_short_string(conn, pool, ") ) ", 4);
2289251881Speter}
2290