marshal.c revision 299742
1219019Sgabor/*
2219019Sgabor * marshal.c :  Marshalling routines for Subversion protocol
3219019Sgabor *
4219019Sgabor * ====================================================================
5219019Sgabor *    Licensed to the Apache Software Foundation (ASF) under one
6219019Sgabor *    or more contributor license agreements.  See the NOTICE file
7219019Sgabor *    distributed with this work for additional information
8219019Sgabor *    regarding copyright ownership.  The ASF licenses this file
9219019Sgabor *    to you under the Apache License, Version 2.0 (the
10219019Sgabor *    "License"); you may not use this file except in compliance
11219019Sgabor *    with the License.  You may obtain a copy of the License at
12219019Sgabor *
13219019Sgabor *      http://www.apache.org/licenses/LICENSE-2.0
14219019Sgabor *
15219019Sgabor *    Unless required by applicable law or agreed to in writing,
16219019Sgabor *    software distributed under the License is distributed on an
17219019Sgabor *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18219019Sgabor *    KIND, either express or implied.  See the License for the
19219019Sgabor *    specific language governing permissions and limitations
20219019Sgabor *    under the License.
21219019Sgabor * ====================================================================
22219019Sgabor */
23219019Sgabor
24219019Sgabor
25219019Sgabor
26219019Sgabor#include <assert.h>
27219019Sgabor#include <stdlib.h>
28219019Sgabor
29219019Sgabor#define APR_WANT_STRFUNC
30219019Sgabor#include <apr_want.h>
31219019Sgabor#include <apr_general.h>
32219019Sgabor#include <apr_lib.h>
33219019Sgabor#include <apr_strings.h>
34219019Sgabor
35219019Sgabor#include "svn_hash.h"
36219019Sgabor#include "svn_types.h"
37219019Sgabor#include "svn_string.h"
38219019Sgabor#include "svn_error.h"
39219019Sgabor#include "svn_pools.h"
40219019Sgabor#include "svn_ra_svn.h"
41219019Sgabor#include "svn_private_config.h"
42219019Sgabor#include "svn_ctype.h"
43219019Sgabor#include "svn_sorts.h"
44219019Sgabor#include "svn_time.h"
45219019Sgabor
46219019Sgabor#include "ra_svn.h"
47219019Sgabor
48219019Sgabor#include "private/svn_string_private.h"
49219019Sgabor#include "private/svn_dep_compat.h"
50219019Sgabor#include "private/svn_error_private.h"
51219019Sgabor#include "private/svn_subr_private.h"
52219019Sgabor
53219019Sgabor#define svn_iswhitespace(c) ((c) == ' ' || (c) == '\n')
54219019Sgabor
55219019Sgabor/* If we receive data that *claims* to be followed by a very long string,
56219019Sgabor * we should not trust that claim right away. But everything up to 1 MB
57219019Sgabor * should be too small to be instrumental for a DOS attack. */
58219019Sgabor
59219019Sgabor#define SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD (0x100000)
60219019Sgabor
61219019Sgabor/* We don't use "words" longer than this in our protocol.  The longest word
62219019Sgabor * we are currently using is only about 16 chars long but we leave room for
63219019Sgabor * longer future capability and command names.
64219019Sgabor */
65219019Sgabor#define MAX_WORD_LENGTH 31
66219019Sgabor
67219019Sgabor/* The generic parsers will use the following value to limit the recursion
68219019Sgabor * depth to some reasonable value.  The current protocol implementation
69219019Sgabor * actually uses only maximum item nesting level of around 5.  So, there is
70219019Sgabor * plenty of headroom here.
71219019Sgabor */
72219019Sgabor#define ITEM_NESTING_LIMIT 64
73219019Sgabor
74219019Sgabor/* Return the APR socket timeout to be used for the connection depending
75219019Sgabor * on whether there is a blockage handler or zero copy has been activated. */
76219019Sgaborstatic apr_interval_time_t
77219019Sgaborget_timeout(svn_ra_svn_conn_t *conn)
78219019Sgabor{
79219019Sgabor  return conn->block_handler ? 0 : -1;
80219019Sgabor}
81219019Sgabor
82219019Sgabor/* --- CONNECTION INITIALIZATION --- */
83219019Sgabor
84219019Sgaborsvn_ra_svn_conn_t *svn_ra_svn_create_conn4(apr_socket_t *sock,
85219019Sgabor                                           svn_stream_t *in_stream,
86219019Sgabor                                           svn_stream_t *out_stream,
87219019Sgabor                                           int compression_level,
88219019Sgabor                                           apr_size_t zero_copy_limit,
89219019Sgabor                                           apr_size_t error_check_interval,
90219019Sgabor                                           apr_pool_t *result_pool)
91219019Sgabor{
92219019Sgabor  svn_ra_svn_conn_t *conn;
93219019Sgabor  void *mem = apr_palloc(result_pool, sizeof(*conn) + SVN_RA_SVN__PAGE_SIZE);
94219019Sgabor  conn = (void*)APR_ALIGN((apr_uintptr_t)mem, SVN_RA_SVN__PAGE_SIZE);
95219019Sgabor
96219019Sgabor  assert((sock && !in_stream && !out_stream)
97219019Sgabor         || (!sock && in_stream && out_stream));
98219019Sgabor#ifdef SVN_HAVE_SASL
99219019Sgabor  conn->sock = sock;
100219019Sgabor  conn->encrypted = FALSE;
101219019Sgabor#endif
102219019Sgabor  conn->session = NULL;
103219019Sgabor  conn->read_ptr = conn->read_buf;
104219019Sgabor  conn->read_end = conn->read_buf;
105219019Sgabor  conn->write_pos = 0;
106219019Sgabor  conn->written_since_error_check = 0;
107219019Sgabor  conn->error_check_interval = error_check_interval;
108219019Sgabor  conn->may_check_for_error = error_check_interval == 0;
109219019Sgabor  conn->block_handler = NULL;
110219019Sgabor  conn->block_baton = NULL;
111219019Sgabor  conn->capabilities = apr_hash_make(result_pool);
112219019Sgabor  conn->compression_level = compression_level;
113219019Sgabor  conn->zero_copy_limit = zero_copy_limit;
114219019Sgabor  conn->pool = result_pool;
115219019Sgabor
116219019Sgabor  if (sock != NULL)
117219019Sgabor    {
118219019Sgabor      apr_sockaddr_t *sa;
119219019Sgabor      conn->stream = svn_ra_svn__stream_from_sock(sock, result_pool);
120219019Sgabor      if (!(apr_socket_addr_get(&sa, APR_REMOTE, sock) == APR_SUCCESS
121219019Sgabor            && apr_sockaddr_ip_get(&conn->remote_ip, sa) == APR_SUCCESS))
122219019Sgabor        conn->remote_ip = NULL;
123219019Sgabor      svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn));
124219019Sgabor    }
125219019Sgabor  else
126219019Sgabor    {
127219019Sgabor      conn->stream = svn_ra_svn__stream_from_streams(in_stream, out_stream,
128219019Sgabor                                                     result_pool);
129219019Sgabor      conn->remote_ip = NULL;
130219019Sgabor    }
131219019Sgabor
132219019Sgabor  return conn;
133219019Sgabor}
134219019Sgabor
135219019Sgaborsvn_error_t *svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn,
136219019Sgabor                                         const apr_array_header_t *list)
137219019Sgabor{
138219019Sgabor  int i;
139219019Sgabor  svn_ra_svn_item_t *item;
140219019Sgabor  const char *word;
141219019Sgabor
142219019Sgabor  for (i = 0; i < list->nelts; i++)
143219019Sgabor    {
144219019Sgabor      item = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
145219019Sgabor      if (item->kind != SVN_RA_SVN_WORD)
146219019Sgabor        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
147219019Sgabor                                _("Capability entry is not a word"));
148219019Sgabor      word = apr_pstrdup(conn->pool, item->u.word);
149219019Sgabor      svn_hash_sets(conn->capabilities, word, word);
150219019Sgabor    }
151219019Sgabor  return SVN_NO_ERROR;
152219019Sgabor}
153219019Sgabor
154219019Sgaborapr_pool_t *
155219019Sgaborsvn_ra_svn__get_pool(svn_ra_svn_conn_t *conn)
156219019Sgabor{
157219019Sgabor  return conn->pool;
158219019Sgabor}
159260003Sdim
160219019Sgaborsvn_error_t *
161219019Sgaborsvn_ra_svn__set_shim_callbacks(svn_ra_svn_conn_t *conn,
162219019Sgabor                               svn_delta_shim_callbacks_t *shim_callbacks)
163219019Sgabor{
164219019Sgabor  conn->shim_callbacks = shim_callbacks;
165219019Sgabor  return SVN_NO_ERROR;
166219019Sgabor}
167219019Sgabor
168219019Sgaborsvn_boolean_t svn_ra_svn_has_capability(svn_ra_svn_conn_t *conn,
169219019Sgabor                                        const char *capability)
170219019Sgabor{
171219019Sgabor  return (svn_hash_gets(conn->capabilities, capability) != NULL);
172219019Sgabor}
173219019Sgabor
174219019Sgaborint
175219019Sgaborsvn_ra_svn_compression_level(svn_ra_svn_conn_t *conn)
176219019Sgabor{
177260003Sdim  return conn->compression_level;
178219019Sgabor}
179219019Sgabor
180281550Stijlapr_size_t
181219019Sgaborsvn_ra_svn_zero_copy_limit(svn_ra_svn_conn_t *conn)
182219019Sgabor{
183281550Stijl  return conn->zero_copy_limit;
184219019Sgabor}
185219019Sgabor
186219019Sgaborconst char *svn_ra_svn_conn_remote_host(svn_ra_svn_conn_t *conn)
187219019Sgabor{
188219019Sgabor  return conn->remote_ip;
189219019Sgabor}
190219019Sgabor
191219019Sgaborvoid
192219019Sgaborsvn_ra_svn__set_block_handler(svn_ra_svn_conn_t *conn,
193219019Sgabor                              ra_svn_block_handler_t handler,
194219019Sgabor                              void *baton)
195219019Sgabor{
196219019Sgabor  conn->block_handler = handler;
197219019Sgabor  conn->block_baton = baton;
198219019Sgabor  svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn));
199219019Sgabor}
200219019Sgabor
201219019Sgaborsvn_error_t *svn_ra_svn__data_available(svn_ra_svn_conn_t *conn,
202219019Sgabor                                       svn_boolean_t *data_available)
203219019Sgabor{
204219019Sgabor  return svn_ra_svn__stream_data_available(conn->stream, data_available);
205219019Sgabor}
206219019Sgabor
207219019Sgabor/* --- WRITE BUFFER MANAGEMENT --- */
208219019Sgabor
209219019Sgabor/* Write data to socket or output file as appropriate. */
210219019Sgaborstatic svn_error_t *writebuf_output(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
211219019Sgabor                                    const char *data, apr_size_t len)
212219019Sgabor{
213219019Sgabor  const char *end = data + len;
214219019Sgabor  apr_size_t count;
215219019Sgabor  apr_pool_t *subpool = NULL;
216219019Sgabor  svn_ra_svn__session_baton_t *session = conn->session;
217219019Sgabor
218219019Sgabor  while (data < end)
219219019Sgabor    {
220219019Sgabor      count = end - data;
221219019Sgabor
222219019Sgabor      if (session && session->callbacks && session->callbacks->cancel_func)
223219019Sgabor        SVN_ERR((session->callbacks->cancel_func)(session->callbacks_baton));
224219019Sgabor
225219019Sgabor      SVN_ERR(svn_ra_svn__stream_write(conn->stream, data, &count));
226219019Sgabor      if (count == 0)
227219019Sgabor        {
228219019Sgabor          if (!subpool)
229219019Sgabor            subpool = svn_pool_create(pool);
230219019Sgabor          else
231219019Sgabor            svn_pool_clear(subpool);
232219019Sgabor          SVN_ERR(conn->block_handler(conn, subpool, conn->block_baton));
233219019Sgabor        }
234219019Sgabor      data += count;
235219019Sgabor
236219019Sgabor      if (session)
237219019Sgabor        {
238219019Sgabor          const svn_ra_callbacks2_t *cb = session->callbacks;
239219019Sgabor          session->bytes_written += count;
240219019Sgabor
241219019Sgabor          if (cb && cb->progress_func)
242219019Sgabor            (cb->progress_func)(session->bytes_written + session->bytes_read,
243219019Sgabor                                -1, cb->progress_baton, subpool);
244219019Sgabor        }
245219019Sgabor    }
246219019Sgabor
247219019Sgabor  conn->written_since_error_check += len;
248219019Sgabor  conn->may_check_for_error
249219019Sgabor    = conn->written_since_error_check >= conn->error_check_interval;
250219019Sgabor
251219019Sgabor  if (subpool)
252219019Sgabor    svn_pool_destroy(subpool);
253219019Sgabor  return SVN_NO_ERROR;
254219019Sgabor}
255219019Sgabor
256219019Sgabor/* Write data from the write buffer out to the socket. */
257219019Sgaborstatic svn_error_t *writebuf_flush(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
258219019Sgabor{
259219019Sgabor  apr_size_t write_pos = conn->write_pos;
260219019Sgabor
261219019Sgabor  /* Clear conn->write_pos first in case the block handler does a read. */
262219019Sgabor  conn->write_pos = 0;
263219019Sgabor  SVN_ERR(writebuf_output(conn, pool, conn->write_buf, write_pos));
264219019Sgabor  return SVN_NO_ERROR;
265219019Sgabor}
266219019Sgabor
267219019Sgaborstatic svn_error_t *writebuf_write(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
268219019Sgabor                                   const char *data, apr_size_t len)
269219019Sgabor{
270219019Sgabor  /* data >= 8k is sent immediately */
271219019Sgabor  if (len >= sizeof(conn->write_buf) / 2)
272219019Sgabor    {
273219019Sgabor      if (conn->write_pos > 0)
274219019Sgabor        SVN_ERR(writebuf_flush(conn, pool));
275219019Sgabor
276219019Sgabor      return writebuf_output(conn, pool, data, len);
277219019Sgabor    }
278219019Sgabor
279219019Sgabor  /* ensure room for the data to add */
280219019Sgabor  if (conn->write_pos + len > sizeof(conn->write_buf))
281219019Sgabor    SVN_ERR(writebuf_flush(conn, pool));
282219019Sgabor
283219019Sgabor  /* buffer the new data block as well */
284219019Sgabor  memcpy(conn->write_buf + conn->write_pos, data, len);
285219019Sgabor  conn->write_pos += len;
286219019Sgabor
287219019Sgabor  return SVN_NO_ERROR;
288219019Sgabor}
289219019Sgabor
290219019Sgabor/* Write STRING_LITERAL, which is a string literal argument.
291219019Sgabor
292219019Sgabor   Note: The purpose of the empty string "" in the macro definition is to
293219019Sgabor   assert that STRING_LITERAL is in fact a string literal. Otherwise, the
294219019Sgabor   string concatenation attempt should produce a compile-time error. */
295219019Sgabor#define writebuf_write_literal(conn, pool, string_literal) \
296219019Sgabor    writebuf_write(conn, pool, string_literal, sizeof(string_literal "") - 1)
297219019Sgabor
298219019Sgaborstatic APR_INLINE svn_error_t *
299219019Sgaborwritebuf_writechar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char data)
300219019Sgabor{
301219019Sgabor  if (conn->write_pos < sizeof(conn->write_buf))
302219019Sgabor  {
303219019Sgabor    conn->write_buf[conn->write_pos] = data;
304219019Sgabor    conn->write_pos++;
305219019Sgabor
306219019Sgabor    return SVN_NO_ERROR;
307219019Sgabor  }
308219019Sgabor  else
309219019Sgabor  {
310219019Sgabor    char temp = data;
311219019Sgabor    return writebuf_write(conn, pool, &temp, 1);
312219019Sgabor  }
313219019Sgabor}
314219019Sgabor
315219019Sgabor/* --- READ BUFFER MANAGEMENT --- */
316219019Sgabor
317219019Sgabor/* Read bytes into DATA until either the read buffer is empty or
318219019Sgabor * we reach END. */
319219019Sgaborstatic char *readbuf_drain(svn_ra_svn_conn_t *conn, char *data, char *end)
320219019Sgabor{
321219019Sgabor  apr_ssize_t buflen, copylen;
322219019Sgabor
323219019Sgabor  buflen = conn->read_end - conn->read_ptr;
324219019Sgabor  copylen = (buflen < end - data) ? buflen : end - data;
325219019Sgabor  memcpy(data, conn->read_ptr, copylen);
326219019Sgabor  conn->read_ptr += copylen;
327219019Sgabor  return data + copylen;
328219019Sgabor}
329219019Sgabor
330219019Sgabor/* Read data from socket or input file as appropriate. */
331219019Sgaborstatic svn_error_t *readbuf_input(svn_ra_svn_conn_t *conn, char *data,
332219019Sgabor                                  apr_size_t *len, apr_pool_t *pool)
333219019Sgabor{
334219019Sgabor  svn_ra_svn__session_baton_t *session = conn->session;
335219019Sgabor
336219019Sgabor  if (session && session->callbacks && session->callbacks->cancel_func)
337219019Sgabor    SVN_ERR((session->callbacks->cancel_func)(session->callbacks_baton));
338219019Sgabor
339219019Sgabor  SVN_ERR(svn_ra_svn__stream_read(conn->stream, data, len));
340219019Sgabor  if (*len == 0)
341219019Sgabor    return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL);
342219019Sgabor
343219019Sgabor  if (session)
344219019Sgabor    {
345219019Sgabor      const svn_ra_callbacks2_t *cb = session->callbacks;
346219019Sgabor      session->bytes_read += *len;
347219019Sgabor
348219019Sgabor      if (cb && cb->progress_func)
349219019Sgabor        (cb->progress_func)(session->bytes_read + session->bytes_written,
350219019Sgabor                            -1, cb->progress_baton, pool);
351219019Sgabor    }
352
353  return SVN_NO_ERROR;
354}
355
356/* Treat the next LEN input bytes from CONN as "read" */
357static svn_error_t *readbuf_skip(svn_ra_svn_conn_t *conn, apr_uint64_t len)
358{
359  do
360  {
361    apr_size_t buflen = conn->read_end - conn->read_ptr;
362    apr_size_t copylen = (buflen < len) ? buflen : (apr_size_t)len;
363    conn->read_ptr += copylen;
364    len -= copylen;
365    if (len == 0)
366      break;
367
368    buflen = sizeof(conn->read_buf);
369    SVN_ERR(svn_ra_svn__stream_read(conn->stream, conn->read_buf, &buflen));
370    if (buflen == 0)
371      return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL);
372
373    conn->read_end = conn->read_buf + buflen;
374    conn->read_ptr = conn->read_buf;
375  }
376  while (len > 0);
377
378  return SVN_NO_ERROR;
379}
380
381/* Read data from the socket into the read buffer, which must be empty. */
382static svn_error_t *readbuf_fill(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
383{
384  apr_size_t len;
385
386  SVN_ERR_ASSERT(conn->read_ptr == conn->read_end);
387  if (conn->write_pos)
388    SVN_ERR(writebuf_flush(conn, pool));
389
390  len = sizeof(conn->read_buf);
391  SVN_ERR(readbuf_input(conn, conn->read_buf, &len, pool));
392  conn->read_ptr = conn->read_buf;
393  conn->read_end = conn->read_buf + len;
394  return SVN_NO_ERROR;
395}
396
397/* This is a hot function calling a cold function.  GCC and others tend to
398 * inline the cold sub-function instead of this hot one.  Therefore, be
399 * very insistent on lining this one.  It is not a correctness issue, though.
400 */
401static SVN__FORCE_INLINE svn_error_t *
402readbuf_getchar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char *result)
403{
404  if (conn->read_ptr == conn->read_end)
405    SVN_ERR(readbuf_fill(conn, pool));
406  *result = *conn->read_ptr++;
407  return SVN_NO_ERROR;
408}
409
410static svn_error_t *readbuf_getchar_skip_whitespace(svn_ra_svn_conn_t *conn,
411                                                    apr_pool_t *pool,
412                                                    char *result)
413{
414  do
415    SVN_ERR(readbuf_getchar(conn, pool, result));
416  while (svn_iswhitespace(*result));
417  return SVN_NO_ERROR;
418}
419
420/* Read the next LEN bytes from CONN and copy them to *DATA. */
421static svn_error_t *readbuf_read(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
422                                 char *data, apr_size_t len)
423{
424  char *end = data + len;
425  apr_size_t count;
426
427  /* Copy in an appropriate amount of data from the buffer. */
428  data = readbuf_drain(conn, data, end);
429
430  /* Read large chunks directly into buffer. */
431  while (end - data > (apr_ssize_t)sizeof(conn->read_buf))
432    {
433      SVN_ERR(writebuf_flush(conn, pool));
434      count = end - data;
435      SVN_ERR(readbuf_input(conn, data, &count, pool));
436      data += count;
437    }
438
439  while (end > data)
440    {
441      /* The remaining amount to read is small; fill the buffer and
442       * copy from that. */
443      SVN_ERR(readbuf_fill(conn, pool));
444      data = readbuf_drain(conn, data, end);
445    }
446
447  return SVN_NO_ERROR;
448}
449
450static svn_error_t *readbuf_skip_leading_garbage(svn_ra_svn_conn_t *conn,
451                                                 apr_pool_t *pool)
452{
453  char buf[256];  /* Must be smaller than sizeof(conn->read_buf) - 1. */
454  const char *p, *end;
455  apr_size_t len;
456  svn_boolean_t lparen = FALSE;
457
458  SVN_ERR_ASSERT(conn->read_ptr == conn->read_end);
459  while (1)
460    {
461      /* Read some data directly from the connection input source. */
462      len = sizeof(buf);
463      SVN_ERR(readbuf_input(conn, buf, &len, pool));
464      end = buf + len;
465
466      /* Scan the data for '(' WS with a very simple state machine. */
467      for (p = buf; p < end; p++)
468        {
469          if (lparen && svn_iswhitespace(*p))
470            break;
471          else
472            lparen = (*p == '(');
473        }
474      if (p < end)
475        break;
476    }
477
478  /* p now points to the whitespace just after the left paren.  Fake
479   * up the left paren and then copy what we have into the read
480   * buffer. */
481  conn->read_buf[0] = '(';
482  memcpy(conn->read_buf + 1, p, end - p);
483  conn->read_ptr = conn->read_buf;
484  conn->read_end = conn->read_buf + 1 + (end - p);
485  return SVN_NO_ERROR;
486}
487
488/* --- WRITING DATA ITEMS --- */
489
490static svn_error_t *write_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
491                                 apr_uint64_t number, char follow)
492{
493  apr_size_t written;
494
495  /* SVN_INT64_BUFFER_SIZE includes space for a terminating NUL that
496   * svn__ui64toa will always append. */
497  if (conn->write_pos + SVN_INT64_BUFFER_SIZE >= sizeof(conn->write_buf))
498    SVN_ERR(writebuf_flush(conn, pool));
499
500  written = svn__ui64toa(conn->write_buf + conn->write_pos, number);
501  conn->write_buf[conn->write_pos + written] = follow;
502  conn->write_pos += written + 1;
503
504  return SVN_NO_ERROR;
505}
506
507svn_error_t *
508svn_ra_svn__write_number(svn_ra_svn_conn_t *conn,
509                         apr_pool_t *pool,
510                         apr_uint64_t number)
511{
512  return write_number(conn, pool, number, ' ');
513}
514
515static svn_error_t *
516svn_ra_svn__write_ncstring(svn_ra_svn_conn_t *conn,
517                           apr_pool_t *pool,
518                           const char *s,
519                           apr_size_t len)
520{
521  if (len < 10)
522    {
523      SVN_ERR(writebuf_writechar(conn, pool, (char)(len + '0')));
524      SVN_ERR(writebuf_writechar(conn, pool, ':'));
525    }
526  else
527    SVN_ERR(write_number(conn, pool, len, ':'));
528
529  SVN_ERR(writebuf_write(conn, pool, s, len));
530  SVN_ERR(writebuf_writechar(conn, pool, ' '));
531
532  return SVN_NO_ERROR;
533}
534
535svn_error_t *
536svn_ra_svn__write_string(svn_ra_svn_conn_t *conn,
537                         apr_pool_t *pool,
538                         const svn_string_t *str)
539{
540  SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, str->data, str->len));
541  return SVN_NO_ERROR;
542}
543
544svn_error_t *
545svn_ra_svn__write_cstring(svn_ra_svn_conn_t *conn,
546                          apr_pool_t *pool,
547                          const char *s)
548{
549  SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, s, strlen(s)));
550  return SVN_NO_ERROR;
551}
552
553svn_error_t *
554svn_ra_svn__write_word(svn_ra_svn_conn_t *conn,
555                       apr_pool_t *pool,
556                       const char *word)
557{
558  SVN_ERR(writebuf_write(conn, pool, word, strlen(word)));
559  SVN_ERR(writebuf_writechar(conn, pool, ' '));
560
561  return SVN_NO_ERROR;
562}
563
564svn_error_t *
565svn_ra_svn__write_boolean(svn_ra_svn_conn_t *conn,
566                          apr_pool_t *pool,
567                          svn_boolean_t value)
568{
569  if (value)
570    SVN_ERR(writebuf_write_literal(conn, pool, "true "));
571  else
572    SVN_ERR(writebuf_write_literal(conn, pool, "false "));
573
574  return SVN_NO_ERROR;
575}
576
577svn_error_t *
578svn_ra_svn__write_proplist(svn_ra_svn_conn_t *conn,
579                           apr_pool_t *pool,
580                           apr_hash_t *props)
581{
582  apr_hash_index_t *hi;
583  const char *propname;
584  svn_string_t *propval;
585  apr_size_t len;
586
587  /* One might use an iterpool here but that would only be used when the
588     send buffer gets flushed and only by the CONN's progress callback.
589     That should happen at most once for typical prop lists and even then
590     use only a few bytes at best.
591   */
592  if (props)
593    for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
594      {
595        apr_hash_this(hi, (const void **)&propname,
596                          (apr_ssize_t *)&len,
597                          (void **)&propval);
598
599        SVN_ERR(svn_ra_svn__start_list(conn, pool));
600        SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, propname, len));
601        SVN_ERR(svn_ra_svn__write_string(conn, pool, propval));
602        SVN_ERR(svn_ra_svn__end_list(conn, pool));
603      }
604
605  return SVN_NO_ERROR;
606}
607
608svn_error_t *
609svn_ra_svn__start_list(svn_ra_svn_conn_t *conn,
610                       apr_pool_t *pool)
611{
612  if (conn->write_pos + 2 <= sizeof(conn->write_buf))
613    {
614      conn->write_buf[conn->write_pos] = '(';
615      conn->write_buf[conn->write_pos+1] = ' ';
616      conn->write_pos += 2;
617      return SVN_NO_ERROR;
618    }
619
620  return writebuf_write(conn, pool, "( ", 2);
621}
622
623svn_error_t *
624svn_ra_svn__end_list(svn_ra_svn_conn_t *conn,
625                     apr_pool_t *pool)
626{
627  if (conn->write_pos + 2 <= sizeof(conn->write_buf))
628  {
629    conn->write_buf[conn->write_pos] = ')';
630    conn->write_buf[conn->write_pos+1] = ' ';
631    conn->write_pos += 2;
632    return SVN_NO_ERROR;
633  }
634
635  return writebuf_write(conn, pool, ") ", 2);
636}
637
638svn_error_t *
639svn_ra_svn__flush(svn_ra_svn_conn_t *conn,
640                  apr_pool_t *pool)
641{
642  SVN_ERR(writebuf_flush(conn, pool));
643  conn->may_check_for_error = TRUE;
644
645  return SVN_NO_ERROR;
646}
647
648/* --- WRITING TUPLES --- */
649
650static svn_error_t *
651vwrite_tuple_cstring(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
652{
653  const char *cstr = va_arg(*ap, const char *);
654  SVN_ERR_ASSERT(cstr);
655  return svn_ra_svn__write_cstring(conn, pool, cstr);
656}
657
658static svn_error_t *
659vwrite_tuple_cstring_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
660{
661  const char *cstr = va_arg(*ap, const char *);
662  return cstr ? svn_ra_svn__write_cstring(conn, pool, cstr) : SVN_NO_ERROR;
663}
664
665static svn_error_t *
666vwrite_tuple_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
667{
668  const svn_string_t *str = va_arg(*ap, const svn_string_t *);
669  SVN_ERR_ASSERT(str);
670  return svn_ra_svn__write_string(conn, pool, str);
671}
672
673static svn_error_t *
674vwrite_tuple_string_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
675{
676  const svn_string_t *str = va_arg(*ap, const svn_string_t *);
677  return str ? svn_ra_svn__write_string(conn, pool, str) : SVN_NO_ERROR;
678}
679
680static svn_error_t *
681vwrite_tuple_word(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
682{
683  const char *cstr = va_arg(*ap, const char *);
684  SVN_ERR_ASSERT(cstr);
685  return svn_ra_svn__write_word(conn, pool, cstr);
686}
687
688static svn_error_t *
689vwrite_tuple_word_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
690{
691  const char *cstr = va_arg(*ap, const char *);
692  return cstr ? svn_ra_svn__write_word(conn, pool, cstr) : SVN_NO_ERROR;
693}
694
695static svn_error_t *
696vwrite_tuple_revision(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
697{
698  svn_revnum_t rev = va_arg(*ap, svn_revnum_t);
699  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
700  return svn_ra_svn__write_number(conn, pool, rev);
701}
702
703static svn_error_t *
704vwrite_tuple_revision_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
705{
706  svn_revnum_t rev = va_arg(*ap, svn_revnum_t);
707  return SVN_IS_VALID_REVNUM(rev)
708       ? svn_ra_svn__write_number(conn, pool, rev)
709       : SVN_NO_ERROR;
710}
711
712static svn_error_t *
713vwrite_tuple_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
714{
715  return svn_ra_svn__write_number(conn, pool, va_arg(*ap, apr_uint64_t));
716}
717
718static svn_error_t *
719vwrite_tuple_boolean(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
720{
721  return svn_ra_svn__write_boolean(conn, pool, va_arg(*ap, svn_boolean_t));
722}
723
724static svn_error_t *
725write_tuple_cstring(svn_ra_svn_conn_t *conn,
726                    apr_pool_t *pool,
727                    const char *cstr)
728{
729  SVN_ERR_ASSERT(cstr);
730  return svn_ra_svn__write_cstring(conn, pool, cstr);
731}
732
733static svn_error_t *
734write_tuple_cstring_opt(svn_ra_svn_conn_t *conn,
735                        apr_pool_t *pool,
736                        const char *cstr)
737{
738  return cstr ? svn_ra_svn__write_cstring(conn, pool, cstr) : SVN_NO_ERROR;
739}
740
741static svn_error_t *
742write_tuple_string(svn_ra_svn_conn_t *conn,
743                   apr_pool_t *pool,
744                   const svn_string_t *str)
745{
746  SVN_ERR_ASSERT(str);
747  return svn_ra_svn__write_string(conn, pool, str);
748}
749
750static svn_error_t *
751write_tuple_string_opt(svn_ra_svn_conn_t *conn,
752                       apr_pool_t *pool,
753                       const svn_string_t *str)
754{
755  return str ? svn_ra_svn__write_string(conn, pool, str) : SVN_NO_ERROR;
756}
757
758static svn_error_t *
759write_tuple_start_list(svn_ra_svn_conn_t *conn,
760                       apr_pool_t *pool)
761{
762  return svn_ra_svn__start_list(conn, pool);
763}
764
765static svn_error_t *
766write_tuple_end_list(svn_ra_svn_conn_t *conn,
767                     apr_pool_t *pool)
768{
769  return svn_ra_svn__end_list(conn, pool);
770}
771
772static svn_error_t *
773write_tuple_revision(svn_ra_svn_conn_t *conn,
774                     apr_pool_t *pool,
775                     svn_revnum_t rev)
776{
777  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
778  return svn_ra_svn__write_number(conn, pool, rev);
779}
780
781static svn_error_t *
782write_tuple_revision_opt(svn_ra_svn_conn_t *conn,
783                         apr_pool_t *pool,
784                         svn_revnum_t rev)
785{
786  return SVN_IS_VALID_REVNUM(rev)
787       ? svn_ra_svn__write_number(conn, pool, rev)
788       : SVN_NO_ERROR;
789}
790
791static svn_error_t *
792write_tuple_boolean(svn_ra_svn_conn_t *conn,
793                    apr_pool_t *pool,
794                    svn_boolean_t value)
795{
796  return svn_ra_svn__write_boolean(conn, pool, value);
797}
798
799static svn_error_t *
800write_tuple_depth(svn_ra_svn_conn_t *conn,
801                  apr_pool_t *pool,
802                  svn_depth_t depth)
803{
804  return svn_ra_svn__write_word(conn, pool, svn_depth_to_word(depth));
805}
806
807
808static svn_error_t *
809write_cmd_add_node(svn_ra_svn_conn_t *conn,
810                   apr_pool_t *pool,
811                   const char *path,
812                   const char *parent_token,
813                   const char *token,
814                   const char *copy_path,
815                   svn_revnum_t copy_rev)
816{
817  SVN_ERR(write_tuple_cstring(conn, pool, path));
818  SVN_ERR(write_tuple_cstring(conn, pool, parent_token));
819  SVN_ERR(write_tuple_cstring(conn, pool, token));
820  SVN_ERR(write_tuple_start_list(conn, pool));
821  SVN_ERR(write_tuple_cstring_opt(conn, pool, copy_path));
822  SVN_ERR(write_tuple_revision_opt(conn, pool, copy_rev));
823  SVN_ERR(write_tuple_end_list(conn, pool));
824
825  return SVN_NO_ERROR;
826}
827
828static svn_error_t *
829write_cmd_open_node(svn_ra_svn_conn_t *conn,
830                    apr_pool_t *pool,
831                    const char *path,
832                    const char *parent_token,
833                    const char *token,
834                    svn_revnum_t rev)
835{
836  SVN_ERR(write_tuple_cstring(conn, pool, path));
837  SVN_ERR(write_tuple_cstring(conn, pool, parent_token));
838  SVN_ERR(write_tuple_cstring(conn, pool, token));
839  SVN_ERR(write_tuple_start_list(conn, pool));
840  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
841  SVN_ERR(write_tuple_end_list(conn, pool));
842
843  return SVN_NO_ERROR;
844}
845
846static svn_error_t *
847write_cmd_change_node_prop(svn_ra_svn_conn_t *conn,
848                           apr_pool_t *pool,
849                           const char *token,
850                           const char *name,
851                           const svn_string_t *value)
852{
853  SVN_ERR(write_tuple_cstring(conn, pool, token));
854  SVN_ERR(write_tuple_cstring(conn, pool, name));
855  SVN_ERR(write_tuple_start_list(conn, pool));
856  SVN_ERR(write_tuple_string_opt(conn, pool, value));
857  SVN_ERR(write_tuple_end_list(conn, pool));
858
859  return SVN_NO_ERROR;
860}
861
862static svn_error_t *
863write_cmd_absent_node(svn_ra_svn_conn_t *conn,
864                      apr_pool_t *pool,
865                      const char *path,
866                      const char *token)
867{
868  SVN_ERR(write_tuple_cstring(conn, pool, path));
869  SVN_ERR(write_tuple_cstring(conn, pool, token));
870
871  return SVN_NO_ERROR;
872}
873
874
875
876
877static svn_error_t *vwrite_tuple(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
878                                 const char *fmt, va_list *ap)
879{
880  svn_boolean_t opt = FALSE;
881
882  if (*fmt == '!')
883    fmt++;
884  else
885    SVN_ERR(svn_ra_svn__start_list(conn, pool));
886  for (; *fmt; fmt++)
887    {
888      if (*fmt == 'c')
889        SVN_ERR(opt ? vwrite_tuple_cstring_opt(conn, pool, ap)
890                    : vwrite_tuple_cstring(conn, pool, ap));
891      else if (*fmt == 's')
892        SVN_ERR(opt ? vwrite_tuple_string_opt(conn, pool, ap)
893                    : vwrite_tuple_string(conn, pool, ap));
894      else if (*fmt == '(' && !opt)
895        SVN_ERR(write_tuple_start_list(conn, pool));
896      else if (*fmt == ')')
897        {
898          SVN_ERR(write_tuple_end_list(conn, pool));
899          opt = FALSE;
900        }
901      else if (*fmt == '?')
902        opt = TRUE;
903      else if (*fmt == 'w')
904        SVN_ERR(opt ? vwrite_tuple_word_opt(conn, pool, ap)
905                    : vwrite_tuple_word(conn, pool, ap));
906      else if (*fmt == 'r')
907        SVN_ERR(opt ? vwrite_tuple_revision_opt(conn, pool, ap)
908                    : vwrite_tuple_revision(conn, pool, ap));
909      else if (*fmt == 'n' && !opt)
910        SVN_ERR(vwrite_tuple_number(conn, pool, ap));
911      else if (*fmt == 'b' && !opt)
912        SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
913      else if (*fmt == '!' && !*(fmt + 1))
914        return SVN_NO_ERROR;
915      else
916        SVN_ERR_MALFUNCTION();
917    }
918  SVN_ERR(svn_ra_svn__end_list(conn, pool));
919  return SVN_NO_ERROR;
920}
921
922svn_error_t *
923svn_ra_svn__write_tuple(svn_ra_svn_conn_t *conn,
924                        apr_pool_t *pool,
925                        const char *fmt, ...)
926{
927  svn_error_t *err;
928  va_list ap;
929
930  va_start(ap, fmt);
931  err = vwrite_tuple(conn, pool, fmt, &ap);
932  va_end(ap);
933  return err;
934}
935
936/* --- READING DATA ITEMS --- */
937
938/* Read LEN bytes from CONN into already-allocated structure ITEM.
939 * Afterwards, *ITEM is of type 'SVN_RA_SVN_STRING', and its string
940 * data is allocated in POOL. */
941static svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
942                                svn_ra_svn_item_t *item, apr_uint64_t len64)
943{
944  apr_size_t len = (apr_size_t)len64;
945  apr_size_t readbuf_len;
946  char *dest;
947  apr_size_t buflen;
948
949  /* We can't store strings longer than the maximum size of apr_size_t,
950   * so check for wrapping */
951  if (len64 > APR_SIZE_MAX)
952    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
953                            _("String length larger than maximum"));
954
955  buflen = conn->read_end - conn->read_ptr;
956  /* Shorter strings can be copied directly from the read buffer. */
957  if (len <= buflen)
958    {
959      item->kind = SVN_RA_SVN_STRING;
960      item->u.string = svn_string_ncreate(conn->read_ptr, len, pool);
961      conn->read_ptr += len;
962    }
963  else
964    {
965      /* Read the string in chunks.  The chunk size is large enough to avoid
966       * re-allocation in typical cases, and small enough to ensure we do
967       * not pre-allocate an unreasonable amount of memory if (perhaps due
968       * to network data corruption or a DOS attack), we receive a bogus
969       * claim that a very long string is going to follow.  In that case, we
970       * start small and wait for all that data to actually show up.  This
971       * does not fully prevent DOS attacks but makes them harder (you have
972       * to actually send gigabytes of data). */
973      svn_stringbuf_t *stringbuf = svn_stringbuf_create_empty(pool);
974
975      /* Read string data directly into the string structure.
976       * Do it iteratively.  */
977      do
978        {
979          /* Determine length of chunk to read and re-alloc the buffer. */
980          readbuf_len
981            = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD
982                  ? len
983                  : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD;
984
985          svn_stringbuf_ensure(stringbuf, stringbuf->len + readbuf_len);
986          dest = stringbuf->data + stringbuf->len;
987
988          /* read data & update length info */
989          SVN_ERR(readbuf_read(conn, pool, dest, readbuf_len));
990
991          stringbuf->len += readbuf_len;
992          len -= readbuf_len;
993        }
994      while (len);
995
996      /* zero-terminate the string */
997      stringbuf->data[stringbuf->len] = '\0';
998
999      /* Return the string properly wrapped into an RA_SVN item. */
1000      item->kind = SVN_RA_SVN_STRING;
1001      item->u.string = svn_stringbuf__morph_into_string(stringbuf);
1002    }
1003
1004  return SVN_NO_ERROR;
1005}
1006
1007/* Given the first non-whitespace character FIRST_CHAR, read an item
1008 * into the already allocated structure ITEM.  LEVEL should be set
1009 * to 0 for the first call and is used to enforce a recursion limit
1010 * on the parser. */
1011static svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1012                              svn_ra_svn_item_t *item, char first_char,
1013                              int level)
1014{
1015  char c = first_char;
1016  apr_uint64_t val;
1017  svn_ra_svn_item_t *listitem;
1018
1019  if (++level >= ITEM_NESTING_LIMIT)
1020    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1021                            _("Items are nested too deeply"));
1022
1023
1024  /* Determine the item type and read it in.  Make sure that c is the
1025   * first character at the end of the item so we can test to make
1026   * sure it's whitespace. */
1027  if (svn_ctype_isdigit(c))
1028    {
1029      /* It's a number or a string.  Read the number part, either way. */
1030      val = c - '0';
1031      while (1)
1032        {
1033          apr_uint64_t prev_val = val;
1034          SVN_ERR(readbuf_getchar(conn, pool, &c));
1035          if (!svn_ctype_isdigit(c))
1036            break;
1037          val = val * 10 + (c - '0');
1038          /* val wrapped past maximum value? */
1039          if ((prev_val >= (APR_UINT64_MAX / 10))
1040              && (val < APR_UINT64_MAX - 10))
1041            return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1042                                    _("Number is larger than maximum"));
1043        }
1044      if (c == ':')
1045        {
1046          /* It's a string. */
1047          SVN_ERR(read_string(conn, pool, item, val));
1048          SVN_ERR(readbuf_getchar(conn, pool, &c));
1049        }
1050      else
1051        {
1052          /* It's a number. */
1053          item->kind = SVN_RA_SVN_NUMBER;
1054          item->u.number = val;
1055        }
1056    }
1057  else if (svn_ctype_isalpha(c))
1058    {
1059      /* It's a word.  Read it into a buffer of limited size. */
1060      char *buffer = apr_palloc(pool, MAX_WORD_LENGTH + 1);
1061      char *end = buffer + MAX_WORD_LENGTH;
1062      char *p = buffer + 1;
1063
1064      buffer[0] = c;
1065      while (1)
1066        {
1067          SVN_ERR(readbuf_getchar(conn, pool, p));
1068          if (!svn_ctype_isalnum(*p) && *p != '-')
1069            break;
1070
1071          if (++p == end)
1072            return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1073                                    _("Word is too long"));
1074        }
1075
1076      c = *p;
1077      *p = '\0';
1078
1079      item->kind = SVN_RA_SVN_WORD;
1080      item->u.word = buffer;
1081    }
1082  else if (c == '(')
1083    {
1084      /* Read in the list items. */
1085      item->kind = SVN_RA_SVN_LIST;
1086      item->u.list = apr_array_make(pool, 4, sizeof(svn_ra_svn_item_t));
1087      while (1)
1088        {
1089          SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1090          if (c == ')')
1091            break;
1092          listitem = apr_array_push(item->u.list);
1093          SVN_ERR(read_item(conn, pool, listitem, c, level));
1094        }
1095      SVN_ERR(readbuf_getchar(conn, pool, &c));
1096    }
1097
1098  if (!svn_iswhitespace(c))
1099    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1100                            _("Malformed network data"));
1101  return SVN_NO_ERROR;
1102}
1103
1104/* Given the first non-whitespace character FIRST_CHAR, read the first
1105 * command (word) encountered in CONN into *ITEM.  If ITEM is NULL, skip
1106 * to the end of the current list.  Use POOL for allocations. */
1107static svn_error_t *
1108read_command_only(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1109                  const char **item, char first_char)
1110{
1111  char c = first_char;
1112
1113  /* Determine the item type and read it in.  Make sure that c is the
1114  * first character at the end of the item so we can test to make
1115  * sure it's whitespace. */
1116  if (svn_ctype_isdigit(c))
1117    {
1118      /* It's a number or a string.  Read the number part, either way. */
1119      apr_uint64_t val, prev_val=0;
1120      val = c - '0';
1121      while (1)
1122        {
1123          prev_val = val;
1124          SVN_ERR(readbuf_getchar(conn, pool, &c));
1125          if (!svn_ctype_isdigit(c))
1126            break;
1127          val = val * 10 + (c - '0');
1128          if (prev_val >= (APR_UINT64_MAX / 10)) /* > maximum value? */
1129            return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1130                                    _("Number is larger than maximum"));
1131        }
1132      if (c == ':')
1133        {
1134          /* It's a string. */
1135          SVN_ERR(readbuf_skip(conn, val));
1136          SVN_ERR(readbuf_getchar(conn, pool, &c));
1137        }
1138    }
1139  else if (svn_ctype_isalpha(c))
1140    {
1141      /* It's a word. */
1142      if (item)
1143        {
1144          /* This is the word we want to read */
1145
1146          char *buf = apr_palloc(pool, 32);
1147          apr_size_t len = 1;
1148          buf[0] = c;
1149
1150          while (1)
1151            {
1152              SVN_ERR(readbuf_getchar(conn, pool, &c));
1153              if (!svn_ctype_isalnum(c) && c != '-')
1154                break;
1155              buf[len] = c;
1156              if (++len == 32)
1157                return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1158                                        _("Word too long"));
1159            }
1160          buf[len] = 0;
1161          *item = buf;
1162        }
1163      else
1164        {
1165          /* we don't need the actual word, just skip it */
1166          do
1167          {
1168            SVN_ERR(readbuf_getchar(conn, pool, &c));
1169          }
1170          while (svn_ctype_isalnum(c) || c == '-');
1171        }
1172    }
1173  else if (c == '(')
1174    {
1175      /* Read in the list items. */
1176      while (1)
1177        {
1178          SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1179          if (c == ')')
1180            break;
1181
1182          if (item && *item == NULL)
1183            SVN_ERR(read_command_only(conn, pool, item, c));
1184          else
1185            SVN_ERR(read_command_only(conn, pool, NULL, c));
1186        }
1187      SVN_ERR(readbuf_getchar(conn, pool, &c));
1188    }
1189
1190  return SVN_NO_ERROR;
1191}
1192
1193svn_error_t *
1194svn_ra_svn__read_item(svn_ra_svn_conn_t *conn,
1195                      apr_pool_t *pool,
1196                      svn_ra_svn_item_t **item)
1197{
1198  char c;
1199
1200  /* Allocate space, read the first character, and then do the rest of
1201   * the work.  This makes sense because of the way lists are read. */
1202  *item = apr_palloc(pool, sizeof(**item));
1203  SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1204  return read_item(conn, pool, *item, c, 0);
1205}
1206
1207/* Drain existing whitespace from the receive buffer of CONN until either
1208   there is no data in the underlying receive socket anymore or we found
1209   a non-whitespace char.  Set *HAS_ITEM to TRUE in the latter case.
1210 */
1211static svn_error_t *
1212svn_ra_svn__has_item(svn_boolean_t *has_item,
1213                     svn_ra_svn_conn_t *conn,
1214                     apr_pool_t *pool)
1215{
1216  do
1217    {
1218      if (conn->read_ptr == conn->read_end)
1219        {
1220          svn_boolean_t available;
1221          if (conn->write_pos)
1222            SVN_ERR(writebuf_flush(conn, pool));
1223
1224          SVN_ERR(svn_ra_svn__data_available(conn, &available));
1225          if (!available)
1226            break;
1227
1228          SVN_ERR(readbuf_fill(conn, pool));
1229        }
1230    }
1231  while (svn_iswhitespace(*conn->read_ptr) && ++conn->read_ptr);
1232
1233  *has_item = conn->read_ptr != conn->read_end;
1234  return SVN_NO_ERROR;
1235}
1236
1237svn_error_t *
1238svn_ra_svn__skip_leading_garbage(svn_ra_svn_conn_t *conn,
1239                                 apr_pool_t *pool)
1240{
1241  return readbuf_skip_leading_garbage(conn, pool);
1242}
1243
1244/* --- READING AND PARSING TUPLES --- */
1245
1246/* Parse a tuple of svn_ra_svn_item_t *'s.  Advance *FMT to the end of the
1247 * tuple specification and advance AP by the corresponding arguments. */
1248static svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *pool,
1249                                 const char **fmt, va_list *ap)
1250{
1251  int count, nesting_level;
1252  svn_ra_svn_item_t *elt;
1253
1254  for (count = 0; **fmt && count < items->nelts; (*fmt)++, count++)
1255    {
1256      /* '?' just means the tuple may stop; skip past it. */
1257      if (**fmt == '?')
1258        (*fmt)++;
1259      elt = &APR_ARRAY_IDX(items, count, svn_ra_svn_item_t);
1260      if (**fmt == '(' && elt->kind == SVN_RA_SVN_LIST)
1261        {
1262          (*fmt)++;
1263          SVN_ERR(vparse_tuple(elt->u.list, pool, fmt, ap));
1264        }
1265      else if (**fmt == 'c' && elt->kind == SVN_RA_SVN_STRING)
1266        *va_arg(*ap, const char **) = elt->u.string->data;
1267      else if (**fmt == 's' && elt->kind == SVN_RA_SVN_STRING)
1268        *va_arg(*ap, svn_string_t **) = elt->u.string;
1269      else if (**fmt == 'w' && elt->kind == SVN_RA_SVN_WORD)
1270        *va_arg(*ap, const char **) = elt->u.word;
1271      else if (**fmt == 'b' && elt->kind == SVN_RA_SVN_WORD)
1272        {
1273          if (strcmp(elt->u.word, "true") == 0)
1274            *va_arg(*ap, svn_boolean_t *) = TRUE;
1275          else if (strcmp(elt->u.word, "false") == 0)
1276            *va_arg(*ap, svn_boolean_t *) = FALSE;
1277          else
1278            break;
1279        }
1280      else if (**fmt == 'n' && elt->kind == SVN_RA_SVN_NUMBER)
1281        *va_arg(*ap, apr_uint64_t *) = elt->u.number;
1282      else if (**fmt == 'r' && elt->kind == SVN_RA_SVN_NUMBER)
1283        *va_arg(*ap, svn_revnum_t *) = (svn_revnum_t) elt->u.number;
1284      else if (**fmt == 'B' && elt->kind == SVN_RA_SVN_WORD)
1285        {
1286          if (strcmp(elt->u.word, "true") == 0)
1287            *va_arg(*ap, apr_uint64_t *) = TRUE;
1288          else if (strcmp(elt->u.word, "false") == 0)
1289            *va_arg(*ap, apr_uint64_t *) = FALSE;
1290          else
1291            break;
1292        }
1293      else if (**fmt == '3' && elt->kind == SVN_RA_SVN_WORD)
1294        {
1295          if (strcmp(elt->u.word, "true") == 0)
1296            *va_arg(*ap, svn_tristate_t *) = svn_tristate_true;
1297          else if (strcmp(elt->u.word, "false") == 0)
1298            *va_arg(*ap, svn_tristate_t *) = svn_tristate_false;
1299          else
1300            break;
1301        }
1302      else if (**fmt == 'l' && elt->kind == SVN_RA_SVN_LIST)
1303        *va_arg(*ap, apr_array_header_t **) = elt->u.list;
1304      else if (**fmt == ')')
1305        return SVN_NO_ERROR;
1306      else
1307        break;
1308    }
1309  if (**fmt == '?')
1310    {
1311      nesting_level = 0;
1312      for (; **fmt; (*fmt)++)
1313        {
1314          switch (**fmt)
1315            {
1316            case '?':
1317              break;
1318            case 'r':
1319              *va_arg(*ap, svn_revnum_t *) = SVN_INVALID_REVNUM;
1320              break;
1321            case 's':
1322              *va_arg(*ap, svn_string_t **) = NULL;
1323              break;
1324            case 'c':
1325            case 'w':
1326              *va_arg(*ap, const char **) = NULL;
1327              break;
1328            case 'l':
1329              *va_arg(*ap, apr_array_header_t **) = NULL;
1330              break;
1331            case 'B':
1332            case 'n':
1333              *va_arg(*ap, apr_uint64_t *) = SVN_RA_SVN_UNSPECIFIED_NUMBER;
1334              break;
1335            case '3':
1336              *va_arg(*ap, svn_tristate_t *) = svn_tristate_unknown;
1337              break;
1338            case '(':
1339              nesting_level++;
1340              break;
1341            case ')':
1342              if (--nesting_level < 0)
1343                return SVN_NO_ERROR;
1344              break;
1345            default:
1346              SVN_ERR_MALFUNCTION();
1347            }
1348        }
1349    }
1350  if (**fmt && **fmt != ')')
1351    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1352                            _("Malformed network data"));
1353  return SVN_NO_ERROR;
1354}
1355
1356svn_error_t *
1357svn_ra_svn__parse_tuple(const apr_array_header_t *list,
1358                        apr_pool_t *pool,
1359                        const char *fmt, ...)
1360{
1361  svn_error_t *err;
1362  va_list ap;
1363
1364  va_start(ap, fmt);
1365  err = vparse_tuple(list, pool, &fmt, &ap);
1366  va_end(ap);
1367  return err;
1368}
1369
1370svn_error_t *
1371svn_ra_svn__read_tuple(svn_ra_svn_conn_t *conn,
1372                       apr_pool_t *pool,
1373                       const char *fmt, ...)
1374{
1375  va_list ap;
1376  svn_ra_svn_item_t *item;
1377  svn_error_t *err;
1378
1379  SVN_ERR(svn_ra_svn__read_item(conn, pool, &item));
1380  if (item->kind != SVN_RA_SVN_LIST)
1381    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1382                            _("Malformed network data"));
1383  va_start(ap, fmt);
1384  err = vparse_tuple(item->u.list, pool, &fmt, &ap);
1385  va_end(ap);
1386  return err;
1387}
1388
1389svn_error_t *
1390svn_ra_svn__read_command_only(svn_ra_svn_conn_t *conn,
1391                              apr_pool_t *pool,
1392                              const char **command)
1393{
1394  char c;
1395  SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1396
1397  *command = NULL;
1398  return read_command_only(conn, pool, command, c);
1399}
1400
1401
1402svn_error_t *
1403svn_ra_svn__parse_proplist(const apr_array_header_t *list,
1404                           apr_pool_t *pool,
1405                           apr_hash_t **props)
1406{
1407  svn_string_t *name;
1408  svn_string_t *value;
1409  svn_ra_svn_item_t *elt;
1410  int i;
1411
1412  *props = svn_hash__make(pool);
1413  for (i = 0; i < list->nelts; i++)
1414    {
1415      elt = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
1416      if (elt->kind != SVN_RA_SVN_LIST)
1417        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1418                                _("Proplist element not a list"));
1419      SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, pool, "ss",
1420                                      &name, &value));
1421      apr_hash_set(*props, name->data, name->len, value);
1422    }
1423
1424  return SVN_NO_ERROR;
1425}
1426
1427
1428/* --- READING AND WRITING COMMANDS AND RESPONSES --- */
1429
1430svn_error_t *svn_ra_svn__locate_real_error_child(svn_error_t *err)
1431{
1432  svn_error_t *this_link;
1433
1434  SVN_ERR_ASSERT(err);
1435
1436  for (this_link = err;
1437       this_link && (this_link->apr_err == SVN_ERR_RA_SVN_CMD_ERR);
1438       this_link = this_link->child)
1439    ;
1440
1441  SVN_ERR_ASSERT(this_link);
1442  return this_link;
1443}
1444
1445svn_error_t *svn_ra_svn__handle_failure_status(const apr_array_header_t *params,
1446                                               apr_pool_t *pool)
1447{
1448  const char *message, *file;
1449  svn_error_t *err = NULL;
1450  svn_ra_svn_item_t *elt;
1451  int i;
1452  apr_uint64_t apr_err, line;
1453  apr_pool_t *subpool = svn_pool_create(pool);
1454
1455  if (params->nelts == 0)
1456    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1457                            _("Empty error list"));
1458
1459  /* Rebuild the error list from the end, to avoid reversing the order. */
1460  for (i = params->nelts - 1; i >= 0; i--)
1461    {
1462      svn_pool_clear(subpool);
1463      elt = &APR_ARRAY_IDX(params, i, svn_ra_svn_item_t);
1464      if (elt->kind != SVN_RA_SVN_LIST)
1465        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1466                                _("Malformed error list"));
1467      SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, subpool, "nccn",
1468                                      &apr_err, &message, &file, &line));
1469      /* The message field should have been optional, but we can't
1470         easily change that, so "" means a nonexistent message. */
1471      if (!*message)
1472        message = NULL;
1473
1474      /* Skip over links in the error chain that were intended only to
1475         exist on the server (to wrap real errors intended for the
1476         client) but accidentally got included in the server's actual
1477         response. */
1478      if ((apr_status_t)apr_err != SVN_ERR_RA_SVN_CMD_ERR)
1479        {
1480          err = svn_error_create((apr_status_t)apr_err, err, message);
1481          err->file = apr_pstrdup(err->pool, file);
1482          err->line = (long)line;
1483        }
1484    }
1485
1486  svn_pool_destroy(subpool);
1487
1488  /* If we get here, then we failed to find a real error in the error
1489     chain that the server proported to be sending us.  That's bad. */
1490  if (! err)
1491    err = svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1492                           _("Malformed error list"));
1493
1494  return err;
1495}
1496
1497svn_error_t *
1498svn_ra_svn__read_cmd_response(svn_ra_svn_conn_t *conn,
1499                              apr_pool_t *pool,
1500                              const char *fmt, ...)
1501{
1502  va_list ap;
1503  const char *status;
1504  apr_array_header_t *params;
1505  svn_error_t *err;
1506
1507  SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "wl", &status, &params));
1508  if (strcmp(status, "success") == 0)
1509    {
1510      va_start(ap, fmt);
1511      err = vparse_tuple(params, pool, &fmt, &ap);
1512      va_end(ap);
1513      return err;
1514    }
1515  else if (strcmp(status, "failure") == 0)
1516    {
1517      return svn_error_trace(svn_ra_svn__handle_failure_status(params, pool));
1518    }
1519
1520  return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1521                           _("Unknown status '%s' in command response"),
1522                           status);
1523}
1524
1525svn_error_t *
1526svn_ra_svn__has_command(svn_boolean_t *has_command,
1527                        svn_boolean_t *terminated,
1528                        svn_ra_svn_conn_t *conn,
1529                        apr_pool_t *pool)
1530{
1531  svn_error_t *err = svn_ra_svn__has_item(has_command, conn, pool);
1532  if (err && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED)
1533    {
1534      *terminated = TRUE;
1535      svn_error_clear(err);
1536      return SVN_NO_ERROR;
1537    }
1538
1539  *terminated = FALSE;
1540  return svn_error_trace(err);
1541}
1542
1543svn_error_t *
1544svn_ra_svn__handle_command(svn_boolean_t *terminate,
1545                           apr_hash_t *cmd_hash,
1546                           void *baton,
1547                           svn_ra_svn_conn_t *conn,
1548                           svn_boolean_t error_on_disconnect,
1549                           apr_pool_t *pool)
1550{
1551  const char *cmdname;
1552  svn_error_t *err, *write_err;
1553  apr_array_header_t *params;
1554  const svn_ra_svn_cmd_entry_t *command;
1555
1556  *terminate = FALSE;
1557  err = svn_ra_svn__read_tuple(conn, pool, "wl", &cmdname, &params);
1558  if (err)
1559    {
1560      if (!error_on_disconnect
1561          && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED)
1562        {
1563          svn_error_clear(err);
1564          *terminate = TRUE;
1565          return SVN_NO_ERROR;
1566        }
1567      return err;
1568    }
1569
1570  command = svn_hash_gets(cmd_hash, cmdname);
1571  if (command)
1572    {
1573      err = (*command->handler)(conn, pool, params, baton);
1574      *terminate = command->terminate;
1575    }
1576  else
1577    {
1578      err = svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL,
1579                              _("Unknown editor command '%s'"), cmdname);
1580      err = svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, err, NULL);
1581    }
1582
1583  if (err && err->apr_err == SVN_ERR_RA_SVN_CMD_ERR)
1584    {
1585      write_err = svn_ra_svn__write_cmd_failure(
1586                      conn, pool,
1587                      svn_ra_svn__locate_real_error_child(err));
1588      svn_error_clear(err);
1589      return write_err ? write_err : SVN_NO_ERROR;
1590    }
1591
1592  return err;
1593}
1594
1595svn_error_t *
1596svn_ra_svn__handle_commands2(svn_ra_svn_conn_t *conn,
1597                             apr_pool_t *pool,
1598                             const svn_ra_svn_cmd_entry_t *commands,
1599                             void *baton,
1600                             svn_boolean_t error_on_disconnect)
1601{
1602  apr_pool_t *subpool = svn_pool_create(pool);
1603  apr_pool_t *iterpool = svn_pool_create(subpool);
1604  const svn_ra_svn_cmd_entry_t *command;
1605  apr_hash_t *cmd_hash = apr_hash_make(subpool);
1606
1607  for (command = commands; command->cmdname; command++)
1608    svn_hash_sets(cmd_hash, command->cmdname, command);
1609
1610  while (1)
1611    {
1612      svn_boolean_t terminate;
1613      svn_error_t *err;
1614      svn_pool_clear(iterpool);
1615
1616      err = svn_ra_svn__handle_command(&terminate, cmd_hash, baton, conn,
1617                                       error_on_disconnect, iterpool);
1618      if (err)
1619        {
1620          svn_pool_destroy(subpool);
1621          return svn_error_trace(err);
1622        }
1623      if (terminate)
1624        break;
1625    }
1626  svn_pool_destroy(iterpool);
1627  svn_pool_destroy(subpool);
1628  return SVN_NO_ERROR;
1629}
1630
1631svn_error_t *
1632svn_ra_svn__write_cmd_target_rev(svn_ra_svn_conn_t *conn,
1633                                 apr_pool_t *pool,
1634                                 svn_revnum_t rev)
1635{
1636  SVN_ERR(writebuf_write_literal(conn, pool, "( target-rev ( "));
1637  SVN_ERR(write_tuple_revision(conn, pool, rev));
1638  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1639
1640  return SVN_NO_ERROR;
1641}
1642
1643svn_error_t *
1644svn_ra_svn__write_cmd_open_root(svn_ra_svn_conn_t *conn,
1645                                apr_pool_t *pool,
1646                                svn_revnum_t rev,
1647                                const char *token)
1648{
1649  SVN_ERR(writebuf_write_literal(conn, pool, "( open-root ( "));
1650  SVN_ERR(write_tuple_start_list(conn, pool));
1651  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
1652  SVN_ERR(write_tuple_end_list(conn, pool));
1653  SVN_ERR(write_tuple_cstring(conn, pool, token));
1654  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1655
1656  return SVN_NO_ERROR;
1657}
1658
1659svn_error_t *
1660svn_ra_svn__write_cmd_delete_entry(svn_ra_svn_conn_t *conn,
1661                                   apr_pool_t *pool,
1662                                   const char *path,
1663                                   svn_revnum_t rev,
1664                                   const char *token)
1665{
1666  SVN_ERR(writebuf_write_literal(conn, pool, "( delete-entry ( "));
1667  SVN_ERR(write_tuple_cstring(conn, pool, path));
1668  SVN_ERR(write_tuple_start_list(conn, pool));
1669  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
1670  SVN_ERR(write_tuple_end_list(conn, pool));
1671  SVN_ERR(write_tuple_cstring(conn, pool, token));
1672  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1673
1674  return SVN_NO_ERROR;
1675}
1676
1677svn_error_t *
1678svn_ra_svn__write_cmd_add_dir(svn_ra_svn_conn_t *conn,
1679                              apr_pool_t *pool,
1680                              const char *path,
1681                              const char *parent_token,
1682                              const char *token,
1683                              const char *copy_path,
1684                              svn_revnum_t copy_rev)
1685{
1686  SVN_ERR(writebuf_write_literal(conn, pool, "( add-dir ( "));
1687  SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token,
1688                              copy_path, copy_rev));
1689  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1690
1691  return SVN_NO_ERROR;
1692}
1693
1694svn_error_t *
1695svn_ra_svn__write_cmd_open_dir(svn_ra_svn_conn_t *conn,
1696                               apr_pool_t *pool,
1697                               const char *path,
1698                               const char *parent_token,
1699                               const char *token,
1700                               svn_revnum_t rev)
1701{
1702  SVN_ERR(writebuf_write_literal(conn, pool, "( open-dir ( "));
1703  SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev));
1704  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1705
1706  return SVN_NO_ERROR;
1707}
1708
1709svn_error_t *
1710svn_ra_svn__write_cmd_change_dir_prop(svn_ra_svn_conn_t *conn,
1711                                      apr_pool_t *pool,
1712                                      const char *token,
1713                                      const char *name,
1714                                      const svn_string_t *value)
1715{
1716  SVN_ERR(writebuf_write_literal(conn, pool, "( change-dir-prop ( "));
1717  SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value));
1718  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1719
1720  return SVN_NO_ERROR;
1721}
1722
1723svn_error_t *
1724svn_ra_svn__write_cmd_close_dir(svn_ra_svn_conn_t *conn,
1725                                apr_pool_t *pool,
1726                                const char *token)
1727{
1728  SVN_ERR(writebuf_write_literal(conn, pool, "( close-dir ( "));
1729  SVN_ERR(write_tuple_cstring(conn, pool, token));
1730  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1731
1732  return SVN_NO_ERROR;
1733}
1734
1735svn_error_t *
1736svn_ra_svn__write_cmd_absent_dir(svn_ra_svn_conn_t *conn,
1737                                 apr_pool_t *pool,
1738                                 const char *path,
1739                                 const char *parent_token)
1740{
1741  SVN_ERR(writebuf_write_literal(conn, pool, "( absent-dir ( "));
1742  SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token));
1743  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1744
1745  return SVN_NO_ERROR;
1746}
1747
1748svn_error_t *
1749svn_ra_svn__write_cmd_add_file(svn_ra_svn_conn_t *conn,
1750                               apr_pool_t *pool,
1751                               const char *path,
1752                               const char *parent_token,
1753                               const char *token,
1754                               const char *copy_path,
1755                               svn_revnum_t copy_rev)
1756{
1757  SVN_ERR(writebuf_write_literal(conn, pool, "( add-file ( "));
1758  SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token,
1759                              copy_path, copy_rev));
1760  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1761
1762  return SVN_NO_ERROR;
1763}
1764
1765svn_error_t *
1766svn_ra_svn__write_cmd_open_file(svn_ra_svn_conn_t *conn,
1767                                apr_pool_t *pool,
1768                                const char *path,
1769                                const char *parent_token,
1770                                const char *token,
1771                                svn_revnum_t rev)
1772{
1773  SVN_ERR(writebuf_write_literal(conn, pool, "( open-file ( "));
1774  SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev));
1775  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1776
1777  return SVN_NO_ERROR;
1778}
1779
1780svn_error_t *
1781svn_ra_svn__write_cmd_change_file_prop(svn_ra_svn_conn_t *conn,
1782                                       apr_pool_t *pool,
1783                                       const char *token,
1784                                       const char *name,
1785                                       const svn_string_t *value)
1786{
1787  SVN_ERR(writebuf_write_literal(conn, pool, "( change-file-prop ( "));
1788  SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value));
1789  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1790
1791  return SVN_NO_ERROR;
1792}
1793
1794svn_error_t *
1795svn_ra_svn__write_cmd_close_file(svn_ra_svn_conn_t *conn,
1796                                 apr_pool_t *pool,
1797                                 const char *token,
1798                                 const char *text_checksum)
1799{
1800  SVN_ERR(writebuf_write_literal(conn, pool, "( close-file ( "));
1801  SVN_ERR(write_tuple_cstring(conn, pool, token));
1802  SVN_ERR(write_tuple_start_list(conn, pool));
1803  SVN_ERR(write_tuple_cstring_opt(conn, pool, text_checksum));
1804  SVN_ERR(write_tuple_end_list(conn, pool));
1805  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1806
1807  return SVN_NO_ERROR;
1808}
1809
1810svn_error_t *
1811svn_ra_svn__write_cmd_absent_file(svn_ra_svn_conn_t *conn,
1812                                  apr_pool_t *pool,
1813                                  const char *path,
1814                                  const char *parent_token)
1815{
1816  SVN_ERR(writebuf_write_literal(conn, pool, "( absent-file ( "));
1817  SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token));
1818  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1819
1820  return SVN_NO_ERROR;
1821}
1822
1823svn_error_t *
1824svn_ra_svn__write_cmd_textdelta_chunk(svn_ra_svn_conn_t *conn,
1825                                      apr_pool_t *pool,
1826                                      const char *token,
1827                                      const svn_string_t *chunk)
1828{
1829  SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-chunk ( "));
1830  SVN_ERR(write_tuple_cstring(conn, pool, token));
1831  SVN_ERR(write_tuple_string(conn, pool, chunk));
1832  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1833
1834  return SVN_NO_ERROR;
1835}
1836
1837svn_error_t *
1838svn_ra_svn__write_cmd_textdelta_end(svn_ra_svn_conn_t *conn,
1839                                    apr_pool_t *pool,
1840                                    const char *token)
1841{
1842  SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-end ( "));
1843  SVN_ERR(write_tuple_cstring(conn, pool, token));
1844  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1845
1846  return SVN_NO_ERROR;
1847}
1848
1849svn_error_t *
1850svn_ra_svn__write_cmd_apply_textdelta(svn_ra_svn_conn_t *conn,
1851                                      apr_pool_t *pool,
1852                                      const char *token,
1853                                      const char *base_checksum)
1854{
1855  SVN_ERR(writebuf_write_literal(conn, pool, "( apply-textdelta ( "));
1856  SVN_ERR(write_tuple_cstring(conn, pool, token));
1857  SVN_ERR(write_tuple_start_list(conn, pool));
1858  SVN_ERR(write_tuple_cstring_opt(conn, pool, base_checksum));
1859  SVN_ERR(write_tuple_end_list(conn, pool));
1860  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1861
1862  return SVN_NO_ERROR;
1863}
1864
1865svn_error_t *
1866svn_ra_svn__write_cmd_close_edit(svn_ra_svn_conn_t *conn,
1867                                 apr_pool_t *pool)
1868{
1869  return writebuf_write_literal(conn, pool, "( close-edit ( ) ) ");
1870}
1871
1872svn_error_t *
1873svn_ra_svn__write_cmd_abort_edit(svn_ra_svn_conn_t *conn,
1874                                 apr_pool_t *pool)
1875{
1876  return writebuf_write_literal(conn, pool, "( abort-edit ( ) ) ");
1877}
1878
1879svn_error_t *
1880svn_ra_svn__write_cmd_set_path(svn_ra_svn_conn_t *conn,
1881                               apr_pool_t *pool,
1882                               const char *path,
1883                               svn_revnum_t rev,
1884                               svn_boolean_t start_empty,
1885                               const char *lock_token,
1886                               svn_depth_t depth)
1887{
1888  SVN_ERR(writebuf_write_literal(conn, pool, "( set-path ( "));
1889  SVN_ERR(write_tuple_cstring(conn, pool, path));
1890  SVN_ERR(write_tuple_revision(conn, pool, rev));
1891  SVN_ERR(write_tuple_boolean(conn, pool, start_empty));
1892  SVN_ERR(write_tuple_start_list(conn, pool));
1893  SVN_ERR(write_tuple_cstring_opt(conn, pool, lock_token));
1894  SVN_ERR(write_tuple_end_list(conn, pool));
1895  SVN_ERR(write_tuple_depth(conn, pool, depth));
1896  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1897
1898  return SVN_NO_ERROR;
1899}
1900
1901svn_error_t *
1902svn_ra_svn__write_cmd_delete_path(svn_ra_svn_conn_t *conn,
1903                                  apr_pool_t *pool,
1904                                  const char *path)
1905{
1906  SVN_ERR(writebuf_write_literal(conn, pool, "( delete-path ( "));
1907  SVN_ERR(write_tuple_cstring(conn, pool, path));
1908  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1909
1910  return SVN_NO_ERROR;
1911}
1912
1913svn_error_t *
1914svn_ra_svn__write_cmd_link_path(svn_ra_svn_conn_t *conn,
1915                                apr_pool_t *pool,
1916                                const char *path,
1917                                const char *url,
1918                                svn_revnum_t rev,
1919                                svn_boolean_t start_empty,
1920                                const char *lock_token,
1921                                svn_depth_t depth)
1922{
1923  SVN_ERR(writebuf_write_literal(conn, pool, "( link-path ( "));
1924  SVN_ERR(write_tuple_cstring(conn, pool, path));
1925  SVN_ERR(write_tuple_cstring(conn, pool, url));
1926  SVN_ERR(write_tuple_revision(conn, pool, rev));
1927  SVN_ERR(write_tuple_boolean(conn, pool, start_empty));
1928  SVN_ERR(write_tuple_start_list(conn, pool));
1929  SVN_ERR(write_tuple_cstring_opt(conn, pool,lock_token));
1930  SVN_ERR(write_tuple_end_list(conn, pool));
1931  SVN_ERR(write_tuple_depth(conn, pool, depth));
1932  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1933
1934  return SVN_NO_ERROR;
1935}
1936
1937svn_error_t *
1938svn_ra_svn__write_cmd_finish_report(svn_ra_svn_conn_t *conn,
1939                                    apr_pool_t *pool)
1940{
1941  return writebuf_write_literal(conn, pool, "( finish-report ( ) ) ");
1942}
1943
1944svn_error_t *
1945svn_ra_svn__write_cmd_abort_report(svn_ra_svn_conn_t *conn,
1946                                   apr_pool_t *pool)
1947{
1948  return writebuf_write_literal(conn, pool, "( abort-report ( ) ) ");
1949}
1950
1951svn_error_t *
1952svn_ra_svn__write_cmd_reparent(svn_ra_svn_conn_t *conn,
1953                               apr_pool_t *pool,
1954                               const char *url)
1955{
1956  SVN_ERR(writebuf_write_literal(conn, pool, "( reparent ( "));
1957  SVN_ERR(write_tuple_cstring(conn, pool, url));
1958  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1959
1960  return SVN_NO_ERROR;
1961}
1962
1963svn_error_t *
1964svn_ra_svn__write_cmd_get_latest_rev(svn_ra_svn_conn_t *conn,
1965                                   apr_pool_t *pool)
1966{
1967  return writebuf_write_literal(conn, pool, "( get-latest-rev ( ) ) ");
1968}
1969
1970svn_error_t *
1971svn_ra_svn__write_cmd_get_dated_rev(svn_ra_svn_conn_t *conn,
1972                                    apr_pool_t *pool,
1973                                    apr_time_t tm)
1974{
1975  SVN_ERR(writebuf_write_literal(conn, pool, "( get-dated-rev ( "));
1976  SVN_ERR(write_tuple_cstring(conn, pool, svn_time_to_cstring(tm, pool)));
1977  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1978
1979  return SVN_NO_ERROR;
1980}
1981
1982svn_error_t *
1983svn_ra_svn__write_cmd_change_rev_prop2(svn_ra_svn_conn_t *conn,
1984                                       apr_pool_t *pool,
1985                                       svn_revnum_t rev,
1986                                       const char *name,
1987                                       const svn_string_t *value,
1988                                       svn_boolean_t dont_care,
1989                                       const svn_string_t *old_value)
1990{
1991  SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop2 ( "));
1992  SVN_ERR(write_tuple_revision(conn, pool, rev));
1993  SVN_ERR(write_tuple_cstring(conn, pool, name));
1994  SVN_ERR(write_tuple_start_list(conn, pool));
1995  SVN_ERR(write_tuple_string_opt(conn, pool, value));
1996  SVN_ERR(write_tuple_end_list(conn, pool));
1997  SVN_ERR(write_tuple_start_list(conn, pool));
1998  SVN_ERR(write_tuple_boolean(conn, pool, dont_care));
1999  SVN_ERR(write_tuple_string_opt(conn, pool, old_value));
2000  SVN_ERR(write_tuple_end_list(conn, pool));
2001  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2002
2003  return SVN_NO_ERROR;
2004}
2005
2006svn_error_t *
2007svn_ra_svn__write_cmd_change_rev_prop(svn_ra_svn_conn_t *conn,
2008                                      apr_pool_t *pool,
2009                                      svn_revnum_t rev,
2010                                      const char *name,
2011                                      const svn_string_t *value)
2012{
2013  SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop ( "));
2014  SVN_ERR(write_tuple_revision(conn, pool, rev));
2015  SVN_ERR(write_tuple_cstring(conn, pool, name));
2016  SVN_ERR(write_tuple_string_opt(conn, pool, value));
2017  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2018
2019  return SVN_NO_ERROR;
2020}
2021
2022svn_error_t *
2023svn_ra_svn__write_cmd_rev_proplist(svn_ra_svn_conn_t *conn,
2024                                   apr_pool_t *pool,
2025                                   svn_revnum_t rev)
2026{
2027  SVN_ERR(writebuf_write_literal(conn, pool, "( rev-proplist ( "));
2028  SVN_ERR(write_tuple_revision(conn, pool, rev));
2029  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2030
2031  return SVN_NO_ERROR;
2032}
2033
2034svn_error_t *
2035svn_ra_svn__write_cmd_rev_prop(svn_ra_svn_conn_t *conn,
2036                               apr_pool_t *pool,
2037                               svn_revnum_t rev,
2038                               const char *name)
2039{
2040  SVN_ERR(writebuf_write_literal(conn, pool, "( rev-prop ( "));
2041  SVN_ERR(write_tuple_revision(conn, pool, rev));
2042  SVN_ERR(write_tuple_cstring(conn, pool, name));
2043  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2044
2045  return SVN_NO_ERROR;
2046}
2047
2048svn_error_t *
2049svn_ra_svn__write_cmd_get_file(svn_ra_svn_conn_t *conn,
2050                               apr_pool_t *pool,
2051                               const char *path,
2052                               svn_revnum_t rev,
2053                               svn_boolean_t props,
2054                               svn_boolean_t stream)
2055{
2056  SVN_ERR(writebuf_write_literal(conn, pool, "( get-file ( "));
2057  SVN_ERR(write_tuple_cstring(conn, pool, path));
2058  SVN_ERR(write_tuple_start_list(conn, pool));
2059  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2060  SVN_ERR(write_tuple_end_list(conn, pool));
2061  SVN_ERR(write_tuple_boolean(conn, pool, props));
2062  SVN_ERR(write_tuple_boolean(conn, pool, stream));
2063
2064  /* Always send the, nominally optional, want-iprops as "false" to
2065     workaround a bug in svnserve 1.8.0-1.8.8 that causes the server
2066     to see "true" if it is omitted. */
2067  SVN_ERR(writebuf_write_literal(conn, pool, " false ) ) "));
2068
2069  return SVN_NO_ERROR;
2070}
2071
2072svn_error_t *
2073svn_ra_svn__write_cmd_update(svn_ra_svn_conn_t *conn,
2074                             apr_pool_t *pool,
2075                             svn_revnum_t rev,
2076                             const char *target,
2077                             svn_boolean_t recurse,
2078                             svn_depth_t depth,
2079                             svn_boolean_t send_copyfrom_args,
2080                             svn_boolean_t ignore_ancestry)
2081{
2082  SVN_ERR(writebuf_write_literal(conn, pool, "( update ( "));
2083  SVN_ERR(write_tuple_start_list(conn, pool));
2084  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2085  SVN_ERR(write_tuple_end_list(conn, pool));
2086  SVN_ERR(write_tuple_cstring(conn, pool, target));
2087  SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2088  SVN_ERR(write_tuple_depth(conn, pool, depth));
2089  SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args));
2090  SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2091  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2092
2093  return SVN_NO_ERROR;
2094}
2095
2096svn_error_t *
2097svn_ra_svn__write_cmd_switch(svn_ra_svn_conn_t *conn,
2098                             apr_pool_t *pool,
2099                             svn_revnum_t rev,
2100                             const char *target,
2101                             svn_boolean_t recurse,
2102                             const char *switch_url,
2103                             svn_depth_t depth,
2104                             svn_boolean_t send_copyfrom_args,
2105                             svn_boolean_t ignore_ancestry)
2106{
2107  SVN_ERR(writebuf_write_literal(conn, pool, "( switch ( "));
2108  SVN_ERR(write_tuple_start_list(conn, pool));
2109  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2110  SVN_ERR(write_tuple_end_list(conn, pool));
2111  SVN_ERR(write_tuple_cstring(conn, pool, target));
2112  SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2113  SVN_ERR(write_tuple_cstring(conn, pool, switch_url));
2114  SVN_ERR(write_tuple_depth(conn, pool, depth));
2115  SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args));
2116  SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2117  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2118
2119  return SVN_NO_ERROR;
2120}
2121
2122svn_error_t *
2123svn_ra_svn__write_cmd_status(svn_ra_svn_conn_t *conn,
2124                             apr_pool_t *pool,
2125                             const char *target,
2126                             svn_boolean_t recurse,
2127                             svn_revnum_t rev,
2128                             svn_depth_t depth)
2129{
2130  SVN_ERR(writebuf_write_literal(conn, pool, "( status ( "));
2131  SVN_ERR(write_tuple_cstring(conn, pool, target));
2132  SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2133  SVN_ERR(write_tuple_start_list(conn, pool));
2134  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2135  SVN_ERR(write_tuple_end_list(conn, pool));
2136  SVN_ERR(write_tuple_depth(conn, pool, depth));
2137  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2138
2139  return SVN_NO_ERROR;
2140}
2141
2142svn_error_t *
2143svn_ra_svn__write_cmd_diff(svn_ra_svn_conn_t *conn,
2144                           apr_pool_t *pool,
2145                           svn_revnum_t rev,
2146                           const char *target,
2147                           svn_boolean_t recurse,
2148                           svn_boolean_t ignore_ancestry,
2149                           const char *versus_url,
2150                           svn_boolean_t text_deltas,
2151                           svn_depth_t depth)
2152{
2153  SVN_ERR(writebuf_write_literal(conn, pool, "( diff ( "));
2154  SVN_ERR(write_tuple_start_list(conn, pool));
2155  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2156  SVN_ERR(write_tuple_end_list(conn, pool));
2157  SVN_ERR(write_tuple_cstring(conn, pool, target));
2158  SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2159  SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2160  SVN_ERR(write_tuple_cstring(conn, pool, versus_url));
2161  SVN_ERR(write_tuple_boolean(conn, pool, text_deltas));
2162  SVN_ERR(write_tuple_depth(conn, pool, depth));
2163  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2164
2165  return SVN_NO_ERROR;
2166}
2167
2168svn_error_t *
2169svn_ra_svn__write_cmd_check_path(svn_ra_svn_conn_t *conn,
2170                                 apr_pool_t *pool,
2171                                 const char *path,
2172                                 svn_revnum_t rev)
2173{
2174  SVN_ERR(writebuf_write_literal(conn, pool, "( check-path ( "));
2175  SVN_ERR(write_tuple_cstring(conn, pool, path));
2176  SVN_ERR(write_tuple_start_list(conn, pool));
2177  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2178  SVN_ERR(write_tuple_end_list(conn, pool));
2179  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2180
2181  return SVN_NO_ERROR;
2182}
2183
2184svn_error_t *
2185svn_ra_svn__write_cmd_stat(svn_ra_svn_conn_t *conn,
2186                           apr_pool_t *pool,
2187                           const char *path,
2188                           svn_revnum_t rev)
2189{
2190  SVN_ERR(writebuf_write_literal(conn, pool, "( stat ( "));
2191  SVN_ERR(write_tuple_cstring(conn, pool, path));
2192  SVN_ERR(write_tuple_start_list(conn, pool));
2193  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2194  SVN_ERR(write_tuple_end_list(conn, pool));
2195  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2196
2197  return SVN_NO_ERROR;
2198}
2199
2200svn_error_t *
2201svn_ra_svn__write_cmd_get_file_revs(svn_ra_svn_conn_t *conn,
2202                                    apr_pool_t *pool,
2203                                    const char *path,
2204                                    svn_revnum_t start,
2205                                    svn_revnum_t end,
2206                                    svn_boolean_t include_merged_revisions)
2207{
2208  SVN_ERR(writebuf_write_literal(conn, pool, "( get-file-revs ( "));
2209  SVN_ERR(write_tuple_cstring(conn, pool, path));
2210  SVN_ERR(write_tuple_start_list(conn, pool));
2211  SVN_ERR(write_tuple_revision_opt(conn, pool, start));
2212  SVN_ERR(write_tuple_end_list(conn, pool));
2213  SVN_ERR(write_tuple_start_list(conn, pool));
2214  SVN_ERR(write_tuple_revision_opt(conn, pool, end));
2215  SVN_ERR(write_tuple_end_list(conn, pool));
2216  SVN_ERR(write_tuple_boolean(conn, pool, include_merged_revisions));
2217  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2218
2219  return SVN_NO_ERROR;
2220}
2221
2222svn_error_t *
2223svn_ra_svn__write_cmd_lock(svn_ra_svn_conn_t *conn,
2224                           apr_pool_t *pool,
2225                           const char *path,
2226                           const char *comment,
2227                           svn_boolean_t steal_lock,
2228                           svn_revnum_t revnum)
2229{
2230  SVN_ERR(writebuf_write_literal(conn, pool, "( lock ( "));
2231  SVN_ERR(write_tuple_cstring(conn, pool, path));
2232  SVN_ERR(write_tuple_start_list(conn, pool));
2233  SVN_ERR(write_tuple_cstring_opt(conn, pool, comment));
2234  SVN_ERR(write_tuple_end_list(conn, pool));
2235  SVN_ERR(write_tuple_boolean(conn, pool, steal_lock));
2236  SVN_ERR(write_tuple_start_list(conn, pool));
2237  SVN_ERR(write_tuple_revision_opt(conn, pool, revnum));
2238  SVN_ERR(write_tuple_end_list(conn, pool));
2239  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2240
2241  return SVN_NO_ERROR;
2242}
2243
2244svn_error_t *
2245svn_ra_svn__write_cmd_unlock(svn_ra_svn_conn_t *conn,
2246                             apr_pool_t *pool,
2247                             const char *path,
2248                             const char *token,
2249                             svn_boolean_t break_lock)
2250{
2251  SVN_ERR(writebuf_write_literal(conn, pool, "( unlock ( "));
2252  SVN_ERR(write_tuple_cstring(conn, pool, path));
2253  SVN_ERR(write_tuple_start_list(conn, pool));
2254  SVN_ERR(write_tuple_cstring_opt(conn, pool, token));
2255  SVN_ERR(write_tuple_end_list(conn, pool));
2256  SVN_ERR(write_tuple_boolean(conn, pool, break_lock));
2257  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2258
2259  return SVN_NO_ERROR;
2260}
2261
2262svn_error_t *
2263svn_ra_svn__write_cmd_get_lock(svn_ra_svn_conn_t *conn,
2264                               apr_pool_t *pool,
2265                               const char *path)
2266{
2267  SVN_ERR(writebuf_write_literal(conn, pool, "( get-lock ( "));
2268  SVN_ERR(write_tuple_cstring(conn, pool, path));
2269  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2270
2271  return SVN_NO_ERROR;
2272}
2273
2274svn_error_t *
2275svn_ra_svn__write_cmd_get_locks(svn_ra_svn_conn_t *conn,
2276                                apr_pool_t *pool,
2277                                const char *path,
2278                                svn_depth_t depth)
2279{
2280  SVN_ERR(writebuf_write_literal(conn, pool, "( get-locks ( "));
2281  SVN_ERR(write_tuple_cstring(conn, pool, path));
2282  SVN_ERR(write_tuple_start_list(conn, pool));
2283  SVN_ERR(write_tuple_depth(conn, pool, depth));
2284  SVN_ERR(write_tuple_end_list(conn, pool));
2285  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2286
2287  return SVN_NO_ERROR;
2288}
2289
2290svn_error_t *
2291svn_ra_svn__write_cmd_replay(svn_ra_svn_conn_t *conn,
2292                             apr_pool_t *pool,
2293                             svn_revnum_t rev,
2294                             svn_revnum_t low_water_mark,
2295                             svn_boolean_t send_deltas)
2296{
2297  SVN_ERR(writebuf_write_literal(conn, pool, "( replay ( "));
2298  SVN_ERR(write_tuple_revision(conn, pool, rev));
2299  SVN_ERR(write_tuple_revision(conn, pool, low_water_mark));
2300  SVN_ERR(write_tuple_boolean(conn, pool, send_deltas));
2301  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2302
2303  return SVN_NO_ERROR;
2304}
2305
2306svn_error_t *
2307svn_ra_svn__write_cmd_replay_range(svn_ra_svn_conn_t *conn,
2308                                   apr_pool_t *pool,
2309                                   svn_revnum_t start_revision,
2310                                   svn_revnum_t end_revision,
2311                                   svn_revnum_t low_water_mark,
2312                                   svn_boolean_t send_deltas)
2313{
2314  SVN_ERR(writebuf_write_literal(conn, pool, "( replay-range ( "));
2315  SVN_ERR(write_tuple_revision(conn, pool, start_revision));
2316  SVN_ERR(write_tuple_revision(conn, pool, end_revision));
2317  SVN_ERR(write_tuple_revision(conn, pool, low_water_mark));
2318  SVN_ERR(write_tuple_boolean(conn, pool, send_deltas));
2319  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2320
2321  return SVN_NO_ERROR;
2322}
2323
2324svn_error_t *
2325svn_ra_svn__write_cmd_get_deleted_rev(svn_ra_svn_conn_t *conn,
2326                                      apr_pool_t *pool,
2327                                      const char *path,
2328                                      svn_revnum_t peg_revision,
2329                                      svn_revnum_t end_revision)
2330{
2331  SVN_ERR(writebuf_write_literal(conn, pool, "( get-deleted-rev ( "));
2332  SVN_ERR(write_tuple_cstring(conn, pool, path));
2333  SVN_ERR(write_tuple_revision(conn, pool, peg_revision));
2334  SVN_ERR(write_tuple_revision(conn, pool, end_revision));
2335  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2336
2337  return SVN_NO_ERROR;
2338}
2339
2340svn_error_t *
2341svn_ra_svn__write_cmd_get_iprops(svn_ra_svn_conn_t *conn,
2342                                 apr_pool_t *pool,
2343                                 const char *path,
2344                                 svn_revnum_t revision)
2345{
2346  SVN_ERR(writebuf_write_literal(conn, pool, "( get-iprops ( "));
2347  SVN_ERR(write_tuple_cstring(conn, pool, path));
2348  SVN_ERR(write_tuple_start_list(conn, pool));
2349  SVN_ERR(write_tuple_revision_opt(conn, pool, revision));
2350  SVN_ERR(write_tuple_end_list(conn, pool));
2351  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2352
2353  return SVN_NO_ERROR;
2354}
2355
2356svn_error_t *
2357svn_ra_svn__write_cmd_finish_replay(svn_ra_svn_conn_t *conn,
2358                                    apr_pool_t *pool)
2359{
2360  return writebuf_write_literal(conn, pool, "( finish-replay ( ) ) ");
2361}
2362
2363svn_error_t *svn_ra_svn__write_cmd_response(svn_ra_svn_conn_t *conn,
2364                                            apr_pool_t *pool,
2365                                            const char *fmt, ...)
2366{
2367  va_list ap;
2368  svn_error_t *err;
2369
2370  SVN_ERR(writebuf_write_literal(conn, pool, "( success "));
2371  va_start(ap, fmt);
2372  err = vwrite_tuple(conn, pool, fmt, &ap);
2373  va_end(ap);
2374  return err ? svn_error_trace(err) : svn_ra_svn__end_list(conn, pool);
2375}
2376
2377svn_error_t *svn_ra_svn__write_cmd_failure(svn_ra_svn_conn_t *conn,
2378                                           apr_pool_t *pool,
2379                                           const svn_error_t *err)
2380{
2381  char buffer[128];
2382  SVN_ERR(writebuf_write_literal(conn, pool, "( failure ( "));
2383  for (; err; err = err->child)
2384    {
2385      const char *msg;
2386
2387#ifdef SVN_ERR__TRACING
2388      if (svn_error__is_tracing_link(err))
2389        msg = err->message;
2390      else
2391#endif
2392        msg = svn_err_best_message(err, buffer, sizeof(buffer));
2393
2394      /* The message string should have been optional, but we can't
2395         easily change that, so marshal nonexistent messages as "". */
2396      SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "nccn",
2397                                      (apr_uint64_t) err->apr_err,
2398                                      msg ? msg : "",
2399                                      err->file ? err->file : "",
2400                                      (apr_uint64_t) err->line));
2401    }
2402  return writebuf_write_literal(conn, pool, ") ) ");
2403}
2404
2405svn_error_t *
2406svn_ra_svn__write_data_log_changed_path(svn_ra_svn_conn_t *conn,
2407                                        apr_pool_t *pool,
2408                                        const char *path,
2409                                        char action,
2410                                        const char *copyfrom_path,
2411                                        svn_revnum_t copyfrom_rev,
2412                                        svn_node_kind_t node_kind,
2413                                        svn_boolean_t text_modified,
2414                                        svn_boolean_t props_modified)
2415{
2416  SVN_ERR(write_tuple_start_list(conn, pool));
2417
2418  SVN_ERR(write_tuple_cstring(conn, pool, path));
2419  SVN_ERR(writebuf_writechar(conn, pool, action));
2420  SVN_ERR(writebuf_writechar(conn, pool, ' '));
2421  SVN_ERR(write_tuple_start_list(conn, pool));
2422  SVN_ERR(write_tuple_cstring_opt(conn, pool, copyfrom_path));
2423  SVN_ERR(write_tuple_revision_opt(conn, pool, copyfrom_rev));
2424  SVN_ERR(write_tuple_end_list(conn, pool));
2425  SVN_ERR(write_tuple_start_list(conn, pool));
2426  SVN_ERR(write_tuple_cstring(conn, pool, svn_node_kind_to_word(node_kind)));
2427  SVN_ERR(write_tuple_boolean(conn, pool, text_modified));
2428  SVN_ERR(write_tuple_boolean(conn, pool, props_modified));
2429
2430  return writebuf_write_literal(conn, pool, ") ) ");
2431}
2432
2433svn_error_t *
2434svn_ra_svn__write_data_log_entry(svn_ra_svn_conn_t *conn,
2435                                 apr_pool_t *pool,
2436                                 svn_revnum_t revision,
2437                                 const svn_string_t *author,
2438                                 const svn_string_t *date,
2439                                 const svn_string_t *message,
2440                                 svn_boolean_t has_children,
2441                                 svn_boolean_t invalid_revnum,
2442                                 unsigned revprop_count)
2443{
2444  SVN_ERR(write_tuple_revision(conn, pool, revision));
2445  SVN_ERR(write_tuple_start_list(conn, pool));
2446  SVN_ERR(write_tuple_string_opt(conn, pool, author));
2447  SVN_ERR(write_tuple_end_list(conn, pool));
2448  SVN_ERR(write_tuple_start_list(conn, pool));
2449  SVN_ERR(write_tuple_string_opt(conn, pool, date));
2450  SVN_ERR(write_tuple_end_list(conn, pool));
2451  SVN_ERR(write_tuple_start_list(conn, pool));
2452  SVN_ERR(write_tuple_string_opt(conn, pool, message));
2453  SVN_ERR(write_tuple_end_list(conn, pool));
2454  SVN_ERR(write_tuple_boolean(conn, pool, has_children));
2455  SVN_ERR(write_tuple_boolean(conn, pool, invalid_revnum));
2456  SVN_ERR(svn_ra_svn__write_number(conn, pool, revprop_count));
2457
2458  return SVN_NO_ERROR;
2459}
2460
2461/* If condition COND is not met, return a "malformed network data" error.
2462 */
2463#define CHECK_PROTOCOL_COND(cond)\
2464  if (!(cond)) \
2465    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, \
2466                            _("Malformed network data"));
2467
2468/* In *RESULT, return the SVN-style string at index IDX in tuple ITEMS.
2469 */
2470static svn_error_t *
2471svn_ra_svn__read_string(const apr_array_header_t *items,
2472                        int idx,
2473                        svn_string_t **result)
2474{
2475  svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
2476  CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING);
2477  *result = elt->u.string;
2478
2479  return SVN_NO_ERROR;
2480}
2481
2482/* In *RESULT, return the C-style string at index IDX in tuple ITEMS.
2483 */
2484static svn_error_t *
2485svn_ra_svn__read_cstring(const apr_array_header_t *items,
2486                         int idx,
2487                         const char **result)
2488{
2489  svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
2490  CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING);
2491  *result = elt->u.string->data;
2492
2493  return SVN_NO_ERROR;
2494}
2495
2496/* In *RESULT, return the word at index IDX in tuple ITEMS.
2497 */
2498static svn_error_t *
2499svn_ra_svn__read_word(const apr_array_header_t *items,
2500                      int idx,
2501                      const char **result)
2502{
2503  svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
2504  CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD);
2505  *result = elt->u.word;
2506
2507  return SVN_NO_ERROR;
2508}
2509
2510/* In *RESULT, return the revision at index IDX in tuple ITEMS.
2511 */
2512static svn_error_t *
2513svn_ra_svn__read_revision(const apr_array_header_t *items,
2514                          int idx,
2515                          svn_revnum_t *result)
2516{
2517  svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
2518  CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_NUMBER);
2519  *result = (svn_revnum_t)elt->u.number;
2520
2521  return SVN_NO_ERROR;
2522}
2523
2524/* In *RESULT, return the boolean at index IDX in tuple ITEMS.
2525 */
2526static svn_error_t *
2527svn_ra_svn__read_boolean(const apr_array_header_t *items,
2528                         int idx,
2529                         apr_uint64_t *result)
2530{
2531  svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
2532  CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD);
2533  if (elt->u.word[0] == 't' && strcmp(elt->u.word, "true") == 0)
2534    *result = TRUE;
2535  else if (strcmp(elt->u.word, "false") == 0)
2536    *result = FALSE;
2537  else
2538    CHECK_PROTOCOL_COND(FALSE);
2539
2540  return SVN_NO_ERROR;
2541}
2542
2543/* In *RESULT, return the tuple at index IDX in tuple ITEMS.
2544 */
2545static svn_error_t *
2546svn_ra_svn__read_list(const apr_array_header_t *items,
2547                      int idx,
2548                      const apr_array_header_t **result)
2549{
2550  svn_ra_svn_item_t *elt  = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
2551  CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_LIST);
2552
2553  *result = elt->u.list;
2554  return SVN_NO_ERROR;
2555}
2556
2557/* Verify the tuple ITEMS contains at least MIN and at most MAX elements.
2558 */
2559static svn_error_t *
2560svn_ra_svn__read_check_array_size(const apr_array_header_t *items,
2561                                  int min,
2562                                  int max)
2563{
2564  CHECK_PROTOCOL_COND(items->nelts >= min && items->nelts <= max);
2565  return SVN_NO_ERROR;
2566}
2567
2568svn_error_t *
2569svn_ra_svn__read_data_log_changed_entry(const apr_array_header_t *items,
2570                                        svn_string_t **cpath,
2571                                        const char **action,
2572                                        const char **copy_path,
2573                                        svn_revnum_t *copy_rev,
2574                                        const char **kind_str,
2575                                        apr_uint64_t *text_mods,
2576                                        apr_uint64_t *prop_mods)
2577{
2578  const apr_array_header_t *sub_items;
2579
2580  /* initialize optional values */
2581  *copy_path = NULL;
2582  *copy_rev = SVN_INVALID_REVNUM;
2583  *kind_str = NULL;
2584  *text_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER;
2585  *prop_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER;
2586
2587  /* top-level elements (mandatory) */
2588  SVN_ERR(svn_ra_svn__read_check_array_size(items, 3, INT_MAX));
2589  SVN_ERR(svn_ra_svn__read_string(items, 0, cpath));
2590  SVN_ERR(svn_ra_svn__read_word(items, 1, action));
2591
2592  /* first sub-structure (mandatory) */
2593  SVN_ERR(svn_ra_svn__read_list(items, 2, &sub_items));
2594  if (sub_items->nelts)
2595    {
2596      SVN_ERR(svn_ra_svn__read_check_array_size(sub_items, 2, 2));
2597      SVN_ERR(svn_ra_svn__read_cstring(sub_items, 0, copy_path));
2598      SVN_ERR(svn_ra_svn__read_revision(sub_items, 1, copy_rev));
2599    }
2600
2601  /* second sub-structure (optional) */
2602  if (items->nelts >= 4)
2603    {
2604      SVN_ERR(svn_ra_svn__read_list(items, 3, &sub_items));
2605      switch (MIN(3, sub_items->nelts))
2606        {
2607          case 3 : SVN_ERR(svn_ra_svn__read_boolean(sub_items, 2, prop_mods));
2608          case 2 : SVN_ERR(svn_ra_svn__read_boolean(sub_items, 1, text_mods));
2609          case 1 : SVN_ERR(svn_ra_svn__read_cstring(sub_items, 0, kind_str));
2610          default: break;
2611        }
2612    }
2613
2614  return SVN_NO_ERROR;
2615}
2616