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_time.h"
44
45#include "ra_svn.h"
46
47#include "private/svn_string_private.h"
48#include "private/svn_dep_compat.h"
49#include "private/svn_error_private.h"
50
51#define svn_iswhitespace(c) ((c) == ' ' || (c) == '\n')
52
53/* If we receive data that *claims* to be followed by a very long string,
54 * we should not trust that claim right away. But everything up to 1 MB
55 * should be too small to be instrumental for a DOS attack. */
56
57#define SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD (0x100000)
58
59/* Return the APR socket timeout to be used for the connection depending
60 * on whether there is a blockage handler or zero copy has been activated. */
61static apr_interval_time_t
62get_timeout(svn_ra_svn_conn_t *conn)
63{
64  return conn->block_handler ? 0 : -1;
65}
66
67/* --- CONNECTION INITIALIZATION --- */
68
69svn_ra_svn_conn_t *svn_ra_svn_create_conn3(apr_socket_t *sock,
70                                           apr_file_t *in_file,
71                                           apr_file_t *out_file,
72                                           int compression_level,
73                                           apr_size_t zero_copy_limit,
74                                           apr_size_t error_check_interval,
75                                           apr_pool_t *pool)
76{
77  svn_ra_svn_conn_t *conn;
78  void *mem = apr_palloc(pool, sizeof(*conn) + SVN_RA_SVN__PAGE_SIZE);
79  conn = (void*)APR_ALIGN((apr_uintptr_t)mem, SVN_RA_SVN__PAGE_SIZE);
80
81  assert((sock && !in_file && !out_file) || (!sock && in_file && out_file));
82#ifdef SVN_HAVE_SASL
83  conn->sock = sock;
84  conn->encrypted = FALSE;
85#endif
86  conn->session = NULL;
87  conn->read_ptr = conn->read_buf;
88  conn->read_end = conn->read_buf;
89  conn->write_pos = 0;
90  conn->written_since_error_check = 0;
91  conn->error_check_interval = error_check_interval;
92  conn->may_check_for_error = error_check_interval == 0;
93  conn->block_handler = NULL;
94  conn->block_baton = NULL;
95  conn->capabilities = apr_hash_make(pool);
96  conn->compression_level = compression_level;
97  conn->zero_copy_limit = zero_copy_limit;
98  conn->pool = pool;
99
100  if (sock != NULL)
101    {
102      apr_sockaddr_t *sa;
103      conn->stream = svn_ra_svn__stream_from_sock(sock, pool);
104      if (!(apr_socket_addr_get(&sa, APR_REMOTE, sock) == APR_SUCCESS
105            && apr_sockaddr_ip_get(&conn->remote_ip, sa) == APR_SUCCESS))
106        conn->remote_ip = NULL;
107      svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn));
108    }
109  else
110    {
111      conn->stream = svn_ra_svn__stream_from_files(in_file, out_file, pool);
112      conn->remote_ip = NULL;
113    }
114
115  return conn;
116}
117
118svn_ra_svn_conn_t *svn_ra_svn_create_conn2(apr_socket_t *sock,
119                                           apr_file_t *in_file,
120                                           apr_file_t *out_file,
121                                           int compression_level,
122                                           apr_pool_t *pool)
123{
124  return svn_ra_svn_create_conn3(sock, in_file, out_file,
125                                 compression_level, 0, 0, pool);
126}
127
128/* backward-compatible implementation using the default compression level */
129svn_ra_svn_conn_t *svn_ra_svn_create_conn(apr_socket_t *sock,
130                                          apr_file_t *in_file,
131                                          apr_file_t *out_file,
132                                          apr_pool_t *pool)
133{
134  return svn_ra_svn_create_conn3(sock, in_file, out_file,
135                                 SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, 0, 0,
136                                 pool);
137}
138
139svn_error_t *svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn,
140                                         const apr_array_header_t *list)
141{
142  int i;
143  svn_ra_svn_item_t *item;
144  const char *word;
145
146  for (i = 0; i < list->nelts; i++)
147    {
148      item = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
149      if (item->kind != SVN_RA_SVN_WORD)
150        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
151                                _("Capability entry is not a word"));
152      word = apr_pstrdup(conn->pool, item->u.word);
153      svn_hash_sets(conn->capabilities, word, word);
154    }
155  return SVN_NO_ERROR;
156}
157
158svn_error_t *
159svn_ra_svn__set_shim_callbacks(svn_ra_svn_conn_t *conn,
160                               svn_delta_shim_callbacks_t *shim_callbacks)
161{
162  conn->shim_callbacks = shim_callbacks;
163  return SVN_NO_ERROR;
164}
165
166svn_boolean_t svn_ra_svn_has_capability(svn_ra_svn_conn_t *conn,
167                                        const char *capability)
168{
169  return (svn_hash_gets(conn->capabilities, capability) != NULL);
170}
171
172int
173svn_ra_svn_compression_level(svn_ra_svn_conn_t *conn)
174{
175  return conn->compression_level;
176}
177
178apr_size_t
179svn_ra_svn_zero_copy_limit(svn_ra_svn_conn_t *conn)
180{
181  return conn->zero_copy_limit;
182}
183
184const char *svn_ra_svn_conn_remote_host(svn_ra_svn_conn_t *conn)
185{
186  return conn->remote_ip;
187}
188
189void
190svn_ra_svn__set_block_handler(svn_ra_svn_conn_t *conn,
191                              ra_svn_block_handler_t handler,
192                              void *baton)
193{
194  conn->block_handler = handler;
195  conn->block_baton = baton;
196  svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn));
197}
198
199svn_boolean_t svn_ra_svn__input_waiting(svn_ra_svn_conn_t *conn,
200                                        apr_pool_t *pool)
201{
202  return svn_ra_svn__stream_pending(conn->stream);
203}
204
205/* --- WRITE BUFFER MANAGEMENT --- */
206
207/* Write data to socket or output file as appropriate. */
208static svn_error_t *writebuf_output(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
209                                    const char *data, apr_size_t len)
210{
211  const char *end = data + len;
212  apr_size_t count;
213  apr_pool_t *subpool = NULL;
214  svn_ra_svn__session_baton_t *session = conn->session;
215
216  while (data < end)
217    {
218      count = end - data;
219
220      if (session && session->callbacks && session->callbacks->cancel_func)
221        SVN_ERR((session->callbacks->cancel_func)(session->callbacks_baton));
222
223      SVN_ERR(svn_ra_svn__stream_write(conn->stream, data, &count));
224      if (count == 0)
225        {
226          if (!subpool)
227            subpool = svn_pool_create(pool);
228          else
229            svn_pool_clear(subpool);
230          SVN_ERR(conn->block_handler(conn, subpool, conn->block_baton));
231        }
232      data += count;
233
234      if (session)
235        {
236          const svn_ra_callbacks2_t *cb = session->callbacks;
237          session->bytes_written += count;
238
239          if (cb && cb->progress_func)
240            (cb->progress_func)(session->bytes_written + session->bytes_read,
241                                -1, cb->progress_baton, subpool);
242        }
243    }
244
245  conn->written_since_error_check += len;
246  conn->may_check_for_error
247    = conn->written_since_error_check >= conn->error_check_interval;
248
249  if (subpool)
250    svn_pool_destroy(subpool);
251  return SVN_NO_ERROR;
252}
253
254/* Write data from the write buffer out to the socket. */
255static svn_error_t *writebuf_flush(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
256{
257  apr_size_t write_pos = conn->write_pos;
258
259  /* Clear conn->write_pos first in case the block handler does a read. */
260  conn->write_pos = 0;
261  SVN_ERR(writebuf_output(conn, pool, conn->write_buf, write_pos));
262  return SVN_NO_ERROR;
263}
264
265static svn_error_t *writebuf_write(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
266                                   const char *data, apr_size_t len)
267{
268  /* data >= 8k is sent immediately */
269  if (len >= sizeof(conn->write_buf) / 2)
270    {
271      if (conn->write_pos > 0)
272        SVN_ERR(writebuf_flush(conn, pool));
273
274      return writebuf_output(conn, pool, data, len);
275    }
276
277  /* ensure room for the data to add */
278  if (conn->write_pos + len > sizeof(conn->write_buf))
279    SVN_ERR(writebuf_flush(conn, pool));
280
281  /* buffer the new data block as well */
282  memcpy(conn->write_buf + conn->write_pos, data, len);
283  conn->write_pos += len;
284
285  return SVN_NO_ERROR;
286}
287
288static svn_error_t *
289writebuf_write_short_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
290                            const char *data, apr_size_t len)
291{
292  apr_size_t left = sizeof(conn->write_buf) - conn->write_pos;
293  if (len <= left)
294  {
295    memcpy(conn->write_buf + conn->write_pos, data, len);
296    conn->write_pos += len;
297    return SVN_NO_ERROR;
298  }
299  else
300    return writebuf_write(conn, pool, data, len);
301}
302
303static APR_INLINE svn_error_t *
304writebuf_writechar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char data)
305{
306  if (conn->write_pos < sizeof(conn->write_buf))
307  {
308    conn->write_buf[conn->write_pos] = data;
309    conn->write_pos++;
310
311    return SVN_NO_ERROR;
312  }
313  else
314  {
315    char temp = data;
316    return writebuf_write(conn, pool, &temp, 1);
317  }
318}
319
320/* --- READ BUFFER MANAGEMENT --- */
321
322/* Read bytes into DATA until either the read buffer is empty or
323 * we reach END. */
324static char *readbuf_drain(svn_ra_svn_conn_t *conn, char *data, char *end)
325{
326  apr_ssize_t buflen, copylen;
327
328  buflen = conn->read_end - conn->read_ptr;
329  copylen = (buflen < end - data) ? buflen : end - data;
330  memcpy(data, conn->read_ptr, copylen);
331  conn->read_ptr += copylen;
332  return data + copylen;
333}
334
335/* Read data from socket or input file as appropriate. */
336static svn_error_t *readbuf_input(svn_ra_svn_conn_t *conn, char *data,
337                                  apr_size_t *len, apr_pool_t *pool)
338{
339  svn_ra_svn__session_baton_t *session = conn->session;
340
341  if (session && session->callbacks && session->callbacks->cancel_func)
342    SVN_ERR((session->callbacks->cancel_func)(session->callbacks_baton));
343
344  SVN_ERR(svn_ra_svn__stream_read(conn->stream, data, len));
345  if (*len == 0)
346    return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL);
347
348  if (session)
349    {
350      const svn_ra_callbacks2_t *cb = session->callbacks;
351      session->bytes_read += *len;
352
353      if (cb && cb->progress_func)
354        (cb->progress_func)(session->bytes_read + session->bytes_written,
355                            -1, cb->progress_baton, pool);
356    }
357
358  return SVN_NO_ERROR;
359}
360
361/* Treat the next LEN input bytes from CONN as "read" */
362static svn_error_t *readbuf_skip(svn_ra_svn_conn_t *conn, apr_uint64_t len)
363{
364  do
365  {
366    apr_size_t buflen = conn->read_end - conn->read_ptr;
367    apr_size_t copylen = (buflen < len) ? buflen : (apr_size_t)len;
368    conn->read_ptr += copylen;
369    len -= copylen;
370    if (len == 0)
371      break;
372
373    buflen = sizeof(conn->read_buf);
374    SVN_ERR(svn_ra_svn__stream_read(conn->stream, conn->read_buf, &buflen));
375    if (buflen == 0)
376      return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL);
377
378    conn->read_end = conn->read_buf + buflen;
379    conn->read_ptr = conn->read_buf;
380  }
381  while (len > 0);
382
383  return SVN_NO_ERROR;
384}
385
386/* Read data from the socket into the read buffer, which must be empty. */
387static svn_error_t *readbuf_fill(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
388{
389  apr_size_t len;
390
391  SVN_ERR_ASSERT(conn->read_ptr == conn->read_end);
392  SVN_ERR(writebuf_flush(conn, pool));
393  len = sizeof(conn->read_buf);
394  SVN_ERR(readbuf_input(conn, conn->read_buf, &len, pool));
395  conn->read_ptr = conn->read_buf;
396  conn->read_end = conn->read_buf + len;
397  return SVN_NO_ERROR;
398}
399
400static APR_INLINE svn_error_t *
401readbuf_getchar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char *result)
402{
403  if (conn->read_ptr == conn->read_end)
404    SVN_ERR(readbuf_fill(conn, pool));
405  *result = *conn->read_ptr++;
406  return SVN_NO_ERROR;
407}
408
409static svn_error_t *readbuf_getchar_skip_whitespace(svn_ra_svn_conn_t *conn,
410                                                    apr_pool_t *pool,
411                                                    char *result)
412{
413  do
414    SVN_ERR(readbuf_getchar(conn, pool, result));
415  while (svn_iswhitespace(*result));
416  return SVN_NO_ERROR;
417}
418
419/* Read the next LEN bytes from CONN and copy them to *DATA. */
420static svn_error_t *readbuf_read(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
421                                 char *data, apr_size_t len)
422{
423  char *end = data + len;
424  apr_size_t count;
425
426  /* Copy in an appropriate amount of data from the buffer. */
427  data = readbuf_drain(conn, data, end);
428
429  /* Read large chunks directly into buffer. */
430  while (end - data > (apr_ssize_t)sizeof(conn->read_buf))
431    {
432      SVN_ERR(writebuf_flush(conn, pool));
433      count = end - data;
434      SVN_ERR(readbuf_input(conn, data, &count, pool));
435      data += count;
436    }
437
438  while (end > data)
439    {
440      /* The remaining amount to read is small; fill the buffer and
441       * copy from that. */
442      SVN_ERR(readbuf_fill(conn, pool));
443      data = readbuf_drain(conn, data, end);
444    }
445
446  return SVN_NO_ERROR;
447}
448
449static svn_error_t *readbuf_skip_leading_garbage(svn_ra_svn_conn_t *conn,
450                                                 apr_pool_t *pool)
451{
452  char buf[256];  /* Must be smaller than sizeof(conn->read_buf) - 1. */
453  const char *p, *end;
454  apr_size_t len;
455  svn_boolean_t lparen = FALSE;
456
457  SVN_ERR_ASSERT(conn->read_ptr == conn->read_end);
458  while (1)
459    {
460      /* Read some data directly from the connection input source. */
461      len = sizeof(buf);
462      SVN_ERR(readbuf_input(conn, buf, &len, pool));
463      end = buf + len;
464
465      /* Scan the data for '(' WS with a very simple state machine. */
466      for (p = buf; p < end; p++)
467        {
468          if (lparen && svn_iswhitespace(*p))
469            break;
470          else
471            lparen = (*p == '(');
472        }
473      if (p < end)
474        break;
475    }
476
477  /* p now points to the whitespace just after the left paren.  Fake
478   * up the left paren and then copy what we have into the read
479   * buffer. */
480  conn->read_buf[0] = '(';
481  memcpy(conn->read_buf + 1, p, end - p);
482  conn->read_ptr = conn->read_buf;
483  conn->read_end = conn->read_buf + 1 + (end - p);
484  return SVN_NO_ERROR;
485}
486
487/* --- WRITING DATA ITEMS --- */
488
489static svn_error_t *write_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
490                                 apr_uint64_t number, char follow)
491{
492  apr_size_t written;
493
494  /* SVN_INT64_BUFFER_SIZE includes space for a terminating NUL that
495   * svn__ui64toa will always append. */
496  if (conn->write_pos + SVN_INT64_BUFFER_SIZE >= sizeof(conn->write_buf))
497    SVN_ERR(writebuf_flush(conn, pool));
498
499  written = svn__ui64toa(conn->write_buf + conn->write_pos, number);
500  conn->write_buf[conn->write_pos + written] = follow;
501  conn->write_pos += written + 1;
502
503  return SVN_NO_ERROR;
504}
505
506svn_error_t *
507svn_ra_svn__write_number(svn_ra_svn_conn_t *conn,
508                         apr_pool_t *pool,
509                         apr_uint64_t number)
510{
511  return write_number(conn, pool, number, ' ');
512}
513
514svn_error_t *
515svn_ra_svn__write_string(svn_ra_svn_conn_t *conn,
516                         apr_pool_t *pool,
517                         const svn_string_t *str)
518{
519  if (str->len < 10)
520    {
521      SVN_ERR(writebuf_writechar(conn, pool, (char)(str->len + '0')));
522      SVN_ERR(writebuf_writechar(conn, pool, ':'));
523    }
524  else
525    SVN_ERR(write_number(conn, pool, str->len, ':'));
526
527  SVN_ERR(writebuf_write(conn, pool, str->data, str->len));
528  SVN_ERR(writebuf_writechar(conn, pool, ' '));
529  return SVN_NO_ERROR;
530}
531
532svn_error_t *
533svn_ra_svn__write_cstring(svn_ra_svn_conn_t *conn,
534                          apr_pool_t *pool,
535                          const char *s)
536{
537  apr_size_t len = strlen(s);
538
539  if (len < 10)
540    {
541      SVN_ERR(writebuf_writechar(conn, pool, (char)(len + '0')));
542      SVN_ERR(writebuf_writechar(conn, pool, ':'));
543    }
544  else
545    SVN_ERR(write_number(conn, pool, len, ':'));
546
547  SVN_ERR(writebuf_write(conn, pool, s, len));
548  SVN_ERR(writebuf_writechar(conn, pool, ' '));
549
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_short_string(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_proplist(svn_ra_svn_conn_t *conn,
566                           apr_pool_t *pool,
567                           apr_hash_t *props)
568{
569  apr_pool_t *iterpool;
570  apr_hash_index_t *hi;
571  const void *key;
572  void *val;
573  const char *propname;
574  svn_string_t *propval;
575
576  if (props)
577    {
578      iterpool = svn_pool_create(pool);
579      for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
580        {
581          svn_pool_clear(iterpool);
582          apr_hash_this(hi, &key, NULL, &val);
583          propname = key;
584          propval = val;
585          SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "cs",
586                                          propname, propval));
587        }
588      svn_pool_destroy(iterpool);
589    }
590
591  return SVN_NO_ERROR;
592}
593
594svn_error_t *
595svn_ra_svn__start_list(svn_ra_svn_conn_t *conn,
596                       apr_pool_t *pool)
597{
598  if (conn->write_pos + 2 <= sizeof(conn->write_buf))
599    {
600      conn->write_buf[conn->write_pos] = '(';
601      conn->write_buf[conn->write_pos+1] = ' ';
602      conn->write_pos += 2;
603      return SVN_NO_ERROR;
604    }
605
606  return writebuf_write(conn, pool, "( ", 2);
607}
608
609svn_error_t *
610svn_ra_svn__end_list(svn_ra_svn_conn_t *conn,
611                     apr_pool_t *pool)
612{
613  if (conn->write_pos + 2 <= sizeof(conn->write_buf))
614  {
615    conn->write_buf[conn->write_pos] = ')';
616    conn->write_buf[conn->write_pos+1] = ' ';
617    conn->write_pos += 2;
618    return SVN_NO_ERROR;
619  }
620
621  return writebuf_write(conn, pool, ") ", 2);
622}
623
624svn_error_t *
625svn_ra_svn__flush(svn_ra_svn_conn_t *conn,
626                  apr_pool_t *pool)
627{
628  SVN_ERR(writebuf_flush(conn, pool));
629  conn->may_check_for_error = TRUE;
630
631  return SVN_NO_ERROR;
632}
633
634/* --- WRITING TUPLES --- */
635
636static svn_error_t *
637vwrite_tuple_cstring(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
638{
639  const char *cstr = va_arg(*ap, const char *);
640  SVN_ERR_ASSERT(cstr);
641  return svn_ra_svn__write_cstring(conn, pool, cstr);
642}
643
644static svn_error_t *
645vwrite_tuple_cstring_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
646{
647  const char *cstr = va_arg(*ap, const char *);
648  return cstr ? svn_ra_svn__write_cstring(conn, pool, cstr) : SVN_NO_ERROR;
649}
650
651static svn_error_t *
652vwrite_tuple_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
653{
654  const svn_string_t *str = va_arg(*ap, const svn_string_t *);
655  SVN_ERR_ASSERT(str);
656  return svn_ra_svn__write_string(conn, pool, str);
657}
658
659static svn_error_t *
660vwrite_tuple_string_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
661{
662  const svn_string_t *str = va_arg(*ap, const svn_string_t *);
663  return str ? svn_ra_svn__write_string(conn, pool, str) : SVN_NO_ERROR;
664}
665
666static svn_error_t *
667vwrite_tuple_word(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
668{
669  const char *cstr = va_arg(*ap, const char *);
670  SVN_ERR_ASSERT(cstr);
671  return svn_ra_svn__write_word(conn, pool, cstr);
672}
673
674static svn_error_t *
675vwrite_tuple_word_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
676{
677  const char *cstr = va_arg(*ap, const char *);
678  return cstr ? svn_ra_svn__write_word(conn, pool, cstr) : SVN_NO_ERROR;
679}
680
681static svn_error_t *
682vwrite_tuple_revision(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
683{
684  svn_revnum_t rev = va_arg(*ap, svn_revnum_t);
685  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
686  return svn_ra_svn__write_number(conn, pool, rev);
687}
688
689static svn_error_t *
690vwrite_tuple_revision_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
691{
692  svn_revnum_t rev = va_arg(*ap, svn_revnum_t);
693  return SVN_IS_VALID_REVNUM(rev)
694       ? svn_ra_svn__write_number(conn, pool, rev)
695       : SVN_NO_ERROR;
696}
697
698static svn_error_t *
699vwrite_tuple_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
700{
701  return svn_ra_svn__write_number(conn, pool, va_arg(*ap, apr_uint64_t));
702}
703
704static svn_error_t *
705vwrite_tuple_boolean(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
706{
707  const char *cstr = va_arg(*ap, svn_boolean_t) ? "true" : "false";
708  return svn_ra_svn__write_word(conn, pool, cstr);
709}
710
711static svn_error_t *
712write_tuple_cstring(svn_ra_svn_conn_t *conn,
713                    apr_pool_t *pool,
714                    const char *cstr)
715{
716  SVN_ERR_ASSERT(cstr);
717  return svn_ra_svn__write_cstring(conn, pool, cstr);
718}
719
720static svn_error_t *
721write_tuple_cstring_opt(svn_ra_svn_conn_t *conn,
722                        apr_pool_t *pool,
723                        const char *cstr)
724{
725  return cstr ? svn_ra_svn__write_cstring(conn, pool, cstr) : SVN_NO_ERROR;
726}
727
728static svn_error_t *
729write_tuple_string(svn_ra_svn_conn_t *conn,
730                   apr_pool_t *pool,
731                   const svn_string_t *str)
732{
733  SVN_ERR_ASSERT(str);
734  return svn_ra_svn__write_string(conn, pool, str);
735}
736
737static svn_error_t *
738write_tuple_string_opt(svn_ra_svn_conn_t *conn,
739                       apr_pool_t *pool,
740                       const svn_string_t *str)
741{
742  return str ? svn_ra_svn__write_string(conn, pool, str) : SVN_NO_ERROR;
743}
744
745static svn_error_t *
746write_tuple_start_list(svn_ra_svn_conn_t *conn,
747                       apr_pool_t *pool)
748{
749  return svn_ra_svn__start_list(conn, pool);
750}
751
752static svn_error_t *
753write_tuple_end_list(svn_ra_svn_conn_t *conn,
754                     apr_pool_t *pool)
755{
756  return svn_ra_svn__end_list(conn, pool);
757}
758
759static svn_error_t *
760write_tuple_revision(svn_ra_svn_conn_t *conn,
761                     apr_pool_t *pool,
762                     svn_revnum_t rev)
763{
764  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
765  return svn_ra_svn__write_number(conn, pool, rev);
766}
767
768static svn_error_t *
769write_tuple_revision_opt(svn_ra_svn_conn_t *conn,
770                         apr_pool_t *pool,
771                         svn_revnum_t rev)
772{
773  return SVN_IS_VALID_REVNUM(rev)
774       ? svn_ra_svn__write_number(conn, pool, rev)
775       : SVN_NO_ERROR;
776}
777
778static svn_error_t *
779write_tuple_boolean(svn_ra_svn_conn_t *conn,
780                    apr_pool_t *pool,
781                    svn_boolean_t value)
782{
783  const char *cstr = value ? "true" : "false";
784  return svn_ra_svn__write_word(conn, pool, cstr);
785}
786
787static svn_error_t *
788write_tuple_depth(svn_ra_svn_conn_t *conn,
789                  apr_pool_t *pool,
790                  svn_depth_t depth)
791{
792  return svn_ra_svn__write_word(conn, pool, svn_depth_to_word(depth));
793}
794
795
796static svn_error_t *
797write_cmd_add_node(svn_ra_svn_conn_t *conn,
798                   apr_pool_t *pool,
799                   const char *path,
800                   const char *parent_token,
801                   const char *token,
802                   const char *copy_path,
803                   svn_revnum_t copy_rev)
804{
805  SVN_ERR(write_tuple_cstring(conn, pool, path));
806  SVN_ERR(write_tuple_cstring(conn, pool, parent_token));
807  SVN_ERR(write_tuple_cstring(conn, pool, token));
808  SVN_ERR(write_tuple_start_list(conn, pool));
809  SVN_ERR(write_tuple_cstring_opt(conn, pool, copy_path));
810  SVN_ERR(write_tuple_revision_opt(conn, pool, copy_rev));
811  SVN_ERR(write_tuple_end_list(conn, pool));
812
813  return SVN_NO_ERROR;
814}
815
816static svn_error_t *
817write_cmd_open_node(svn_ra_svn_conn_t *conn,
818                    apr_pool_t *pool,
819                    const char *path,
820                    const char *parent_token,
821                    const char *token,
822                    svn_revnum_t rev)
823{
824  SVN_ERR(write_tuple_cstring(conn, pool, path));
825  SVN_ERR(write_tuple_cstring(conn, pool, parent_token));
826  SVN_ERR(write_tuple_cstring(conn, pool, token));
827  SVN_ERR(write_tuple_start_list(conn, pool));
828  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
829  SVN_ERR(write_tuple_end_list(conn, pool));
830
831  return SVN_NO_ERROR;
832}
833
834static svn_error_t *
835write_cmd_change_node_prop(svn_ra_svn_conn_t *conn,
836                           apr_pool_t *pool,
837                           const char *token,
838                           const char *name,
839                           const svn_string_t *value)
840{
841  SVN_ERR(write_tuple_cstring(conn, pool, token));
842  SVN_ERR(write_tuple_cstring(conn, pool, name));
843  SVN_ERR(write_tuple_start_list(conn, pool));
844  SVN_ERR(write_tuple_string_opt(conn, pool, value));
845  SVN_ERR(write_tuple_end_list(conn, pool));
846
847  return SVN_NO_ERROR;
848}
849
850static svn_error_t *
851write_cmd_absent_node(svn_ra_svn_conn_t *conn,
852                      apr_pool_t *pool,
853                      const char *path,
854                      const char *token)
855{
856  SVN_ERR(write_tuple_cstring(conn, pool, path));
857  SVN_ERR(write_tuple_cstring(conn, pool, token));
858
859  return SVN_NO_ERROR;
860}
861
862
863
864
865static svn_error_t *vwrite_tuple(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
866                                 const char *fmt, va_list *ap)
867{
868  svn_boolean_t opt = FALSE;
869
870  if (*fmt == '!')
871    fmt++;
872  else
873    SVN_ERR(svn_ra_svn__start_list(conn, pool));
874  for (; *fmt; fmt++)
875    {
876      if (*fmt == 'c')
877        SVN_ERR(opt ? vwrite_tuple_cstring_opt(conn, pool, ap)
878                    : vwrite_tuple_cstring(conn, pool, ap));
879      else if (*fmt == 's')
880        SVN_ERR(opt ? vwrite_tuple_string_opt(conn, pool, ap)
881                    : vwrite_tuple_string(conn, pool, ap));
882      else if (*fmt == '(' && !opt)
883        SVN_ERR(write_tuple_start_list(conn, pool));
884      else if (*fmt == ')')
885        {
886          SVN_ERR(write_tuple_end_list(conn, pool));
887          opt = FALSE;
888        }
889      else if (*fmt == '?')
890        opt = TRUE;
891      else if (*fmt == 'w')
892        SVN_ERR(opt ? vwrite_tuple_word_opt(conn, pool, ap)
893                    : vwrite_tuple_word(conn, pool, ap));
894      else if (*fmt == 'r')
895        SVN_ERR(opt ? vwrite_tuple_revision_opt(conn, pool, ap)
896                    : vwrite_tuple_revision(conn, pool, ap));
897      else if (*fmt == 'n' && !opt)
898        SVN_ERR(vwrite_tuple_number(conn, pool, ap));
899      else if (*fmt == 'b' && !opt)
900        SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
901      else if (*fmt == '!' && !*(fmt + 1))
902        return SVN_NO_ERROR;
903      else
904        SVN_ERR_MALFUNCTION();
905    }
906  SVN_ERR(svn_ra_svn__end_list(conn, pool));
907  return SVN_NO_ERROR;
908}
909
910svn_error_t *
911svn_ra_svn__write_tuple(svn_ra_svn_conn_t *conn,
912                        apr_pool_t *pool,
913                        const char *fmt, ...)
914{
915  svn_error_t *err;
916  va_list ap;
917
918  va_start(ap, fmt);
919  err = vwrite_tuple(conn, pool, fmt, &ap);
920  va_end(ap);
921  return err;
922}
923
924/* --- READING DATA ITEMS --- */
925
926/* Read LEN bytes from CONN into already-allocated structure ITEM.
927 * Afterwards, *ITEM is of type 'SVN_RA_SVN_STRING', and its string
928 * data is allocated in POOL. */
929static svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
930                                svn_ra_svn_item_t *item, apr_uint64_t len64)
931{
932  svn_stringbuf_t *stringbuf;
933  apr_size_t len = (apr_size_t)len64;
934  apr_size_t readbuf_len;
935  char *dest;
936
937  /* We can't store strings longer than the maximum size of apr_size_t,
938   * so check for wrapping */
939  if (len64 > APR_SIZE_MAX)
940    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
941                            _("String length larger than maximum"));
942
943  /* Read the string in chunks.  The chunk size is large enough to avoid
944   * re-allocation in typical cases, and small enough to ensure we do not
945   * pre-allocate an unreasonable amount of memory if (perhaps due to
946   * network data corruption or a DOS attack), we receive a bogus claim that
947   * a very long string is going to follow.  In that case, we start small
948   * and wait for all that data to actually show up.  This does not fully
949   * prevent DOS attacks but makes them harder (you have to actually send
950   * gigabytes of data). */
951  readbuf_len = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD
952                    ? len
953                    : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD;
954  stringbuf = svn_stringbuf_create_ensure(readbuf_len, pool);
955  dest = stringbuf->data;
956
957  /* Read remaining string data directly into the string structure.
958   * Do it iteratively, if necessary.  */
959  while (readbuf_len)
960    {
961      SVN_ERR(readbuf_read(conn, pool, dest, readbuf_len));
962
963      stringbuf->len += readbuf_len;
964      len -= readbuf_len;
965
966      /* Early exit. In most cases, strings can be read in the first
967       * iteration. */
968      if (len == 0)
969        break;
970
971      /* Prepare next iteration: determine length of chunk to read
972       * and re-alloc the string buffer. */
973      readbuf_len
974        = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD
975              ? len
976              : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD;
977
978      svn_stringbuf_ensure(stringbuf, stringbuf->len + readbuf_len);
979      dest = stringbuf->data + stringbuf->len;
980    }
981
982  /* zero-terminate the string */
983  stringbuf->data[stringbuf->len] = '\0';
984
985  /* Return the string properly wrapped into an RA_SVN item. */
986  item->kind = SVN_RA_SVN_STRING;
987  item->u.string = svn_stringbuf__morph_into_string(stringbuf);
988
989  return SVN_NO_ERROR;
990}
991
992/* Given the first non-whitespace character FIRST_CHAR, read an item
993 * into the already allocated structure ITEM.  LEVEL should be set
994 * to 0 for the first call and is used to enforce a recurssion limit
995 * on the parser. */
996static svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
997                              svn_ra_svn_item_t *item, char first_char,
998                              int level)
999{
1000  char c = first_char;
1001  apr_uint64_t val;
1002  svn_stringbuf_t *str;
1003  svn_ra_svn_item_t *listitem;
1004
1005  if (++level >= 64)
1006    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1007                            _("Too many nested items"));
1008
1009
1010  /* Determine the item type and read it in.  Make sure that c is the
1011   * first character at the end of the item so we can test to make
1012   * sure it's whitespace. */
1013  if (svn_ctype_isdigit(c))
1014    {
1015      /* It's a number or a string.  Read the number part, either way. */
1016      val = c - '0';
1017      while (1)
1018        {
1019          apr_uint64_t prev_val = val;
1020          SVN_ERR(readbuf_getchar(conn, pool, &c));
1021          if (!svn_ctype_isdigit(c))
1022            break;
1023          val = val * 10 + (c - '0');
1024          /* val wrapped past maximum value? */
1025          if (prev_val >= (APR_UINT64_MAX / 10) && (val / 10) != prev_val)
1026            return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1027                                    _("Number is larger than maximum"));
1028        }
1029      if (c == ':')
1030        {
1031          /* It's a string. */
1032          SVN_ERR(read_string(conn, pool, item, val));
1033          SVN_ERR(readbuf_getchar(conn, pool, &c));
1034        }
1035      else
1036        {
1037          /* It's a number. */
1038          item->kind = SVN_RA_SVN_NUMBER;
1039          item->u.number = val;
1040        }
1041    }
1042  else if (svn_ctype_isalpha(c))
1043    {
1044      /* It's a word. */
1045      str = svn_stringbuf_create_ensure(16, pool);
1046      svn_stringbuf_appendbyte(str, c);
1047      while (1)
1048        {
1049          SVN_ERR(readbuf_getchar(conn, pool, &c));
1050          if (!svn_ctype_isalnum(c) && c != '-')
1051            break;
1052          svn_stringbuf_appendbyte(str, c);
1053        }
1054      item->kind = SVN_RA_SVN_WORD;
1055      item->u.word = str->data;
1056    }
1057  else if (c == '(')
1058    {
1059      /* Read in the list items. */
1060      item->kind = SVN_RA_SVN_LIST;
1061      item->u.list = apr_array_make(pool, 4, sizeof(svn_ra_svn_item_t));
1062      while (1)
1063        {
1064          SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1065          if (c == ')')
1066            break;
1067          listitem = apr_array_push(item->u.list);
1068          SVN_ERR(read_item(conn, pool, listitem, c, level));
1069        }
1070      SVN_ERR(readbuf_getchar(conn, pool, &c));
1071    }
1072
1073  if (!svn_iswhitespace(c))
1074    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1075                            _("Malformed network data"));
1076  return SVN_NO_ERROR;
1077}
1078
1079/* Given the first non-whitespace character FIRST_CHAR, read the first
1080 * command (word) encountered in CONN into *ITEM.  If ITEM is NULL, skip
1081 * to the end of the current list.  Use POOL for allocations. */
1082static svn_error_t *
1083read_command_only(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1084                  const char **item, char first_char)
1085{
1086  char c = first_char;
1087
1088  /* Determine the item type and read it in.  Make sure that c is the
1089  * first character at the end of the item so we can test to make
1090  * sure it's whitespace. */
1091  if (svn_ctype_isdigit(c))
1092    {
1093      /* It's a number or a string.  Read the number part, either way. */
1094      apr_uint64_t val, prev_val=0;
1095      val = c - '0';
1096      while (1)
1097        {
1098          prev_val = val;
1099          SVN_ERR(readbuf_getchar(conn, pool, &c));
1100          if (!svn_ctype_isdigit(c))
1101            break;
1102          val = val * 10 + (c - '0');
1103          if (prev_val >= (APR_UINT64_MAX / 10)) /* > maximum value? */
1104            return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1105                                    _("Number is larger than maximum"));
1106        }
1107      if (c == ':')
1108        {
1109          /* It's a string. */
1110          SVN_ERR(readbuf_skip(conn, val));
1111          SVN_ERR(readbuf_getchar(conn, pool, &c));
1112        }
1113    }
1114  else if (svn_ctype_isalpha(c))
1115    {
1116      /* It's a word. */
1117      if (item)
1118        {
1119          /* This is the word we want to read */
1120
1121          char *buf = apr_palloc(pool, 32);
1122          apr_size_t len = 1;
1123          buf[0] = c;
1124
1125          while (1)
1126            {
1127              SVN_ERR(readbuf_getchar(conn, pool, &c));
1128              if (!svn_ctype_isalnum(c) && c != '-')
1129                break;
1130              buf[len] = c;
1131              if (++len == 32)
1132                return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1133                                        _("Word too long"));
1134            }
1135          buf[len] = 0;
1136          *item = buf;
1137        }
1138      else
1139        {
1140          /* we don't need the actual word, just skip it */
1141          do
1142          {
1143            SVN_ERR(readbuf_getchar(conn, pool, &c));
1144          }
1145          while (svn_ctype_isalnum(c) || c == '-');
1146        }
1147    }
1148  else if (c == '(')
1149    {
1150      /* Read in the list items. */
1151      while (1)
1152        {
1153          SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1154          if (c == ')')
1155            break;
1156
1157          if (item && *item == NULL)
1158            SVN_ERR(read_command_only(conn, pool, item, c));
1159          else
1160            SVN_ERR(read_command_only(conn, pool, NULL, c));
1161        }
1162      SVN_ERR(readbuf_getchar(conn, pool, &c));
1163    }
1164
1165  return SVN_NO_ERROR;
1166}
1167
1168svn_error_t *
1169svn_ra_svn__read_item(svn_ra_svn_conn_t *conn,
1170                      apr_pool_t *pool,
1171                      svn_ra_svn_item_t **item)
1172{
1173  char c;
1174
1175  /* Allocate space, read the first character, and then do the rest of
1176   * the work.  This makes sense because of the way lists are read. */
1177  *item = apr_palloc(pool, sizeof(**item));
1178  SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1179  return read_item(conn, pool, *item, c, 0);
1180}
1181
1182svn_error_t *
1183svn_ra_svn__skip_leading_garbage(svn_ra_svn_conn_t *conn,
1184                                 apr_pool_t *pool)
1185{
1186  return readbuf_skip_leading_garbage(conn, pool);
1187}
1188
1189/* --- READING AND PARSING TUPLES --- */
1190
1191/* Parse a tuple of svn_ra_svn_item_t *'s.  Advance *FMT to the end of the
1192 * tuple specification and advance AP by the corresponding arguments. */
1193static svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *pool,
1194                                 const char **fmt, va_list *ap)
1195{
1196  int count, nesting_level;
1197  svn_ra_svn_item_t *elt;
1198
1199  for (count = 0; **fmt && count < items->nelts; (*fmt)++, count++)
1200    {
1201      /* '?' just means the tuple may stop; skip past it. */
1202      if (**fmt == '?')
1203        (*fmt)++;
1204      elt = &APR_ARRAY_IDX(items, count, svn_ra_svn_item_t);
1205      if (**fmt == 'n' && elt->kind == SVN_RA_SVN_NUMBER)
1206        *va_arg(*ap, apr_uint64_t *) = elt->u.number;
1207      else if (**fmt == 'r' && elt->kind == SVN_RA_SVN_NUMBER)
1208        *va_arg(*ap, svn_revnum_t *) = (svn_revnum_t) elt->u.number;
1209      else if (**fmt == 's' && elt->kind == SVN_RA_SVN_STRING)
1210        *va_arg(*ap, svn_string_t **) = elt->u.string;
1211      else if (**fmt == 'c' && elt->kind == SVN_RA_SVN_STRING)
1212        *va_arg(*ap, const char **) = elt->u.string->data;
1213      else if (**fmt == 'w' && elt->kind == SVN_RA_SVN_WORD)
1214        *va_arg(*ap, const char **) = elt->u.word;
1215      else if (**fmt == 'b' && elt->kind == SVN_RA_SVN_WORD)
1216        {
1217          if (strcmp(elt->u.word, "true") == 0)
1218            *va_arg(*ap, svn_boolean_t *) = TRUE;
1219          else if (strcmp(elt->u.word, "false") == 0)
1220            *va_arg(*ap, svn_boolean_t *) = FALSE;
1221          else
1222            break;
1223        }
1224      else if (**fmt == 'B' && elt->kind == SVN_RA_SVN_WORD)
1225        {
1226          if (strcmp(elt->u.word, "true") == 0)
1227            *va_arg(*ap, apr_uint64_t *) = TRUE;
1228          else if (strcmp(elt->u.word, "false") == 0)
1229            *va_arg(*ap, apr_uint64_t *) = FALSE;
1230          else
1231            break;
1232        }
1233      else if (**fmt == 'l' && elt->kind == SVN_RA_SVN_LIST)
1234        *va_arg(*ap, apr_array_header_t **) = elt->u.list;
1235      else if (**fmt == '(' && elt->kind == SVN_RA_SVN_LIST)
1236        {
1237          (*fmt)++;
1238          SVN_ERR(vparse_tuple(elt->u.list, pool, fmt, ap));
1239        }
1240      else if (**fmt == ')')
1241        return SVN_NO_ERROR;
1242      else
1243        break;
1244    }
1245  if (**fmt == '?')
1246    {
1247      nesting_level = 0;
1248      for (; **fmt; (*fmt)++)
1249        {
1250          switch (**fmt)
1251            {
1252            case '?':
1253              break;
1254            case 'r':
1255              *va_arg(*ap, svn_revnum_t *) = SVN_INVALID_REVNUM;
1256              break;
1257            case 's':
1258              *va_arg(*ap, svn_string_t **) = NULL;
1259              break;
1260            case 'c':
1261            case 'w':
1262              *va_arg(*ap, const char **) = NULL;
1263              break;
1264            case 'l':
1265              *va_arg(*ap, apr_array_header_t **) = NULL;
1266              break;
1267            case 'B':
1268            case 'n':
1269              *va_arg(*ap, apr_uint64_t *) = SVN_RA_SVN_UNSPECIFIED_NUMBER;
1270              break;
1271            case '(':
1272              nesting_level++;
1273              break;
1274            case ')':
1275              if (--nesting_level < 0)
1276                return SVN_NO_ERROR;
1277              break;
1278            default:
1279              SVN_ERR_MALFUNCTION();
1280            }
1281        }
1282    }
1283  if (**fmt && **fmt != ')')
1284    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1285                            _("Malformed network data"));
1286  return SVN_NO_ERROR;
1287}
1288
1289svn_error_t *
1290svn_ra_svn__parse_tuple(const apr_array_header_t *list,
1291                        apr_pool_t *pool,
1292                        const char *fmt, ...)
1293{
1294  svn_error_t *err;
1295  va_list ap;
1296
1297  va_start(ap, fmt);
1298  err = vparse_tuple(list, pool, &fmt, &ap);
1299  va_end(ap);
1300  return err;
1301}
1302
1303svn_error_t *
1304svn_ra_svn__read_tuple(svn_ra_svn_conn_t *conn,
1305                       apr_pool_t *pool,
1306                       const char *fmt, ...)
1307{
1308  va_list ap;
1309  svn_ra_svn_item_t *item;
1310  svn_error_t *err;
1311
1312  SVN_ERR(svn_ra_svn__read_item(conn, pool, &item));
1313  if (item->kind != SVN_RA_SVN_LIST)
1314    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1315                            _("Malformed network data"));
1316  va_start(ap, fmt);
1317  err = vparse_tuple(item->u.list, pool, &fmt, &ap);
1318  va_end(ap);
1319  return err;
1320}
1321
1322svn_error_t *
1323svn_ra_svn__read_command_only(svn_ra_svn_conn_t *conn,
1324                              apr_pool_t *pool,
1325                              const char **command)
1326{
1327  char c;
1328  SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1329
1330  *command = NULL;
1331  return read_command_only(conn, pool, command, c);
1332}
1333
1334
1335svn_error_t *
1336svn_ra_svn__parse_proplist(const apr_array_header_t *list,
1337                           apr_pool_t *pool,
1338                           apr_hash_t **props)
1339{
1340  char *name;
1341  svn_string_t *value;
1342  svn_ra_svn_item_t *elt;
1343  int i;
1344
1345  *props = apr_hash_make(pool);
1346  for (i = 0; i < list->nelts; i++)
1347    {
1348      elt = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
1349      if (elt->kind != SVN_RA_SVN_LIST)
1350        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1351                                _("Proplist element not a list"));
1352      SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, pool, "cs",
1353                                      &name, &value));
1354      svn_hash_sets(*props, name, value);
1355    }
1356
1357  return SVN_NO_ERROR;
1358}
1359
1360
1361/* --- READING AND WRITING COMMANDS AND RESPONSES --- */
1362
1363svn_error_t *svn_ra_svn__locate_real_error_child(svn_error_t *err)
1364{
1365  svn_error_t *this_link;
1366
1367  SVN_ERR_ASSERT(err);
1368
1369  for (this_link = err;
1370       this_link && (this_link->apr_err == SVN_ERR_RA_SVN_CMD_ERR);
1371       this_link = this_link->child)
1372    ;
1373
1374  SVN_ERR_ASSERT(this_link);
1375  return this_link;
1376}
1377
1378svn_error_t *svn_ra_svn__handle_failure_status(const apr_array_header_t *params,
1379                                               apr_pool_t *pool)
1380{
1381  const char *message, *file;
1382  svn_error_t *err = NULL;
1383  svn_ra_svn_item_t *elt;
1384  int i;
1385  apr_uint64_t apr_err, line;
1386  apr_pool_t *subpool = svn_pool_create(pool);
1387
1388  if (params->nelts == 0)
1389    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1390                            _("Empty error list"));
1391
1392  /* Rebuild the error list from the end, to avoid reversing the order. */
1393  for (i = params->nelts - 1; i >= 0; i--)
1394    {
1395      svn_pool_clear(subpool);
1396      elt = &APR_ARRAY_IDX(params, i, svn_ra_svn_item_t);
1397      if (elt->kind != SVN_RA_SVN_LIST)
1398        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1399                                _("Malformed error list"));
1400      SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, subpool, "nccn",
1401                                      &apr_err, &message, &file, &line));
1402      /* The message field should have been optional, but we can't
1403         easily change that, so "" means a nonexistent message. */
1404      if (!*message)
1405        message = NULL;
1406
1407      /* Skip over links in the error chain that were intended only to
1408         exist on the server (to wrap real errors intended for the
1409         client) but accidentally got included in the server's actual
1410         response. */
1411      if ((apr_status_t)apr_err != SVN_ERR_RA_SVN_CMD_ERR)
1412        {
1413          err = svn_error_create((apr_status_t)apr_err, err, message);
1414          err->file = apr_pstrdup(err->pool, file);
1415          err->line = (long)line;
1416        }
1417    }
1418
1419  svn_pool_destroy(subpool);
1420
1421  /* If we get here, then we failed to find a real error in the error
1422     chain that the server proported to be sending us.  That's bad. */
1423  if (! err)
1424    err = svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1425                           _("Malformed error list"));
1426
1427  return err;
1428}
1429
1430svn_error_t *
1431svn_ra_svn__read_cmd_response(svn_ra_svn_conn_t *conn,
1432                              apr_pool_t *pool,
1433                              const char *fmt, ...)
1434{
1435  va_list ap;
1436  const char *status;
1437  apr_array_header_t *params;
1438  svn_error_t *err;
1439
1440  SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "wl", &status, &params));
1441  if (strcmp(status, "success") == 0)
1442    {
1443      va_start(ap, fmt);
1444      err = vparse_tuple(params, pool, &fmt, &ap);
1445      va_end(ap);
1446      return err;
1447    }
1448  else if (strcmp(status, "failure") == 0)
1449    {
1450      return svn_ra_svn__handle_failure_status(params, pool);
1451    }
1452
1453  return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1454                           _("Unknown status '%s' in command response"),
1455                           status);
1456}
1457
1458svn_error_t *
1459svn_ra_svn__handle_commands2(svn_ra_svn_conn_t *conn,
1460                             apr_pool_t *pool,
1461                             const svn_ra_svn_cmd_entry_t *commands,
1462                             void *baton,
1463                             svn_boolean_t error_on_disconnect)
1464{
1465  apr_pool_t *subpool = svn_pool_create(pool);
1466  apr_pool_t *iterpool = svn_pool_create(subpool);
1467  const char *cmdname;
1468  const svn_ra_svn_cmd_entry_t *command;
1469  svn_error_t *err, *write_err;
1470  apr_array_header_t *params;
1471  apr_hash_t *cmd_hash = apr_hash_make(subpool);
1472
1473  for (command = commands; command->cmdname; command++)
1474    svn_hash_sets(cmd_hash, command->cmdname, command);
1475
1476  while (1)
1477    {
1478      svn_pool_clear(iterpool);
1479      err = svn_ra_svn__read_tuple(conn, iterpool, "wl", &cmdname, &params);
1480      if (err)
1481        {
1482          if (!error_on_disconnect
1483              && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED)
1484            {
1485              svn_error_clear(err);
1486              svn_pool_destroy(subpool);
1487              return SVN_NO_ERROR;
1488            }
1489          return err;
1490        }
1491      command = svn_hash_gets(cmd_hash, cmdname);
1492
1493      if (command)
1494        err = (*command->handler)(conn, iterpool, params, baton);
1495      else
1496        {
1497          err = svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL,
1498                                  _("Unknown editor command '%s'"), cmdname);
1499          err = svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, err, NULL);
1500        }
1501
1502      if (err && err->apr_err == SVN_ERR_RA_SVN_CMD_ERR)
1503        {
1504          write_err = svn_ra_svn__write_cmd_failure(
1505                          conn, iterpool,
1506                          svn_ra_svn__locate_real_error_child(err));
1507          svn_error_clear(err);
1508          if (write_err)
1509            return write_err;
1510        }
1511      else if (err)
1512        return err;
1513
1514      if (command && command->terminate)
1515        break;
1516    }
1517  svn_pool_destroy(iterpool);
1518  svn_pool_destroy(subpool);
1519  return SVN_NO_ERROR;
1520}
1521
1522svn_error_t *
1523svn_ra_svn__write_cmd_target_rev(svn_ra_svn_conn_t *conn,
1524                                 apr_pool_t *pool,
1525                                 svn_revnum_t rev)
1526{
1527  SVN_ERR(writebuf_write_short_string(conn, pool, "( target-rev ( ", 15));
1528  SVN_ERR(write_tuple_revision(conn, pool, rev));
1529  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1530
1531  return SVN_NO_ERROR;
1532}
1533
1534svn_error_t *
1535svn_ra_svn__write_cmd_open_root(svn_ra_svn_conn_t *conn,
1536                                apr_pool_t *pool,
1537                                svn_revnum_t rev,
1538                                const char *token)
1539{
1540  SVN_ERR(writebuf_write_short_string(conn, pool, "( open-root ( ", 14));
1541  SVN_ERR(write_tuple_start_list(conn, pool));
1542  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
1543  SVN_ERR(write_tuple_end_list(conn, pool));
1544  SVN_ERR(write_tuple_cstring(conn, pool, token));
1545  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1546
1547  return SVN_NO_ERROR;
1548}
1549
1550svn_error_t *
1551svn_ra_svn__write_cmd_delete_entry(svn_ra_svn_conn_t *conn,
1552                                   apr_pool_t *pool,
1553                                   const char *path,
1554                                   svn_revnum_t rev,
1555                                   const char *token)
1556{
1557  SVN_ERR(writebuf_write_short_string(conn, pool, "( delete-entry ( ", 17));
1558  SVN_ERR(write_tuple_cstring(conn, pool, path));
1559  SVN_ERR(write_tuple_start_list(conn, pool));
1560  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
1561  SVN_ERR(write_tuple_end_list(conn, pool));
1562  SVN_ERR(write_tuple_cstring(conn, pool, token));
1563  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1564
1565  return SVN_NO_ERROR;
1566}
1567
1568svn_error_t *
1569svn_ra_svn__write_cmd_add_dir(svn_ra_svn_conn_t *conn,
1570                              apr_pool_t *pool,
1571                              const char *path,
1572                              const char *parent_token,
1573                              const char *token,
1574                              const char *copy_path,
1575                              svn_revnum_t copy_rev)
1576{
1577  SVN_ERR(writebuf_write_short_string(conn, pool, "( add-dir ( ", 12));
1578  SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token,
1579                              copy_path, copy_rev));
1580  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1581
1582  return SVN_NO_ERROR;
1583}
1584
1585svn_error_t *
1586svn_ra_svn__write_cmd_open_dir(svn_ra_svn_conn_t *conn,
1587                               apr_pool_t *pool,
1588                               const char *path,
1589                               const char *parent_token,
1590                               const char *token,
1591                               svn_revnum_t rev)
1592{
1593  SVN_ERR(writebuf_write_short_string(conn, pool, "( open-dir ( ", 13));
1594  SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev));
1595  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1596
1597  return SVN_NO_ERROR;
1598}
1599
1600svn_error_t *
1601svn_ra_svn__write_cmd_change_dir_prop(svn_ra_svn_conn_t *conn,
1602                                      apr_pool_t *pool,
1603                                      const char *token,
1604                                      const char *name,
1605                                      const svn_string_t *value)
1606{
1607  SVN_ERR(writebuf_write_short_string(conn, pool, "( change-dir-prop ( ", 20));
1608  SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value));
1609  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1610
1611  return SVN_NO_ERROR;
1612}
1613
1614svn_error_t *
1615svn_ra_svn__write_cmd_close_dir(svn_ra_svn_conn_t *conn,
1616                                apr_pool_t *pool,
1617                                const char *token)
1618{
1619  SVN_ERR(writebuf_write_short_string(conn, pool, "( close-dir ( ", 14));
1620  SVN_ERR(write_tuple_cstring(conn, pool, token));
1621  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1622
1623  return SVN_NO_ERROR;
1624}
1625
1626svn_error_t *
1627svn_ra_svn__write_cmd_absent_dir(svn_ra_svn_conn_t *conn,
1628                                 apr_pool_t *pool,
1629                                 const char *path,
1630                                 const char *parent_token)
1631{
1632  SVN_ERR(writebuf_write_short_string(conn, pool, "( absent-dir ( ", 15));
1633  SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token));
1634  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1635
1636  return SVN_NO_ERROR;
1637}
1638
1639svn_error_t *
1640svn_ra_svn__write_cmd_add_file(svn_ra_svn_conn_t *conn,
1641                               apr_pool_t *pool,
1642                               const char *path,
1643                               const char *parent_token,
1644                               const char *token,
1645                               const char *copy_path,
1646                               svn_revnum_t copy_rev)
1647{
1648  SVN_ERR(writebuf_write_short_string(conn, pool, "( add-file ( ", 13));
1649  SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token,
1650                              copy_path, copy_rev));
1651  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1652
1653  return SVN_NO_ERROR;
1654}
1655
1656svn_error_t *
1657svn_ra_svn__write_cmd_open_file(svn_ra_svn_conn_t *conn,
1658                                apr_pool_t *pool,
1659                                const char *path,
1660                                const char *parent_token,
1661                                const char *token,
1662                                svn_revnum_t rev)
1663{
1664  SVN_ERR(writebuf_write_short_string(conn, pool, "( open-file ( ", 14));
1665  SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev));
1666  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1667
1668  return SVN_NO_ERROR;
1669}
1670
1671svn_error_t *
1672svn_ra_svn__write_cmd_change_file_prop(svn_ra_svn_conn_t *conn,
1673                                       apr_pool_t *pool,
1674                                       const char *token,
1675                                       const char *name,
1676                                       const svn_string_t *value)
1677{
1678  SVN_ERR(writebuf_write_short_string(conn, pool, "( change-file-prop ( ", 21));
1679  SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value));
1680  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1681
1682  return SVN_NO_ERROR;
1683}
1684
1685svn_error_t *
1686svn_ra_svn__write_cmd_close_file(svn_ra_svn_conn_t *conn,
1687                                 apr_pool_t *pool,
1688                                 const char *token,
1689                                 const char *text_checksum)
1690{
1691  SVN_ERR(writebuf_write_short_string(conn, pool, "( close-file ( ", 15));
1692  SVN_ERR(write_tuple_cstring(conn, pool, token));
1693  SVN_ERR(write_tuple_start_list(conn, pool));
1694  SVN_ERR(write_tuple_cstring_opt(conn, pool, text_checksum));
1695  SVN_ERR(write_tuple_end_list(conn, pool));
1696  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1697
1698  return SVN_NO_ERROR;
1699}
1700
1701svn_error_t *
1702svn_ra_svn__write_cmd_absent_file(svn_ra_svn_conn_t *conn,
1703                                  apr_pool_t *pool,
1704                                  const char *path,
1705                                  const char *parent_token)
1706{
1707  SVN_ERR(writebuf_write_short_string(conn, pool, "( absent-file ( ", 16));
1708  SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token));
1709  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1710
1711  return SVN_NO_ERROR;
1712}
1713
1714svn_error_t *
1715svn_ra_svn__write_cmd_textdelta_chunk(svn_ra_svn_conn_t *conn,
1716                                      apr_pool_t *pool,
1717                                      const char *token,
1718                                      const svn_string_t *chunk)
1719{
1720  SVN_ERR(writebuf_write_short_string(conn, pool, "( textdelta-chunk ( ", 20));
1721  SVN_ERR(write_tuple_cstring(conn, pool, token));
1722  SVN_ERR(write_tuple_string(conn, pool, chunk));
1723  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1724
1725  return SVN_NO_ERROR;
1726}
1727
1728svn_error_t *
1729svn_ra_svn__write_cmd_textdelta_end(svn_ra_svn_conn_t *conn,
1730                                    apr_pool_t *pool,
1731                                    const char *token)
1732{
1733  SVN_ERR(writebuf_write_short_string(conn, pool, "( textdelta-end ( ", 18));
1734  SVN_ERR(write_tuple_cstring(conn, pool, token));
1735  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1736
1737  return SVN_NO_ERROR;
1738}
1739
1740svn_error_t *
1741svn_ra_svn__write_cmd_apply_textdelta(svn_ra_svn_conn_t *conn,
1742                                      apr_pool_t *pool,
1743                                      const char *token,
1744                                      const char *base_checksum)
1745{
1746  SVN_ERR(writebuf_write_short_string(conn, pool, "( apply-textdelta ( ", 20));
1747  SVN_ERR(write_tuple_cstring(conn, pool, token));
1748  SVN_ERR(write_tuple_start_list(conn, pool));
1749  SVN_ERR(write_tuple_cstring_opt(conn, pool, base_checksum));
1750  SVN_ERR(write_tuple_end_list(conn, pool));
1751  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1752
1753  return SVN_NO_ERROR;
1754}
1755
1756svn_error_t *
1757svn_ra_svn__write_cmd_close_edit(svn_ra_svn_conn_t *conn,
1758                                 apr_pool_t *pool)
1759{
1760  return writebuf_write_short_string(conn, pool, "( close-edit ( ) ) ", 19);
1761}
1762
1763svn_error_t *
1764svn_ra_svn__write_cmd_abort_edit(svn_ra_svn_conn_t *conn,
1765                                 apr_pool_t *pool)
1766{
1767  return writebuf_write_short_string(conn, pool, "( abort-edit ( ) ) ", 19);
1768}
1769
1770svn_error_t *
1771svn_ra_svn__write_cmd_set_path(svn_ra_svn_conn_t *conn,
1772                               apr_pool_t *pool,
1773                               const char *path,
1774                               svn_revnum_t rev,
1775                               svn_boolean_t start_empty,
1776                               const char *lock_token,
1777                               svn_depth_t depth)
1778{
1779  SVN_ERR(writebuf_write_short_string(conn, pool, "( set-path ( ", 13));
1780  SVN_ERR(write_tuple_cstring(conn, pool, path));
1781  SVN_ERR(write_tuple_revision(conn, pool, rev));
1782  SVN_ERR(write_tuple_boolean(conn, pool, start_empty));
1783  SVN_ERR(write_tuple_start_list(conn, pool));
1784  SVN_ERR(write_tuple_cstring_opt(conn, pool, lock_token));
1785  SVN_ERR(write_tuple_end_list(conn, pool));
1786  SVN_ERR(write_tuple_depth(conn, pool, depth));
1787  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1788
1789  return SVN_NO_ERROR;
1790}
1791
1792svn_error_t *
1793svn_ra_svn__write_cmd_delete_path(svn_ra_svn_conn_t *conn,
1794                                  apr_pool_t *pool,
1795                                  const char *path)
1796{
1797  SVN_ERR(writebuf_write_short_string(conn, pool, "( delete-path ( ", 16));
1798  SVN_ERR(write_tuple_cstring(conn, pool, path));
1799  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1800
1801  return SVN_NO_ERROR;
1802}
1803
1804svn_error_t *
1805svn_ra_svn__write_cmd_link_path(svn_ra_svn_conn_t *conn,
1806                                apr_pool_t *pool,
1807                                const char *path,
1808                                const char *url,
1809                                svn_revnum_t rev,
1810                                svn_boolean_t start_empty,
1811                                const char *lock_token,
1812                                svn_depth_t depth)
1813{
1814  SVN_ERR(writebuf_write_short_string(conn, pool, "( link-path ( ", 14));
1815  SVN_ERR(write_tuple_cstring(conn, pool, path));
1816  SVN_ERR(write_tuple_cstring(conn, pool, url));
1817  SVN_ERR(write_tuple_revision(conn, pool, rev));
1818  SVN_ERR(write_tuple_boolean(conn, pool, start_empty));
1819  SVN_ERR(write_tuple_start_list(conn, pool));
1820  SVN_ERR(write_tuple_cstring_opt(conn, pool,lock_token));
1821  SVN_ERR(write_tuple_end_list(conn, pool));
1822  SVN_ERR(write_tuple_depth(conn, pool, depth));
1823  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1824
1825  return SVN_NO_ERROR;
1826}
1827
1828svn_error_t *
1829svn_ra_svn__write_cmd_finish_report(svn_ra_svn_conn_t *conn,
1830                                    apr_pool_t *pool)
1831{
1832  return writebuf_write_short_string(conn, pool, "( finish-report ( ) ) ", 22);
1833}
1834
1835svn_error_t *
1836svn_ra_svn__write_cmd_abort_report(svn_ra_svn_conn_t *conn,
1837                                   apr_pool_t *pool)
1838{
1839  return writebuf_write_short_string(conn, pool, "( abort-report ( ) ) ", 21);
1840}
1841
1842svn_error_t *
1843svn_ra_svn__write_cmd_reparent(svn_ra_svn_conn_t *conn,
1844                               apr_pool_t *pool,
1845                               const char *url)
1846{
1847  SVN_ERR(writebuf_write_short_string(conn, pool, "( reparent ( ", 13));
1848  SVN_ERR(write_tuple_cstring(conn, pool, url));
1849  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1850
1851  return SVN_NO_ERROR;
1852}
1853
1854svn_error_t *
1855svn_ra_svn__write_cmd_get_latest_rev(svn_ra_svn_conn_t *conn,
1856                                   apr_pool_t *pool)
1857{
1858  return writebuf_write_short_string(conn, pool, "( get-latest-rev ( ) ) ", 23);
1859}
1860
1861svn_error_t *
1862svn_ra_svn__write_cmd_get_dated_rev(svn_ra_svn_conn_t *conn,
1863                                    apr_pool_t *pool,
1864                                    apr_time_t tm)
1865{
1866  SVN_ERR(writebuf_write_short_string(conn, pool, "( get-dated-rev ( ", 18));
1867  SVN_ERR(write_tuple_cstring(conn, pool, svn_time_to_cstring(tm, pool)));
1868  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1869
1870  return SVN_NO_ERROR;
1871}
1872
1873svn_error_t *
1874svn_ra_svn__write_cmd_change_rev_prop2(svn_ra_svn_conn_t *conn,
1875                                       apr_pool_t *pool,
1876                                       svn_revnum_t rev,
1877                                       const char *name,
1878                                       const svn_string_t *value,
1879                                       svn_boolean_t dont_care,
1880                                       const svn_string_t *old_value)
1881{
1882  SVN_ERR(writebuf_write_short_string(conn, pool, "( change-rev-prop2 ( ", 21));
1883  SVN_ERR(write_tuple_revision(conn, pool, rev));
1884  SVN_ERR(write_tuple_cstring(conn, pool, name));
1885  SVN_ERR(write_tuple_start_list(conn, pool));
1886  SVN_ERR(write_tuple_string_opt(conn, pool, value));
1887  SVN_ERR(write_tuple_end_list(conn, pool));
1888  SVN_ERR(write_tuple_start_list(conn, pool));
1889  SVN_ERR(write_tuple_boolean(conn, pool, dont_care));
1890  SVN_ERR(write_tuple_string_opt(conn, pool, old_value));
1891  SVN_ERR(write_tuple_end_list(conn, pool));
1892  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1893
1894  return SVN_NO_ERROR;
1895}
1896
1897svn_error_t *
1898svn_ra_svn__write_cmd_change_rev_prop(svn_ra_svn_conn_t *conn,
1899                                      apr_pool_t *pool,
1900                                      svn_revnum_t rev,
1901                                      const char *name,
1902                                      const svn_string_t *value)
1903{
1904  SVN_ERR(writebuf_write_short_string(conn, pool, "( change-rev-prop ( ", 20));
1905  SVN_ERR(write_tuple_revision(conn, pool, rev));
1906  SVN_ERR(write_tuple_cstring(conn, pool, name));
1907  SVN_ERR(write_tuple_string_opt(conn, pool, value));
1908  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1909
1910  return SVN_NO_ERROR;
1911}
1912
1913svn_error_t *
1914svn_ra_svn__write_cmd_rev_proplist(svn_ra_svn_conn_t *conn,
1915                                   apr_pool_t *pool,
1916                                   svn_revnum_t rev)
1917{
1918  SVN_ERR(writebuf_write_short_string(conn, pool, "( rev-proplist ( ", 17));
1919  SVN_ERR(write_tuple_revision(conn, pool, rev));
1920  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1921
1922  return SVN_NO_ERROR;
1923}
1924
1925svn_error_t *
1926svn_ra_svn__write_cmd_rev_prop(svn_ra_svn_conn_t *conn,
1927                               apr_pool_t *pool,
1928                               svn_revnum_t rev,
1929                               const char *name)
1930{
1931  SVN_ERR(writebuf_write_short_string(conn, pool, "( rev-prop ( ", 13));
1932  SVN_ERR(write_tuple_revision(conn, pool, rev));
1933  SVN_ERR(write_tuple_cstring(conn, pool, name));
1934  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1935
1936  return SVN_NO_ERROR;
1937}
1938
1939svn_error_t *
1940svn_ra_svn__write_cmd_get_file(svn_ra_svn_conn_t *conn,
1941                               apr_pool_t *pool,
1942                               const char *path,
1943                               svn_revnum_t rev,
1944                               svn_boolean_t props,
1945                               svn_boolean_t stream)
1946{
1947  SVN_ERR(writebuf_write_short_string(conn, pool, "( get-file ( ", 13));
1948  SVN_ERR(write_tuple_cstring(conn, pool, path));
1949  SVN_ERR(write_tuple_start_list(conn, pool));
1950  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
1951  SVN_ERR(write_tuple_end_list(conn, pool));
1952  SVN_ERR(write_tuple_boolean(conn, pool, props));
1953  SVN_ERR(write_tuple_boolean(conn, pool, stream));
1954  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1955
1956  return SVN_NO_ERROR;
1957}
1958
1959svn_error_t *
1960svn_ra_svn__write_cmd_update(svn_ra_svn_conn_t *conn,
1961                             apr_pool_t *pool,
1962                             svn_revnum_t rev,
1963                             const char *target,
1964                             svn_boolean_t recurse,
1965                             svn_depth_t depth,
1966                             svn_boolean_t send_copyfrom_args,
1967                             svn_boolean_t ignore_ancestry)
1968{
1969  SVN_ERR(writebuf_write_short_string(conn, pool, "( update ( ", 11));
1970  SVN_ERR(write_tuple_start_list(conn, pool));
1971  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
1972  SVN_ERR(write_tuple_end_list(conn, pool));
1973  SVN_ERR(write_tuple_cstring(conn, pool, target));
1974  SVN_ERR(write_tuple_boolean(conn, pool, recurse));
1975  SVN_ERR(write_tuple_depth(conn, pool, depth));
1976  SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args));
1977  SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
1978  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
1979
1980  return SVN_NO_ERROR;
1981}
1982
1983svn_error_t *
1984svn_ra_svn__write_cmd_switch(svn_ra_svn_conn_t *conn,
1985                             apr_pool_t *pool,
1986                             svn_revnum_t rev,
1987                             const char *target,
1988                             svn_boolean_t recurse,
1989                             const char *switch_url,
1990                             svn_depth_t depth,
1991                             svn_boolean_t send_copyfrom_args,
1992                             svn_boolean_t ignore_ancestry)
1993{
1994  SVN_ERR(writebuf_write_short_string(conn, pool, "( switch ( ", 11));
1995  SVN_ERR(write_tuple_start_list(conn, pool));
1996  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
1997  SVN_ERR(write_tuple_end_list(conn, pool));
1998  SVN_ERR(write_tuple_cstring(conn, pool, target));
1999  SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2000  SVN_ERR(write_tuple_cstring(conn, pool, switch_url));
2001  SVN_ERR(write_tuple_depth(conn, pool, depth));
2002  SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args));
2003  SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2004  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2005
2006  return SVN_NO_ERROR;
2007}
2008
2009svn_error_t *
2010svn_ra_svn__write_cmd_status(svn_ra_svn_conn_t *conn,
2011                             apr_pool_t *pool,
2012                             const char *target,
2013                             svn_boolean_t recurse,
2014                             svn_revnum_t rev,
2015                             svn_depth_t depth)
2016{
2017  SVN_ERR(writebuf_write_short_string(conn, pool, "( status ( ", 11));
2018  SVN_ERR(write_tuple_cstring(conn, pool, target));
2019  SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2020  SVN_ERR(write_tuple_start_list(conn, pool));
2021  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2022  SVN_ERR(write_tuple_end_list(conn, pool));
2023  SVN_ERR(write_tuple_depth(conn, pool, depth));
2024  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2025
2026  return SVN_NO_ERROR;
2027}
2028
2029svn_error_t *
2030svn_ra_svn__write_cmd_diff(svn_ra_svn_conn_t *conn,
2031                           apr_pool_t *pool,
2032                           svn_revnum_t rev,
2033                           const char *target,
2034                           svn_boolean_t recurse,
2035                           svn_boolean_t ignore_ancestry,
2036                           const char *versus_url,
2037                           svn_boolean_t text_deltas,
2038                           svn_depth_t depth)
2039{
2040  SVN_ERR(writebuf_write_short_string(conn, pool, "( diff ( ", 9));
2041  SVN_ERR(write_tuple_start_list(conn, pool));
2042  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2043  SVN_ERR(write_tuple_end_list(conn, pool));
2044  SVN_ERR(write_tuple_cstring(conn, pool, target));
2045  SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2046  SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2047  SVN_ERR(write_tuple_cstring(conn, pool, versus_url));
2048  SVN_ERR(write_tuple_boolean(conn, pool, text_deltas));
2049  SVN_ERR(write_tuple_depth(conn, pool, depth));
2050  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2051
2052  return SVN_NO_ERROR;
2053}
2054
2055svn_error_t *
2056svn_ra_svn__write_cmd_check_path(svn_ra_svn_conn_t *conn,
2057                                 apr_pool_t *pool,
2058                                 const char *path,
2059                                 svn_revnum_t rev)
2060{
2061  SVN_ERR(writebuf_write_short_string(conn, pool, "( check-path ( ", 15));
2062  SVN_ERR(write_tuple_cstring(conn, pool, path));
2063  SVN_ERR(write_tuple_start_list(conn, pool));
2064  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2065  SVN_ERR(write_tuple_end_list(conn, pool));
2066  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2067
2068  return SVN_NO_ERROR;
2069}
2070
2071svn_error_t *
2072svn_ra_svn__write_cmd_stat(svn_ra_svn_conn_t *conn,
2073                           apr_pool_t *pool,
2074                           const char *path,
2075                           svn_revnum_t rev)
2076{
2077  SVN_ERR(writebuf_write_short_string(conn, pool, "( stat ( ", 9));
2078  SVN_ERR(write_tuple_cstring(conn, pool, path));
2079  SVN_ERR(write_tuple_start_list(conn, pool));
2080  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2081  SVN_ERR(write_tuple_end_list(conn, pool));
2082  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2083
2084  return SVN_NO_ERROR;
2085}
2086
2087svn_error_t *
2088svn_ra_svn__write_cmd_get_file_revs(svn_ra_svn_conn_t *conn,
2089                                    apr_pool_t *pool,
2090                                    const char *path,
2091                                    svn_revnum_t start,
2092                                    svn_revnum_t end,
2093                                    svn_boolean_t include_merged_revisions)
2094{
2095  SVN_ERR(writebuf_write_short_string(conn, pool, "( get-file-revs ( ", 18));
2096  SVN_ERR(write_tuple_cstring(conn, pool, path));
2097  SVN_ERR(write_tuple_start_list(conn, pool));
2098  SVN_ERR(write_tuple_revision_opt(conn, pool, start));
2099  SVN_ERR(write_tuple_end_list(conn, pool));
2100  SVN_ERR(write_tuple_start_list(conn, pool));
2101  SVN_ERR(write_tuple_revision_opt(conn, pool, end));
2102  SVN_ERR(write_tuple_end_list(conn, pool));
2103  SVN_ERR(write_tuple_boolean(conn, pool, include_merged_revisions));
2104  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2105
2106  return SVN_NO_ERROR;
2107}
2108
2109svn_error_t *
2110svn_ra_svn__write_cmd_lock(svn_ra_svn_conn_t *conn,
2111                           apr_pool_t *pool,
2112                           const char *path,
2113                           const char *comment,
2114                           svn_boolean_t steal_lock,
2115                           svn_revnum_t revnum)
2116{
2117  SVN_ERR(writebuf_write_short_string(conn, pool, "( lock ( ", 9));
2118  SVN_ERR(write_tuple_cstring(conn, pool, path));
2119  SVN_ERR(write_tuple_start_list(conn, pool));
2120  SVN_ERR(write_tuple_cstring_opt(conn, pool, comment));
2121  SVN_ERR(write_tuple_end_list(conn, pool));
2122  SVN_ERR(write_tuple_boolean(conn, pool, steal_lock));
2123  SVN_ERR(write_tuple_start_list(conn, pool));
2124  SVN_ERR(write_tuple_revision_opt(conn, pool, revnum));
2125  SVN_ERR(write_tuple_end_list(conn, pool));
2126  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2127
2128  return SVN_NO_ERROR;
2129}
2130
2131svn_error_t *
2132svn_ra_svn__write_cmd_unlock(svn_ra_svn_conn_t *conn,
2133                             apr_pool_t *pool,
2134                             const char *path,
2135                             const char *token,
2136                             svn_boolean_t break_lock)
2137{
2138  SVN_ERR(writebuf_write_short_string(conn, pool, "( unlock ( ", 11));
2139  SVN_ERR(write_tuple_cstring(conn, pool, path));
2140  SVN_ERR(write_tuple_start_list(conn, pool));
2141  SVN_ERR(write_tuple_cstring_opt(conn, pool, token));
2142  SVN_ERR(write_tuple_end_list(conn, pool));
2143  SVN_ERR(write_tuple_boolean(conn, pool, break_lock));
2144  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2145
2146  return SVN_NO_ERROR;
2147}
2148
2149svn_error_t *
2150svn_ra_svn__write_cmd_get_lock(svn_ra_svn_conn_t *conn,
2151                               apr_pool_t *pool,
2152                               const char *path)
2153{
2154  SVN_ERR(writebuf_write_short_string(conn, pool, "( get-lock ( ", 13));
2155  SVN_ERR(write_tuple_cstring(conn, pool, path));
2156  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2157
2158  return SVN_NO_ERROR;
2159}
2160
2161svn_error_t *
2162svn_ra_svn__write_cmd_get_locks(svn_ra_svn_conn_t *conn,
2163                                apr_pool_t *pool,
2164                                const char *path,
2165                                svn_depth_t depth)
2166{
2167  SVN_ERR(writebuf_write_short_string(conn, pool, "( get-locks ( ", 14));
2168  SVN_ERR(write_tuple_cstring(conn, pool, path));
2169  SVN_ERR(write_tuple_start_list(conn, pool));
2170  SVN_ERR(write_tuple_depth(conn, pool, depth));
2171  SVN_ERR(write_tuple_end_list(conn, pool));
2172  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2173
2174  return SVN_NO_ERROR;
2175}
2176
2177svn_error_t *
2178svn_ra_svn__write_cmd_replay(svn_ra_svn_conn_t *conn,
2179                             apr_pool_t *pool,
2180                             svn_revnum_t rev,
2181                             svn_revnum_t low_water_mark,
2182                             svn_boolean_t send_deltas)
2183{
2184  SVN_ERR(writebuf_write_short_string(conn, pool, "( replay ( ", 11));
2185  SVN_ERR(write_tuple_revision(conn, pool, rev));
2186  SVN_ERR(write_tuple_revision(conn, pool, low_water_mark));
2187  SVN_ERR(write_tuple_boolean(conn, pool, send_deltas));
2188  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2189
2190  return SVN_NO_ERROR;
2191}
2192
2193svn_error_t *
2194svn_ra_svn__write_cmd_replay_range(svn_ra_svn_conn_t *conn,
2195                                   apr_pool_t *pool,
2196                                   svn_revnum_t start_revision,
2197                                   svn_revnum_t end_revision,
2198                                   svn_revnum_t low_water_mark,
2199                                   svn_boolean_t send_deltas)
2200{
2201  SVN_ERR(writebuf_write_short_string(conn, pool, "( replay-range ( ", 17));
2202  SVN_ERR(write_tuple_revision(conn, pool, start_revision));
2203  SVN_ERR(write_tuple_revision(conn, pool, end_revision));
2204  SVN_ERR(write_tuple_revision(conn, pool, low_water_mark));
2205  SVN_ERR(write_tuple_boolean(conn, pool, send_deltas));
2206  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2207
2208  return SVN_NO_ERROR;
2209}
2210
2211svn_error_t *
2212svn_ra_svn__write_cmd_get_deleted_rev(svn_ra_svn_conn_t *conn,
2213                                      apr_pool_t *pool,
2214                                      const char *path,
2215                                      svn_revnum_t peg_revision,
2216                                      svn_revnum_t end_revision)
2217{
2218  SVN_ERR(writebuf_write_short_string(conn, pool, "( get-deleted-rev ( ", 20));
2219  SVN_ERR(write_tuple_cstring(conn, pool, path));
2220  SVN_ERR(write_tuple_revision(conn, pool, peg_revision));
2221  SVN_ERR(write_tuple_revision(conn, pool, end_revision));
2222  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2223
2224  return SVN_NO_ERROR;
2225}
2226
2227svn_error_t *
2228svn_ra_svn__write_cmd_get_iprops(svn_ra_svn_conn_t *conn,
2229                                 apr_pool_t *pool,
2230                                 const char *path,
2231                                 svn_revnum_t revision)
2232{
2233  SVN_ERR(writebuf_write_short_string(conn, pool, "( get-iprops ( ", 15));
2234  SVN_ERR(write_tuple_cstring(conn, pool, path));
2235  SVN_ERR(write_tuple_start_list(conn, pool));
2236  SVN_ERR(write_tuple_revision_opt(conn, pool, revision));
2237  SVN_ERR(write_tuple_end_list(conn, pool));
2238  SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4));
2239
2240  return SVN_NO_ERROR;
2241}
2242
2243svn_error_t *
2244svn_ra_svn__write_cmd_finish_replay(svn_ra_svn_conn_t *conn,
2245                                    apr_pool_t *pool)
2246{
2247  return writebuf_write_short_string(conn, pool, "( finish-replay ( ) ) ", 22);
2248}
2249
2250svn_error_t *svn_ra_svn__write_cmd_response(svn_ra_svn_conn_t *conn,
2251                                            apr_pool_t *pool,
2252                                            const char *fmt, ...)
2253{
2254  va_list ap;
2255  svn_error_t *err;
2256
2257  SVN_ERR(writebuf_write_short_string(conn, pool, "( success ", 10));
2258  va_start(ap, fmt);
2259  err = vwrite_tuple(conn, pool, fmt, &ap);
2260  va_end(ap);
2261  return err ? svn_error_trace(err) : svn_ra_svn__end_list(conn, pool);
2262}
2263
2264svn_error_t *svn_ra_svn__write_cmd_failure(svn_ra_svn_conn_t *conn,
2265                                           apr_pool_t *pool, svn_error_t *err)
2266{
2267  char buffer[128];
2268  SVN_ERR(writebuf_write_short_string(conn, pool, "( failure ( ", 12));
2269  for (; err; err = err->child)
2270    {
2271      const char *msg;
2272
2273#ifdef SVN_ERR__TRACING
2274      if (svn_error__is_tracing_link(err))
2275        msg = err->message;
2276      else
2277#endif
2278        msg = svn_err_best_message(err, buffer, sizeof(buffer));
2279
2280      /* The message string should have been optional, but we can't
2281         easily change that, so marshal nonexistent messages as "". */
2282      SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "nccn",
2283                                      (apr_uint64_t) err->apr_err,
2284                                      msg ? msg : "",
2285                                      err->file ? err->file : "",
2286                                      (apr_uint64_t) err->line));
2287    }
2288  return writebuf_write_short_string(conn, pool, ") ) ", 4);
2289}
2290