]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_ra_svn/marshal.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / libsvn_ra_svn / marshal.c
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 \f
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. */
61 static apr_interval_time_t
62 get_timeout(svn_ra_svn_conn_t *conn)
63 {
64   return conn->block_handler ? 0 : -1;
65 }
66
67 /* --- CONNECTION INITIALIZATION --- */
68
69 svn_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
118 svn_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 */
129 svn_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
139 svn_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
158 svn_error_t *
159 svn_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
166 svn_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
172 int
173 svn_ra_svn_compression_level(svn_ra_svn_conn_t *conn)
174 {
175   return conn->compression_level;
176 }
177
178 apr_size_t
179 svn_ra_svn_zero_copy_limit(svn_ra_svn_conn_t *conn)
180 {
181   return conn->zero_copy_limit;
182 }
183
184 const char *svn_ra_svn_conn_remote_host(svn_ra_svn_conn_t *conn)
185 {
186   return conn->remote_ip;
187 }
188
189 void
190 svn_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
199 svn_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. */
208 static 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. */
255 static 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
265 static 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
288 static svn_error_t *
289 writebuf_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
303 static APR_INLINE svn_error_t *
304 writebuf_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. */
324 static 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. */
336 static 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" */
362 static 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. */
387 static 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
400 static APR_INLINE svn_error_t *
401 readbuf_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
409 static 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. */
420 static 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
449 static 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
489 static 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
506 svn_error_t *
507 svn_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
514 svn_error_t *
515 svn_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
532 svn_error_t *
533 svn_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
553 svn_error_t *
554 svn_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
564 svn_error_t *
565 svn_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
594 svn_error_t *
595 svn_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
609 svn_error_t *
610 svn_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
624 svn_error_t *
625 svn_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
636 static svn_error_t *
637 vwrite_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
644 static svn_error_t *
645 vwrite_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
651 static svn_error_t *
652 vwrite_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
659 static svn_error_t *
660 vwrite_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
666 static svn_error_t *
667 vwrite_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
674 static svn_error_t *
675 vwrite_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
681 static svn_error_t *
682 vwrite_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
689 static svn_error_t *
690 vwrite_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
698 static svn_error_t *
699 vwrite_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
704 static svn_error_t *
705 vwrite_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
711 static svn_error_t *
712 write_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
720 static svn_error_t *
721 write_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
728 static svn_error_t *
729 write_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
737 static svn_error_t *
738 write_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
745 static svn_error_t *
746 write_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
752 static svn_error_t *
753 write_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
759 static svn_error_t *
760 write_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
768 static svn_error_t *
769 write_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
778 static svn_error_t *
779 write_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
787 static svn_error_t *
788 write_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
796 static svn_error_t *
797 write_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
816 static svn_error_t *
817 write_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
834 static svn_error_t *
835 write_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
850 static svn_error_t *
851 write_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
865 static 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
910 svn_error_t *
911 svn_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. */
929 static 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. */
996 static 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. */
1082 static svn_error_t *
1083 read_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
1168 svn_error_t *
1169 svn_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
1182 svn_error_t *
1183 svn_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. */
1193 static 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
1289 svn_error_t *
1290 svn_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
1303 svn_error_t *
1304 svn_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
1322 svn_error_t *
1323 svn_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
1335 svn_error_t *
1336 svn_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
1363 svn_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
1378 svn_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
1430 svn_error_t *
1431 svn_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
1458 svn_error_t *
1459 svn_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
1522 svn_error_t *
1523 svn_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
1534 svn_error_t *
1535 svn_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
1550 svn_error_t *
1551 svn_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
1568 svn_error_t *
1569 svn_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
1585 svn_error_t *
1586 svn_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
1600 svn_error_t *
1601 svn_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
1614 svn_error_t *
1615 svn_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
1626 svn_error_t *
1627 svn_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
1639 svn_error_t *
1640 svn_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
1656 svn_error_t *
1657 svn_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
1671 svn_error_t *
1672 svn_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
1685 svn_error_t *
1686 svn_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
1701 svn_error_t *
1702 svn_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
1714 svn_error_t *
1715 svn_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
1728 svn_error_t *
1729 svn_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
1740 svn_error_t *
1741 svn_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
1756 svn_error_t *
1757 svn_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
1763 svn_error_t *
1764 svn_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
1770 svn_error_t *
1771 svn_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
1792 svn_error_t *
1793 svn_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
1804 svn_error_t *
1805 svn_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
1828 svn_error_t *
1829 svn_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
1835 svn_error_t *
1836 svn_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
1842 svn_error_t *
1843 svn_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
1854 svn_error_t *
1855 svn_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
1861 svn_error_t *
1862 svn_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
1873 svn_error_t *
1874 svn_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
1897 svn_error_t *
1898 svn_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
1913 svn_error_t *
1914 svn_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
1925 svn_error_t *
1926 svn_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
1939 svn_error_t *
1940 svn_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
1959 svn_error_t *
1960 svn_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
1983 svn_error_t *
1984 svn_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
2009 svn_error_t *
2010 svn_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
2029 svn_error_t *
2030 svn_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
2055 svn_error_t *
2056 svn_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
2071 svn_error_t *
2072 svn_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
2087 svn_error_t *
2088 svn_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
2109 svn_error_t *
2110 svn_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
2131 svn_error_t *
2132 svn_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
2149 svn_error_t *
2150 svn_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
2161 svn_error_t *
2162 svn_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
2177 svn_error_t *
2178 svn_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
2193 svn_error_t *
2194 svn_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
2211 svn_error_t *
2212 svn_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
2227 svn_error_t *
2228 svn_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
2243 svn_error_t *
2244 svn_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
2250 svn_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
2264 svn_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 }