marshal.c revision 299742
1/*
2 * marshal.c :  Marshalling routines for Subversion protocol
3 *
4 * ====================================================================
5 *    Licensed to the Apache Software Foundation (ASF) under one
6 *    or more contributor license agreements.  See the NOTICE file
7 *    distributed with this work for additional information
8 *    regarding copyright ownership.  The ASF licenses this file
9 *    to you under the Apache License, Version 2.0 (the
10 *    "License"); you may not use this file except in compliance
11 *    with the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 *    Unless required by applicable law or agreed to in writing,
16 *    software distributed under the License is distributed on an
17 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 *    KIND, either express or implied.  See the License for the
19 *    specific language governing permissions and limitations
20 *    under the License.
21 * ====================================================================
22 */
23
24
25
26#include <assert.h>
27#include <stdlib.h>
28
29#define APR_WANT_STRFUNC
30#include <apr_want.h>
31#include <apr_general.h>
32#include <apr_lib.h>
33#include <apr_strings.h>
34
35#include "svn_hash.h"
36#include "svn_types.h"
37#include "svn_string.h"
38#include "svn_error.h"
39#include "svn_pools.h"
40#include "svn_ra_svn.h"
41#include "svn_private_config.h"
42#include "svn_ctype.h"
43#include "svn_sorts.h"
44#include "svn_time.h"
45
46#include "ra_svn.h"
47
48#include "private/svn_string_private.h"
49#include "private/svn_dep_compat.h"
50#include "private/svn_error_private.h"
51#include "private/svn_subr_private.h"
52
53#define svn_iswhitespace(c) ((c) == ' ' || (c) == '\n')
54
55/* If we receive data that *claims* to be followed by a very long string,
56 * we should not trust that claim right away. But everything up to 1 MB
57 * should be too small to be instrumental for a DOS attack. */
58
59#define SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD (0x100000)
60
61/* We don't use "words" longer than this in our protocol.  The longest word
62 * we are currently using is only about 16 chars long but we leave room for
63 * longer future capability and command names.
64 */
65#define MAX_WORD_LENGTH 31
66
67/* The generic parsers will use the following value to limit the recursion
68 * depth to some reasonable value.  The current protocol implementation
69 * actually uses only maximum item nesting level of around 5.  So, there is
70 * plenty of headroom here.
71 */
72#define ITEM_NESTING_LIMIT 64
73
74/* Return the APR socket timeout to be used for the connection depending
75 * on whether there is a blockage handler or zero copy has been activated. */
76static apr_interval_time_t
77get_timeout(svn_ra_svn_conn_t *conn)
78{
79  return conn->block_handler ? 0 : -1;
80}
81
82/* --- CONNECTION INITIALIZATION --- */
83
84svn_ra_svn_conn_t *svn_ra_svn_create_conn4(apr_socket_t *sock,
85                                           svn_stream_t *in_stream,
86                                           svn_stream_t *out_stream,
87                                           int compression_level,
88                                           apr_size_t zero_copy_limit,
89                                           apr_size_t error_check_interval,
90                                           apr_pool_t *result_pool)
91{
92  svn_ra_svn_conn_t *conn;
93  void *mem = apr_palloc(result_pool, sizeof(*conn) + SVN_RA_SVN__PAGE_SIZE);
94  conn = (void*)APR_ALIGN((apr_uintptr_t)mem, SVN_RA_SVN__PAGE_SIZE);
95
96  assert((sock && !in_stream && !out_stream)
97         || (!sock && in_stream && out_stream));
98#ifdef SVN_HAVE_SASL
99  conn->sock = sock;
100  conn->encrypted = FALSE;
101#endif
102  conn->session = NULL;
103  conn->read_ptr = conn->read_buf;
104  conn->read_end = conn->read_buf;
105  conn->write_pos = 0;
106  conn->written_since_error_check = 0;
107  conn->error_check_interval = error_check_interval;
108  conn->may_check_for_error = error_check_interval == 0;
109  conn->block_handler = NULL;
110  conn->block_baton = NULL;
111  conn->capabilities = apr_hash_make(result_pool);
112  conn->compression_level = compression_level;
113  conn->zero_copy_limit = zero_copy_limit;
114  conn->pool = result_pool;
115
116  if (sock != NULL)
117    {
118      apr_sockaddr_t *sa;
119      conn->stream = svn_ra_svn__stream_from_sock(sock, result_pool);
120      if (!(apr_socket_addr_get(&sa, APR_REMOTE, sock) == APR_SUCCESS
121            && apr_sockaddr_ip_get(&conn->remote_ip, sa) == APR_SUCCESS))
122        conn->remote_ip = NULL;
123      svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn));
124    }
125  else
126    {
127      conn->stream = svn_ra_svn__stream_from_streams(in_stream, out_stream,
128                                                     result_pool);
129      conn->remote_ip = NULL;
130    }
131
132  return conn;
133}
134
135svn_error_t *svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn,
136                                         const apr_array_header_t *list)
137{
138  int i;
139  svn_ra_svn_item_t *item;
140  const char *word;
141
142  for (i = 0; i < list->nelts; i++)
143    {
144      item = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
145      if (item->kind != SVN_RA_SVN_WORD)
146        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
147                                _("Capability entry is not a word"));
148      word = apr_pstrdup(conn->pool, item->u.word);
149      svn_hash_sets(conn->capabilities, word, word);
150    }
151  return SVN_NO_ERROR;
152}
153
154apr_pool_t *
155svn_ra_svn__get_pool(svn_ra_svn_conn_t *conn)
156{
157  return conn->pool;
158}
159
160svn_error_t *
161svn_ra_svn__set_shim_callbacks(svn_ra_svn_conn_t *conn,
162                               svn_delta_shim_callbacks_t *shim_callbacks)
163{
164  conn->shim_callbacks = shim_callbacks;
165  return SVN_NO_ERROR;
166}
167
168svn_boolean_t svn_ra_svn_has_capability(svn_ra_svn_conn_t *conn,
169                                        const char *capability)
170{
171  return (svn_hash_gets(conn->capabilities, capability) != NULL);
172}
173
174int
175svn_ra_svn_compression_level(svn_ra_svn_conn_t *conn)
176{
177  return conn->compression_level;
178}
179
180apr_size_t
181svn_ra_svn_zero_copy_limit(svn_ra_svn_conn_t *conn)
182{
183  return conn->zero_copy_limit;
184}
185
186const char *svn_ra_svn_conn_remote_host(svn_ra_svn_conn_t *conn)
187{
188  return conn->remote_ip;
189}
190
191void
192svn_ra_svn__set_block_handler(svn_ra_svn_conn_t *conn,
193                              ra_svn_block_handler_t handler,
194                              void *baton)
195{
196  conn->block_handler = handler;
197  conn->block_baton = baton;
198  svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn));
199}
200
201svn_error_t *svn_ra_svn__data_available(svn_ra_svn_conn_t *conn,
202                                       svn_boolean_t *data_available)
203{
204  return svn_ra_svn__stream_data_available(conn->stream, data_available);
205}
206
207/* --- WRITE BUFFER MANAGEMENT --- */
208
209/* Write data to socket or output file as appropriate. */
210static svn_error_t *writebuf_output(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
211                                    const char *data, apr_size_t len)
212{
213  const char *end = data + len;
214  apr_size_t count;
215  apr_pool_t *subpool = NULL;
216  svn_ra_svn__session_baton_t *session = conn->session;
217
218  while (data < end)
219    {
220      count = end - data;
221
222      if (session && session->callbacks && session->callbacks->cancel_func)
223        SVN_ERR((session->callbacks->cancel_func)(session->callbacks_baton));
224
225      SVN_ERR(svn_ra_svn__stream_write(conn->stream, data, &count));
226      if (count == 0)
227        {
228          if (!subpool)
229            subpool = svn_pool_create(pool);
230          else
231            svn_pool_clear(subpool);
232          SVN_ERR(conn->block_handler(conn, subpool, conn->block_baton));
233        }
234      data += count;
235
236      if (session)
237        {
238          const svn_ra_callbacks2_t *cb = session->callbacks;
239          session->bytes_written += count;
240
241          if (cb && cb->progress_func)
242            (cb->progress_func)(session->bytes_written + session->bytes_read,
243                                -1, cb->progress_baton, subpool);
244        }
245    }
246
247  conn->written_since_error_check += len;
248  conn->may_check_for_error
249    = conn->written_since_error_check >= conn->error_check_interval;
250
251  if (subpool)
252    svn_pool_destroy(subpool);
253  return SVN_NO_ERROR;
254}
255
256/* Write data from the write buffer out to the socket. */
257static svn_error_t *writebuf_flush(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
258{
259  apr_size_t write_pos = conn->write_pos;
260
261  /* Clear conn->write_pos first in case the block handler does a read. */
262  conn->write_pos = 0;
263  SVN_ERR(writebuf_output(conn, pool, conn->write_buf, write_pos));
264  return SVN_NO_ERROR;
265}
266
267static svn_error_t *writebuf_write(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
268                                   const char *data, apr_size_t len)
269{
270  /* data >= 8k is sent immediately */
271  if (len >= sizeof(conn->write_buf) / 2)
272    {
273      if (conn->write_pos > 0)
274        SVN_ERR(writebuf_flush(conn, pool));
275
276      return writebuf_output(conn, pool, data, len);
277    }
278
279  /* ensure room for the data to add */
280  if (conn->write_pos + len > sizeof(conn->write_buf))
281    SVN_ERR(writebuf_flush(conn, pool));
282
283  /* buffer the new data block as well */
284  memcpy(conn->write_buf + conn->write_pos, data, len);
285  conn->write_pos += len;
286
287  return SVN_NO_ERROR;
288}
289
290/* Write STRING_LITERAL, which is a string literal argument.
291
292   Note: The purpose of the empty string "" in the macro definition is to
293   assert that STRING_LITERAL is in fact a string literal. Otherwise, the
294   string concatenation attempt should produce a compile-time error. */
295#define writebuf_write_literal(conn, pool, string_literal) \
296    writebuf_write(conn, pool, string_literal, sizeof(string_literal "") - 1)
297
298static APR_INLINE svn_error_t *
299writebuf_writechar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char data)
300{
301  if (conn->write_pos < sizeof(conn->write_buf))
302  {
303    conn->write_buf[conn->write_pos] = data;
304    conn->write_pos++;
305
306    return SVN_NO_ERROR;
307  }
308  else
309  {
310    char temp = data;
311    return writebuf_write(conn, pool, &temp, 1);
312  }
313}
314
315/* --- READ BUFFER MANAGEMENT --- */
316
317/* Read bytes into DATA until either the read buffer is empty or
318 * we reach END. */
319static char *readbuf_drain(svn_ra_svn_conn_t *conn, char *data, char *end)
320{
321  apr_ssize_t buflen, copylen;
322
323  buflen = conn->read_end - conn->read_ptr;
324  copylen = (buflen < end - data) ? buflen : end - data;
325  memcpy(data, conn->read_ptr, copylen);
326  conn->read_ptr += copylen;
327  return data + copylen;
328}
329
330/* Read data from socket or input file as appropriate. */
331static svn_error_t *readbuf_input(svn_ra_svn_conn_t *conn, char *data,
332                                  apr_size_t *len, apr_pool_t *pool)
333{
334  svn_ra_svn__session_baton_t *session = conn->session;
335
336  if (session && session->callbacks && session->callbacks->cancel_func)
337    SVN_ERR((session->callbacks->cancel_func)(session->callbacks_baton));
338
339  SVN_ERR(svn_ra_svn__stream_read(conn->stream, data, len));
340  if (*len == 0)
341    return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL);
342
343  if (session)
344    {
345      const svn_ra_callbacks2_t *cb = session->callbacks;
346      session->bytes_read += *len;
347
348      if (cb && cb->progress_func)
349        (cb->progress_func)(session->bytes_read + session->bytes_written,
350                            -1, cb->progress_baton, pool);
351    }
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