xref: /freebsd-13-stable/contrib/subversion/subversion/libsvn_ra_svn/marshal.c (revision 7725780a600dd28ae2c61f2d51c76d41a070958b)
1 /*
2  * marshal.c :  Marshalling routines for Subversion protocol
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23 
24 
25 
26 #include <assert.h>
27 #include <stdlib.h>
28 
29 #define APR_WANT_STRFUNC
30 #include <apr_want.h>
31 #include <apr_general.h>
32 #include <apr_lib.h>
33 #include <apr_strings.h>
34 
35 #include "svn_hash.h"
36 #include "svn_types.h"
37 #include "svn_string.h"
38 #include "svn_error.h"
39 #include "svn_pools.h"
40 #include "svn_ra_svn.h"
41 #include "svn_private_config.h"
42 #include "svn_ctype.h"
43 #include "svn_sorts.h"
44 #include "svn_time.h"
45 
46 #include "ra_svn.h"
47 
48 #include "private/svn_string_private.h"
49 #include "private/svn_dep_compat.h"
50 #include "private/svn_error_private.h"
51 #include "private/svn_subr_private.h"
52 
53 #define svn_iswhitespace(c) ((c) == ' ' || (c) == '\n')
54 
55 /* If we receive data that *claims* to be followed by a very long string,
56  * we should not trust that claim right away. But everything up to 1 MB
57  * should be too small to be instrumental for a DOS attack. */
58 
59 #define SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD (0x100000)
60 
61 /* We don't use "words" longer than this in our protocol.  The longest word
62  * we are currently using is only about 16 chars long but we leave room for
63  * longer future capability and command names.  See read_item() to understand
64  * why MAX_WORD_LENGTH - 1 should be a multiple of 8.
65  */
66 #define MAX_WORD_LENGTH 25
67 
68 /* The generic parsers will use the following value to limit the recursion
69  * depth to some reasonable value.  The current protocol implementation
70  * actually uses only maximum item nesting level of around 5.  So, there is
71  * plenty of headroom here.
72  */
73 #define ITEM_NESTING_LIMIT 64
74 
75 /* The protocol words for booleans. */
76 static const svn_string_t str_true = SVN__STATIC_STRING("true");
77 static const svn_string_t str_false = SVN__STATIC_STRING("false");
78 
79 /* Return the APR socket timeout to be used for the connection depending
80  * on whether there is a blockage handler or zero copy has been activated. */
81 static apr_interval_time_t
get_timeout(svn_ra_svn_conn_t * conn)82 get_timeout(svn_ra_svn_conn_t *conn)
83 {
84   return conn->block_handler ? 0 : -1;
85 }
86 
87 /* --- Public / private API data conversion --- */
88 
89 void
svn_ra_svn__to_public_item(svn_ra_svn_item_t * target,const svn_ra_svn__item_t * source,apr_pool_t * result_pool)90 svn_ra_svn__to_public_item(svn_ra_svn_item_t *target,
91                            const svn_ra_svn__item_t *source,
92                            apr_pool_t *result_pool)
93 {
94   target->kind = source->kind;
95   switch (source->kind)
96     {
97       case SVN_RA_SVN_STRING:
98         target->u.string = svn_string_dup(&source->u.string, result_pool);
99         break;
100       case SVN_RA_SVN_NUMBER:
101         target->u.number = source->u.number;
102         break;
103       case SVN_RA_SVN_WORD:
104         target->u.word = source->u.word.data;
105         break;
106       case SVN_RA_SVN_LIST:
107         target->u.list = svn_ra_svn__to_public_array(&source->u.list,
108                                                      result_pool);
109         break;
110     }
111 }
112 
113 apr_array_header_t *
svn_ra_svn__to_public_array(const svn_ra_svn__list_t * source,apr_pool_t * result_pool)114 svn_ra_svn__to_public_array(const svn_ra_svn__list_t *source,
115                             apr_pool_t *result_pool)
116 {
117   apr_array_header_t *result = apr_array_make(result_pool, source->nelts,
118                                               sizeof(svn_ra_svn_item_t));
119 
120   int i;
121   for (i = 0; i < source->nelts; ++i)
122     {
123       svn_ra_svn_item_t *sub_target = apr_array_push(result);
124       svn_ra_svn__item_t *sub_source = &SVN_RA_SVN__LIST_ITEM(source, i);
125 
126       svn_ra_svn__to_public_item(sub_target, sub_source, result_pool);
127     }
128 
129   return result;
130 }
131 
132 void
svn_ra_svn__to_private_item(svn_ra_svn__item_t * target,const svn_ra_svn_item_t * source,apr_pool_t * result_pool)133 svn_ra_svn__to_private_item(svn_ra_svn__item_t *target,
134                             const svn_ra_svn_item_t *source,
135                             apr_pool_t *result_pool)
136 {
137   target->kind = source->kind;
138   switch (source->kind)
139     {
140       case SVN_RA_SVN_STRING:
141         target->u.string = *source->u.string;
142         break;
143       case SVN_RA_SVN_NUMBER:
144         target->u.number = source->u.number;
145         break;
146       case SVN_RA_SVN_WORD:
147         target->u.word.data = source->u.word;
148         target->u.word.len = strlen(source->u.word);
149         break;
150       case SVN_RA_SVN_LIST:
151         target->u.list = *svn_ra_svn__to_private_array(source->u.list,
152                                                        result_pool);
153         break;
154     }
155 }
156 
157 svn_ra_svn__list_t *
svn_ra_svn__to_private_array(const apr_array_header_t * source,apr_pool_t * result_pool)158 svn_ra_svn__to_private_array(const apr_array_header_t *source,
159                              apr_pool_t *result_pool)
160 {
161   int i;
162 
163   svn_ra_svn__list_t *result = apr_pcalloc(result_pool, sizeof(*result));
164   result->nelts = source->nelts;
165   result->items = apr_palloc(result_pool,
166                              source->nelts * sizeof(*result->items));
167 
168   for (i = 0; i < source->nelts; ++i)
169     {
170       svn_ra_svn__item_t *sub_target = &result->items[i];
171       svn_ra_svn_item_t *sub_source = &APR_ARRAY_IDX(source, i,
172                                                      svn_ra_svn_item_t);
173 
174       svn_ra_svn__to_private_item(sub_target, sub_source, result_pool);
175     }
176 
177   return result;
178 }
179 
180 /* --- CONNECTION INITIALIZATION --- */
181 
svn_ra_svn_create_conn5(apr_socket_t * sock,svn_stream_t * in_stream,svn_stream_t * out_stream,int compression_level,apr_size_t zero_copy_limit,apr_size_t error_check_interval,apr_uint64_t max_in,apr_uint64_t max_out,apr_pool_t * result_pool)182 svn_ra_svn_conn_t *svn_ra_svn_create_conn5(apr_socket_t *sock,
183                                            svn_stream_t *in_stream,
184                                            svn_stream_t *out_stream,
185                                            int compression_level,
186                                            apr_size_t zero_copy_limit,
187                                            apr_size_t error_check_interval,
188                                            apr_uint64_t max_in,
189                                            apr_uint64_t max_out,
190                                            apr_pool_t *result_pool)
191 {
192   svn_ra_svn_conn_t *conn;
193   void *mem = apr_palloc(result_pool, sizeof(*conn) + SVN_RA_SVN__PAGE_SIZE);
194   conn = (void*)APR_ALIGN((apr_uintptr_t)mem, SVN_RA_SVN__PAGE_SIZE);
195 
196   assert((sock && !in_stream && !out_stream)
197          || (!sock && in_stream && out_stream));
198 #ifdef SVN_HAVE_SASL
199   conn->sock = sock;
200   conn->encrypted = FALSE;
201 #endif
202   conn->session = NULL;
203   conn->read_ptr = conn->read_buf;
204   conn->read_end = conn->read_buf;
205   conn->write_pos = 0;
206   conn->written_since_error_check = 0;
207   conn->error_check_interval = error_check_interval;
208   conn->may_check_for_error = error_check_interval == 0;
209   conn->max_in = max_in;
210   conn->current_in = 0;
211   conn->max_out = max_out;
212   conn->current_out = 0;
213   conn->block_handler = NULL;
214   conn->block_baton = NULL;
215   conn->capabilities = apr_hash_make(result_pool);
216   conn->compression_level = compression_level;
217   conn->zero_copy_limit = zero_copy_limit;
218   conn->pool = result_pool;
219 
220   if (sock != NULL)
221     {
222       apr_sockaddr_t *sa;
223       conn->stream = svn_ra_svn__stream_from_sock(sock, result_pool);
224       if (!(apr_socket_addr_get(&sa, APR_REMOTE, sock) == APR_SUCCESS
225             && apr_sockaddr_ip_get(&conn->remote_ip, sa) == APR_SUCCESS))
226         conn->remote_ip = NULL;
227       svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn));
228     }
229   else
230     {
231       conn->stream = svn_ra_svn__stream_from_streams(in_stream, out_stream,
232                                                      result_pool);
233       conn->remote_ip = NULL;
234     }
235 
236   return conn;
237 }
238 
239 svn_error_t *
svn_ra_svn_set_capabilities(svn_ra_svn_conn_t * conn,const apr_array_header_t * list)240 svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn,
241                             const apr_array_header_t *list)
242 {
243   svn_ra_svn__list_t *internal
244     = svn_ra_svn__to_private_array(list, list->pool);
245   return svn_error_trace(svn_ra_svn__set_capabilities(conn, internal));
246 }
247 
248 svn_error_t *
svn_ra_svn__set_capabilities(svn_ra_svn_conn_t * conn,const svn_ra_svn__list_t * list)249 svn_ra_svn__set_capabilities(svn_ra_svn_conn_t *conn,
250                              const svn_ra_svn__list_t *list)
251 {
252   int i;
253   svn_ra_svn__item_t *item;
254   const char *word;
255 
256   for (i = 0; i < list->nelts; i++)
257     {
258       item = &SVN_RA_SVN__LIST_ITEM(list, i);
259       if (item->kind != SVN_RA_SVN_WORD)
260         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
261                                 _("Capability entry is not a word"));
262       word = apr_pstrmemdup(conn->pool, item->u.word.data, item->u.word.len);
263       apr_hash_set(conn->capabilities, word, item->u.word.len, word);
264     }
265   return SVN_NO_ERROR;
266 }
267 
268 int
svn_ra_svn__svndiff_version(svn_ra_svn_conn_t * conn)269 svn_ra_svn__svndiff_version(svn_ra_svn_conn_t *conn)
270 {
271   /* If we don't want to use compression, use the non-compressing
272    * "version 0" implementation. */
273   if (svn_ra_svn_compression_level(conn) <= 0)
274     return 0;
275 
276   /* Prefer SVNDIFF2 over SVNDIFF1. */
277   if (svn_ra_svn_has_capability(conn, SVN_RA_SVN_CAP_SVNDIFF2_ACCEPTED))
278     return 2;
279   if (svn_ra_svn_has_capability(conn, SVN_RA_SVN_CAP_SVNDIFF1))
280     return 1;
281 
282   /* The connection does not support SVNDIFF1/2; default to "version 0". */
283   return 0;
284 }
285 
286 apr_pool_t *
svn_ra_svn__get_pool(svn_ra_svn_conn_t * conn)287 svn_ra_svn__get_pool(svn_ra_svn_conn_t *conn)
288 {
289   return conn->pool;
290 }
291 
292 svn_error_t *
svn_ra_svn__set_shim_callbacks(svn_ra_svn_conn_t * conn,svn_delta_shim_callbacks_t * shim_callbacks)293 svn_ra_svn__set_shim_callbacks(svn_ra_svn_conn_t *conn,
294                                svn_delta_shim_callbacks_t *shim_callbacks)
295 {
296   conn->shim_callbacks = shim_callbacks;
297   return SVN_NO_ERROR;
298 }
299 
svn_ra_svn_has_capability(svn_ra_svn_conn_t * conn,const char * capability)300 svn_boolean_t svn_ra_svn_has_capability(svn_ra_svn_conn_t *conn,
301                                         const char *capability)
302 {
303   return (svn_hash_gets(conn->capabilities, capability) != NULL);
304 }
305 
306 int
svn_ra_svn_compression_level(svn_ra_svn_conn_t * conn)307 svn_ra_svn_compression_level(svn_ra_svn_conn_t *conn)
308 {
309   return conn->compression_level;
310 }
311 
312 apr_size_t
svn_ra_svn_zero_copy_limit(svn_ra_svn_conn_t * conn)313 svn_ra_svn_zero_copy_limit(svn_ra_svn_conn_t *conn)
314 {
315   return conn->zero_copy_limit;
316 }
317 
svn_ra_svn_conn_remote_host(svn_ra_svn_conn_t * conn)318 const char *svn_ra_svn_conn_remote_host(svn_ra_svn_conn_t *conn)
319 {
320   return conn->remote_ip;
321 }
322 
323 void
svn_ra_svn__set_block_handler(svn_ra_svn_conn_t * conn,ra_svn_block_handler_t handler,void * baton)324 svn_ra_svn__set_block_handler(svn_ra_svn_conn_t *conn,
325                               ra_svn_block_handler_t handler,
326                               void *baton)
327 {
328   conn->block_handler = handler;
329   conn->block_baton = baton;
330   svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn));
331 }
332 
svn_ra_svn__data_available(svn_ra_svn_conn_t * conn,svn_boolean_t * data_available)333 svn_error_t *svn_ra_svn__data_available(svn_ra_svn_conn_t *conn,
334                                        svn_boolean_t *data_available)
335 {
336   return svn_ra_svn__stream_data_available(conn->stream, data_available);
337 }
338 
339 void
svn_ra_svn__reset_command_io_counters(svn_ra_svn_conn_t * conn)340 svn_ra_svn__reset_command_io_counters(svn_ra_svn_conn_t *conn)
341 {
342   conn->current_in = 0;
343   conn->current_out = 0;
344 }
345 
346 
347 /* --- WRITE BUFFER MANAGEMENT --- */
348 
349 /* Return an error object if CONN exceeded its send or receive limits. */
350 static svn_error_t *
check_io_limits(svn_ra_svn_conn_t * conn)351 check_io_limits(svn_ra_svn_conn_t *conn)
352 {
353   if (conn->max_in && (conn->current_in > conn->max_in))
354     return svn_error_create(SVN_ERR_RA_SVN_REQUEST_SIZE, NULL,
355                             "The client request size exceeds the "
356                             "configured limit");
357 
358   if (conn->max_out && (conn->current_out > conn->max_out))
359     return svn_error_create(SVN_ERR_RA_SVN_RESPONSE_SIZE, NULL,
360                             "The server response size exceeds the "
361                             "configured limit");
362 
363   return SVN_NO_ERROR;
364 }
365 
366 /* Write data to socket or output file as appropriate. */
writebuf_output(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * data,apr_size_t len)367 static svn_error_t *writebuf_output(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
368                                     const char *data, apr_size_t len)
369 {
370   const char *end = data + len;
371   apr_size_t count;
372   apr_pool_t *subpool = NULL;
373   svn_ra_svn__session_baton_t *session = conn->session;
374 
375   /* Limit the size of the response, if a limit has been configured.
376    * This is to limit the server load in case users e.g. accidentally ran
377    * an export on the root folder. */
378   conn->current_out += len;
379   SVN_ERR(check_io_limits(conn));
380 
381   while (data < end)
382     {
383       count = end - data;
384 
385       if (session && session->callbacks && session->callbacks->cancel_func)
386         SVN_ERR((session->callbacks->cancel_func)(session->callbacks_baton));
387 
388       SVN_ERR(svn_ra_svn__stream_write(conn->stream, data, &count));
389       if (count == 0)
390         {
391           if (!subpool)
392             subpool = svn_pool_create(pool);
393           else
394             svn_pool_clear(subpool);
395           SVN_ERR(conn->block_handler(conn, subpool, conn->block_baton));
396         }
397       data += count;
398 
399       if (session)
400         {
401           const svn_ra_callbacks2_t *cb = session->callbacks;
402           session->bytes_written += count;
403 
404           if (cb && cb->progress_func)
405             (cb->progress_func)(session->bytes_written + session->bytes_read,
406                                 -1, cb->progress_baton, subpool);
407         }
408     }
409 
410   conn->written_since_error_check += len;
411   conn->may_check_for_error
412     = conn->written_since_error_check >= conn->error_check_interval;
413 
414   if (subpool)
415     svn_pool_destroy(subpool);
416   return SVN_NO_ERROR;
417 }
418 
419 /* Write data from the write buffer out to the socket. */
writebuf_flush(svn_ra_svn_conn_t * conn,apr_pool_t * pool)420 static svn_error_t *writebuf_flush(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
421 {
422   apr_size_t write_pos = conn->write_pos;
423 
424   /* Clear conn->write_pos first in case the block handler does a read. */
425   conn->write_pos = 0;
426   SVN_ERR(writebuf_output(conn, pool, conn->write_buf, write_pos));
427   return SVN_NO_ERROR;
428 }
429 
writebuf_write(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * data,apr_size_t len)430 static svn_error_t *writebuf_write(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
431                                    const char *data, apr_size_t len)
432 {
433   /* data >= 8k is sent immediately */
434   if (len >= sizeof(conn->write_buf) / 2)
435     {
436       if (conn->write_pos > 0)
437         SVN_ERR(writebuf_flush(conn, pool));
438 
439       return writebuf_output(conn, pool, data, len);
440     }
441 
442   /* ensure room for the data to add */
443   if (conn->write_pos + len > sizeof(conn->write_buf))
444     SVN_ERR(writebuf_flush(conn, pool));
445 
446   /* buffer the new data block as well */
447   memcpy(conn->write_buf + conn->write_pos, data, len);
448   conn->write_pos += len;
449 
450   return SVN_NO_ERROR;
451 }
452 
453 /* Write STRING_LITERAL, which is a string literal argument.
454 
455    Note: The purpose of the empty string "" in the macro definition is to
456    assert that STRING_LITERAL is in fact a string literal. Otherwise, the
457    string concatenation attempt should produce a compile-time error. */
458 #define writebuf_write_literal(conn, pool, string_literal) \
459     writebuf_write(conn, pool, string_literal, sizeof(string_literal "") - 1)
460 
461 static APR_INLINE svn_error_t *
writebuf_writechar(svn_ra_svn_conn_t * conn,apr_pool_t * pool,char data)462 writebuf_writechar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char data)
463 {
464   if (conn->write_pos < sizeof(conn->write_buf))
465   {
466     conn->write_buf[conn->write_pos] = data;
467     conn->write_pos++;
468 
469     return SVN_NO_ERROR;
470   }
471   else
472   {
473     char temp = data;
474     return writebuf_write(conn, pool, &temp, 1);
475   }
476 }
477 
478 /* --- READ BUFFER MANAGEMENT --- */
479 
480 /* Read bytes into DATA until either the read buffer is empty or
481  * we reach END. */
readbuf_drain(svn_ra_svn_conn_t * conn,char * data,char * end)482 static char *readbuf_drain(svn_ra_svn_conn_t *conn, char *data, char *end)
483 {
484   apr_ssize_t buflen, copylen;
485 
486   buflen = conn->read_end - conn->read_ptr;
487   copylen = (buflen < end - data) ? buflen : end - data;
488   memcpy(data, conn->read_ptr, copylen);
489   conn->read_ptr += copylen;
490   return data + copylen;
491 }
492 
493 /* Read data from socket or input file as appropriate. */
readbuf_input(svn_ra_svn_conn_t * conn,char * data,apr_size_t * len,apr_pool_t * pool)494 static svn_error_t *readbuf_input(svn_ra_svn_conn_t *conn, char *data,
495                                   apr_size_t *len, apr_pool_t *pool)
496 {
497   svn_ra_svn__session_baton_t *session = conn->session;
498 
499   /* First, give the user a chance to cancel the request before we do. */
500   if (session && session->callbacks && session->callbacks->cancel_func)
501     SVN_ERR((session->callbacks->cancel_func)(session->callbacks_baton));
502 
503   /* Limit our memory usage, if a limit has been configured.  Note that
504    * we first read the whole request into memory before process it. */
505   SVN_ERR(check_io_limits(conn));
506 
507   /* Actually fill the buffer. */
508   SVN_ERR(svn_ra_svn__stream_read(conn->stream, data, len));
509   if (*len == 0)
510     return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL);
511   conn->current_in += *len;
512 
513   if (session)
514     {
515       const svn_ra_callbacks2_t *cb = session->callbacks;
516       session->bytes_read += *len;
517 
518       if (cb && cb->progress_func)
519         (cb->progress_func)(session->bytes_read + session->bytes_written,
520                             -1, cb->progress_baton, pool);
521     }
522 
523   return SVN_NO_ERROR;
524 }
525 
526 /* Treat the next LEN input bytes from CONN as "read" */
readbuf_skip(svn_ra_svn_conn_t * conn,apr_uint64_t len)527 static svn_error_t *readbuf_skip(svn_ra_svn_conn_t *conn, apr_uint64_t len)
528 {
529   do
530   {
531     apr_size_t buflen = conn->read_end - conn->read_ptr;
532     apr_size_t copylen = (buflen < len) ? buflen : (apr_size_t)len;
533     conn->read_ptr += copylen;
534     len -= copylen;
535     if (len == 0)
536       break;
537 
538     buflen = sizeof(conn->read_buf);
539     SVN_ERR(svn_ra_svn__stream_read(conn->stream, conn->read_buf, &buflen));
540     if (buflen == 0)
541       return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL);
542 
543     conn->read_end = conn->read_buf + buflen;
544     conn->read_ptr = conn->read_buf;
545   }
546   while (len > 0);
547 
548   return SVN_NO_ERROR;
549 }
550 
551 /* Read data from the socket into the read buffer, which must be empty. */
readbuf_fill(svn_ra_svn_conn_t * conn,apr_pool_t * pool)552 static svn_error_t *readbuf_fill(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
553 {
554   apr_size_t len;
555 
556   SVN_ERR_ASSERT(conn->read_ptr == conn->read_end);
557 
558   /* Make sure we tell the other side everything we have to say before
559    * reading / waiting for an answer. */
560   if (conn->write_pos)
561     SVN_ERR(writebuf_flush(conn, pool));
562 
563   /* Fill (some of the) buffer. */
564   len = sizeof(conn->read_buf);
565   SVN_ERR(readbuf_input(conn, conn->read_buf, &len, pool));
566   conn->read_ptr = conn->read_buf;
567   conn->read_end = conn->read_buf + len;
568   return SVN_NO_ERROR;
569 }
570 
571 /* This is a hot function calling a cold function.  GCC and others tend to
572  * inline the cold sub-function instead of this hot one.  Therefore, be
573  * very insistent on lining this one.  It is not a correctness issue, though.
574  */
575 static SVN__FORCE_INLINE svn_error_t *
readbuf_getchar(svn_ra_svn_conn_t * conn,apr_pool_t * pool,char * result)576 readbuf_getchar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char *result)
577 {
578   if (conn->read_ptr == conn->read_end)
579     SVN_ERR(readbuf_fill(conn, pool));
580   *result = *conn->read_ptr++;
581   return SVN_NO_ERROR;
582 }
583 
readbuf_getchar_skip_whitespace(svn_ra_svn_conn_t * conn,apr_pool_t * pool,char * result)584 static svn_error_t *readbuf_getchar_skip_whitespace(svn_ra_svn_conn_t *conn,
585                                                     apr_pool_t *pool,
586                                                     char *result)
587 {
588   do
589     SVN_ERR(readbuf_getchar(conn, pool, result));
590   while (svn_iswhitespace(*result));
591   return SVN_NO_ERROR;
592 }
593 
594 /* Read the next LEN bytes from CONN and copy them to *DATA. */
readbuf_read(svn_ra_svn_conn_t * conn,apr_pool_t * pool,char * data,apr_size_t len)595 static svn_error_t *readbuf_read(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
596                                  char *data, apr_size_t len)
597 {
598   char *end = data + len;
599   apr_size_t count;
600 
601   /* Copy in an appropriate amount of data from the buffer. */
602   data = readbuf_drain(conn, data, end);
603 
604   /* Read large chunks directly into buffer. */
605   while (end - data > (apr_ssize_t)sizeof(conn->read_buf))
606     {
607       SVN_ERR(writebuf_flush(conn, pool));
608       count = end - data;
609       SVN_ERR(readbuf_input(conn, data, &count, pool));
610       data += count;
611     }
612 
613   while (end > data)
614     {
615       /* The remaining amount to read is small; fill the buffer and
616        * copy from that. */
617       SVN_ERR(readbuf_fill(conn, pool));
618       data = readbuf_drain(conn, data, end);
619     }
620 
621   return SVN_NO_ERROR;
622 }
623 
readbuf_skip_leading_garbage(svn_ra_svn_conn_t * conn,apr_pool_t * pool)624 static svn_error_t *readbuf_skip_leading_garbage(svn_ra_svn_conn_t *conn,
625                                                  apr_pool_t *pool)
626 {
627   char buf[256];  /* Must be smaller than sizeof(conn->read_buf) - 1. */
628   const char *p, *end;
629   apr_size_t len;
630   svn_boolean_t lparen = FALSE;
631 
632   SVN_ERR_ASSERT(conn->read_ptr == conn->read_end);
633   while (1)
634     {
635       /* Read some data directly from the connection input source. */
636       len = sizeof(buf);
637       SVN_ERR(readbuf_input(conn, buf, &len, pool));
638       end = buf + len;
639 
640       /* Scan the data for '(' WS with a very simple state machine. */
641       for (p = buf; p < end; p++)
642         {
643           if (lparen && svn_iswhitespace(*p))
644             break;
645           else
646             lparen = (*p == '(');
647         }
648       if (p < end)
649         break;
650     }
651 
652   /* p now points to the whitespace just after the left paren.  Fake
653    * up the left paren and then copy what we have into the read
654    * buffer. */
655   conn->read_buf[0] = '(';
656   memcpy(conn->read_buf + 1, p, end - p);
657   conn->read_ptr = conn->read_buf;
658   conn->read_end = conn->read_buf + 1 + (end - p);
659   return SVN_NO_ERROR;
660 }
661 
662 /* --- WRITING DATA ITEMS --- */
663 
write_number(svn_ra_svn_conn_t * conn,apr_pool_t * pool,apr_uint64_t number,char follow)664 static svn_error_t *write_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
665                                  apr_uint64_t number, char follow)
666 {
667   apr_size_t written;
668 
669   /* SVN_INT64_BUFFER_SIZE includes space for a terminating NUL that
670    * svn__ui64toa will always append. */
671   if (conn->write_pos + SVN_INT64_BUFFER_SIZE >= sizeof(conn->write_buf))
672     SVN_ERR(writebuf_flush(conn, pool));
673 
674   written = svn__ui64toa(conn->write_buf + conn->write_pos, number);
675   conn->write_buf[conn->write_pos + written] = follow;
676   conn->write_pos += written + 1;
677 
678   return SVN_NO_ERROR;
679 }
680 
681 svn_error_t *
svn_ra_svn__write_number(svn_ra_svn_conn_t * conn,apr_pool_t * pool,apr_uint64_t number)682 svn_ra_svn__write_number(svn_ra_svn_conn_t *conn,
683                          apr_pool_t *pool,
684                          apr_uint64_t number)
685 {
686   return write_number(conn, pool, number, ' ');
687 }
688 
689 /* Write string S of length LEN to TARGET and return the first position
690    after the written data.
691 
692    NOTE: This function assumes that TARGET has enough room for S, the LEN
693          prefix and the required separators.  The available buffer size
694          should be SVN_INT64_BUFFER_SIZE + LEN + 1 to avoid any chance of
695          overflow.
696  */
697 static char *
write_ncstring_quick(char * target,const char * s,apr_size_t len)698 write_ncstring_quick(char *target,
699                      const char *s,
700                      apr_size_t len)
701 {
702   /* Write string length. */
703   if (len < 10)
704     {
705       *target = (char)(len + '0');
706       target++;
707     }
708   else
709     {
710       target += svn__ui64toa(target, len);
711     }
712 
713   /* Separator & contents. */
714   target[0] = ':';
715   memcpy(target + 1, s, len);
716   target[len + 1] = ' ';
717 
718   /* First location after the string. */
719   return target + len + 2;
720 }
721 
722 
723 static svn_error_t *
svn_ra_svn__write_ncstring(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * s,apr_size_t len)724 svn_ra_svn__write_ncstring(svn_ra_svn_conn_t *conn,
725                            apr_pool_t *pool,
726                            const char *s,
727                            apr_size_t len)
728 {
729   /* Apart from LEN bytes of string contents, we need room for a number,
730      a colon and a space. */
731   apr_size_t max_fill = sizeof(conn->write_buf) - SVN_INT64_BUFFER_SIZE - 2;
732 
733   /* In most cases, there is enough left room in the WRITE_BUF
734      the we can serialize directly into it.  On platforms with
735      segmented memory, LEN might actually be close to APR_SIZE_MAX.
736      Blindly doing arithmetic on it might cause an overflow. */
737   if ((len <= max_fill) && (conn->write_pos <= max_fill - len))
738     {
739       /* Quick path. */
740       conn->write_pos = write_ncstring_quick(conn->write_buf
741                                                + conn->write_pos, s, len)
742                       - conn->write_buf;
743     }
744   else
745     {
746       /* Slower fallback code. */
747       SVN_ERR(write_number(conn, pool, len, ':'));
748 
749       SVN_ERR(writebuf_write(conn, pool, s, len));
750       SVN_ERR(writebuf_writechar(conn, pool, ' '));
751     }
752 
753   return SVN_NO_ERROR;
754 }
755 
756 svn_error_t *
svn_ra_svn__write_string(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * str)757 svn_ra_svn__write_string(svn_ra_svn_conn_t *conn,
758                          apr_pool_t *pool,
759                          const svn_string_t *str)
760 {
761   SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, str->data, str->len));
762   return SVN_NO_ERROR;
763 }
764 
765 svn_error_t *
svn_ra_svn__write_cstring(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * s)766 svn_ra_svn__write_cstring(svn_ra_svn_conn_t *conn,
767                           apr_pool_t *pool,
768                           const char *s)
769 {
770   SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, s, strlen(s)));
771   return SVN_NO_ERROR;
772 }
773 
774 svn_error_t *
svn_ra_svn__write_word(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * word)775 svn_ra_svn__write_word(svn_ra_svn_conn_t *conn,
776                        apr_pool_t *pool,
777                        const char *word)
778 {
779   SVN_ERR(writebuf_write(conn, pool, word, strlen(word)));
780   SVN_ERR(writebuf_writechar(conn, pool, ' '));
781 
782   return SVN_NO_ERROR;
783 }
784 
785 svn_error_t *
svn_ra_svn__write_boolean(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_boolean_t value)786 svn_ra_svn__write_boolean(svn_ra_svn_conn_t *conn,
787                           apr_pool_t *pool,
788                           svn_boolean_t value)
789 {
790   if (value)
791     SVN_ERR(writebuf_write_literal(conn, pool, "true "));
792   else
793     SVN_ERR(writebuf_write_literal(conn, pool, "false "));
794 
795   return SVN_NO_ERROR;
796 }
797 
798 svn_error_t *
svn_ra_svn__write_proplist(svn_ra_svn_conn_t * conn,apr_pool_t * pool,apr_hash_t * props)799 svn_ra_svn__write_proplist(svn_ra_svn_conn_t *conn,
800                            apr_pool_t *pool,
801                            apr_hash_t *props)
802 {
803   apr_hash_index_t *hi;
804   const char *propname;
805   svn_string_t *propval;
806   apr_size_t len;
807 
808   /* One might use an iterpool here but that would only be used when the
809      send buffer gets flushed and only by the CONN's progress callback.
810      That should happen at most once for typical prop lists and even then
811      use only a few bytes at best.
812    */
813   if (props)
814     for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
815       {
816         apr_hash_this(hi, (const void **)&propname,
817                           (apr_ssize_t *)&len,
818                           (void **)&propval);
819 
820         SVN_ERR(svn_ra_svn__start_list(conn, pool));
821         SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, propname, len));
822         SVN_ERR(svn_ra_svn__write_string(conn, pool, propval));
823         SVN_ERR(svn_ra_svn__end_list(conn, pool));
824       }
825 
826   return SVN_NO_ERROR;
827 }
828 
829 svn_error_t *
svn_ra_svn__start_list(svn_ra_svn_conn_t * conn,apr_pool_t * pool)830 svn_ra_svn__start_list(svn_ra_svn_conn_t *conn,
831                        apr_pool_t *pool)
832 {
833   if (conn->write_pos + 2 <= sizeof(conn->write_buf))
834     {
835       conn->write_buf[conn->write_pos] = '(';
836       conn->write_buf[conn->write_pos+1] = ' ';
837       conn->write_pos += 2;
838       return SVN_NO_ERROR;
839     }
840 
841   return writebuf_write(conn, pool, "( ", 2);
842 }
843 
844 svn_error_t *
svn_ra_svn__end_list(svn_ra_svn_conn_t * conn,apr_pool_t * pool)845 svn_ra_svn__end_list(svn_ra_svn_conn_t *conn,
846                      apr_pool_t *pool)
847 {
848   if (conn->write_pos + 2 <= sizeof(conn->write_buf))
849   {
850     conn->write_buf[conn->write_pos] = ')';
851     conn->write_buf[conn->write_pos+1] = ' ';
852     conn->write_pos += 2;
853     return SVN_NO_ERROR;
854   }
855 
856   return writebuf_write(conn, pool, ") ", 2);
857 }
858 
859 svn_error_t *
svn_ra_svn__flush(svn_ra_svn_conn_t * conn,apr_pool_t * pool)860 svn_ra_svn__flush(svn_ra_svn_conn_t *conn,
861                   apr_pool_t *pool)
862 {
863   SVN_ERR(writebuf_flush(conn, pool));
864   conn->may_check_for_error = TRUE;
865 
866   return SVN_NO_ERROR;
867 }
868 
869 /* --- WRITING TUPLES --- */
870 
871 static svn_error_t *
vwrite_tuple_cstring(svn_ra_svn_conn_t * conn,apr_pool_t * pool,va_list * ap)872 vwrite_tuple_cstring(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
873 {
874   const char *cstr = va_arg(*ap, const char *);
875   SVN_ERR_ASSERT(cstr);
876   return svn_ra_svn__write_cstring(conn, pool, cstr);
877 }
878 
879 static svn_error_t *
vwrite_tuple_cstring_opt(svn_ra_svn_conn_t * conn,apr_pool_t * pool,va_list * ap)880 vwrite_tuple_cstring_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
881 {
882   const char *cstr = va_arg(*ap, const char *);
883   return cstr ? svn_ra_svn__write_cstring(conn, pool, cstr) : SVN_NO_ERROR;
884 }
885 
886 static svn_error_t *
vwrite_tuple_string(svn_ra_svn_conn_t * conn,apr_pool_t * pool,va_list * ap)887 vwrite_tuple_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
888 {
889   const svn_string_t *str = va_arg(*ap, const svn_string_t *);
890   SVN_ERR_ASSERT(str);
891   return svn_ra_svn__write_string(conn, pool, str);
892 }
893 
894 static svn_error_t *
vwrite_tuple_string_opt(svn_ra_svn_conn_t * conn,apr_pool_t * pool,va_list * ap)895 vwrite_tuple_string_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
896 {
897   const svn_string_t *str = va_arg(*ap, const svn_string_t *);
898   return str ? svn_ra_svn__write_string(conn, pool, str) : SVN_NO_ERROR;
899 }
900 
901 static svn_error_t *
vwrite_tuple_word(svn_ra_svn_conn_t * conn,apr_pool_t * pool,va_list * ap)902 vwrite_tuple_word(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
903 {
904   const char *cstr = va_arg(*ap, const char *);
905   SVN_ERR_ASSERT(cstr);
906   return svn_ra_svn__write_word(conn, pool, cstr);
907 }
908 
909 static svn_error_t *
vwrite_tuple_word_opt(svn_ra_svn_conn_t * conn,apr_pool_t * pool,va_list * ap)910 vwrite_tuple_word_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
911 {
912   const char *cstr = va_arg(*ap, const char *);
913   return cstr ? svn_ra_svn__write_word(conn, pool, cstr) : SVN_NO_ERROR;
914 }
915 
916 static svn_error_t *
vwrite_tuple_revision(svn_ra_svn_conn_t * conn,apr_pool_t * pool,va_list * ap)917 vwrite_tuple_revision(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
918 {
919   svn_revnum_t rev = va_arg(*ap, svn_revnum_t);
920   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
921   return svn_ra_svn__write_number(conn, pool, rev);
922 }
923 
924 static svn_error_t *
vwrite_tuple_revision_opt(svn_ra_svn_conn_t * conn,apr_pool_t * pool,va_list * ap)925 vwrite_tuple_revision_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
926 {
927   svn_revnum_t rev = va_arg(*ap, svn_revnum_t);
928   return SVN_IS_VALID_REVNUM(rev)
929        ? svn_ra_svn__write_number(conn, pool, rev)
930        : SVN_NO_ERROR;
931 }
932 
933 static svn_error_t *
vwrite_tuple_number(svn_ra_svn_conn_t * conn,apr_pool_t * pool,va_list * ap)934 vwrite_tuple_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
935 {
936   return svn_ra_svn__write_number(conn, pool, va_arg(*ap, apr_uint64_t));
937 }
938 
939 static svn_error_t *
vwrite_tuple_boolean(svn_ra_svn_conn_t * conn,apr_pool_t * pool,va_list * ap)940 vwrite_tuple_boolean(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
941 {
942   return svn_ra_svn__write_boolean(conn, pool, va_arg(*ap, svn_boolean_t));
943 }
944 
945 static svn_error_t *
write_tuple_cstring(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * cstr)946 write_tuple_cstring(svn_ra_svn_conn_t *conn,
947                     apr_pool_t *pool,
948                     const char *cstr)
949 {
950   SVN_ERR_ASSERT(cstr);
951   return svn_ra_svn__write_cstring(conn, pool, cstr);
952 }
953 
954 static svn_error_t *
write_tuple_cstring_opt(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * cstr)955 write_tuple_cstring_opt(svn_ra_svn_conn_t *conn,
956                         apr_pool_t *pool,
957                         const char *cstr)
958 {
959   return cstr ? svn_ra_svn__write_cstring(conn, pool, cstr) : SVN_NO_ERROR;
960 }
961 
962 static svn_error_t *
write_tuple_string(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * str)963 write_tuple_string(svn_ra_svn_conn_t *conn,
964                    apr_pool_t *pool,
965                    const svn_string_t *str)
966 {
967   SVN_ERR_ASSERT(str);
968   return svn_ra_svn__write_string(conn, pool, str);
969 }
970 
971 static svn_error_t *
write_tuple_string_opt(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * str)972 write_tuple_string_opt(svn_ra_svn_conn_t *conn,
973                        apr_pool_t *pool,
974                        const svn_string_t *str)
975 {
976   return str ? svn_ra_svn__write_string(conn, pool, str) : SVN_NO_ERROR;
977 }
978 
979 /* Optimized sending code for the "(s?)" pattern. */
980 static svn_error_t *
write_tuple_string_opt_list(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * str)981 write_tuple_string_opt_list(svn_ra_svn_conn_t *conn,
982                             apr_pool_t *pool,
983                             const svn_string_t *str)
984 {
985   apr_size_t max_fill;
986 
987   /* Special case. */
988   if (!str)
989     return writebuf_write(conn, pool, "( ) ", 4);
990 
991   /* If this how far we can fill the WRITE_BUF with string data and still
992      guarantee that the length info will fit in as well. */
993   max_fill = sizeof(conn->write_buf)
994            - 2                       /* open list */
995            - SVN_INT64_BUFFER_SIZE   /* string length + separator */
996            - 2;                      /* close list */
997 
998    /* On platforms with segmented memory, STR->LEN might actually be
999       close to APR_SIZE_MAX.  Blindly doing arithmetic on it might
1000       cause an overflow. */
1001   if ((str->len <= max_fill) && (conn->write_pos <= max_fill - str->len))
1002     {
1003       /* Quick path. */
1004       /* Open list. */
1005       char *p = conn->write_buf + conn->write_pos;
1006       p[0] = '(';
1007       p[1] = ' ';
1008 
1009       /* Write string. */
1010       p = write_ncstring_quick(p + 2, str->data, str->len);
1011 
1012       /* Close list. */
1013       p[0] = ')';
1014       p[1] = ' ';
1015       conn->write_pos = p + 2 - conn->write_buf;
1016     }
1017   else
1018     {
1019       /* Standard code path (fallback). */
1020       SVN_ERR(svn_ra_svn__start_list(conn, pool));
1021       SVN_ERR(svn_ra_svn__write_string(conn, pool, str));
1022       SVN_ERR(svn_ra_svn__end_list(conn, pool));
1023     }
1024 
1025   return SVN_NO_ERROR;
1026 }
1027 
1028 static svn_error_t *
write_tuple_start_list(svn_ra_svn_conn_t * conn,apr_pool_t * pool)1029 write_tuple_start_list(svn_ra_svn_conn_t *conn,
1030                        apr_pool_t *pool)
1031 {
1032   return svn_ra_svn__start_list(conn, pool);
1033 }
1034 
1035 static svn_error_t *
write_tuple_end_list(svn_ra_svn_conn_t * conn,apr_pool_t * pool)1036 write_tuple_end_list(svn_ra_svn_conn_t *conn,
1037                      apr_pool_t *pool)
1038 {
1039   return svn_ra_svn__end_list(conn, pool);
1040 }
1041 
1042 static svn_error_t *
write_tuple_revision(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t rev)1043 write_tuple_revision(svn_ra_svn_conn_t *conn,
1044                      apr_pool_t *pool,
1045                      svn_revnum_t rev)
1046 {
1047   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
1048   return svn_ra_svn__write_number(conn, pool, rev);
1049 }
1050 
1051 static svn_error_t *
write_tuple_revision_opt(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t rev)1052 write_tuple_revision_opt(svn_ra_svn_conn_t *conn,
1053                          apr_pool_t *pool,
1054                          svn_revnum_t rev)
1055 {
1056   return SVN_IS_VALID_REVNUM(rev)
1057        ? svn_ra_svn__write_number(conn, pool, rev)
1058        : SVN_NO_ERROR;
1059 }
1060 
1061 static svn_error_t *
write_tuple_boolean(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_boolean_t value)1062 write_tuple_boolean(svn_ra_svn_conn_t *conn,
1063                     apr_pool_t *pool,
1064                     svn_boolean_t value)
1065 {
1066   return svn_ra_svn__write_boolean(conn, pool, value);
1067 }
1068 
1069 static svn_error_t *
write_tuple_depth(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_depth_t depth)1070 write_tuple_depth(svn_ra_svn_conn_t *conn,
1071                   apr_pool_t *pool,
1072                   svn_depth_t depth)
1073 {
1074   return svn_ra_svn__write_word(conn, pool, svn_depth_to_word(depth));
1075 }
1076 
1077 
1078 static svn_error_t *
write_cmd_add_node(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,const svn_string_t * parent_token,const svn_string_t * token,const char * copy_path,svn_revnum_t copy_rev)1079 write_cmd_add_node(svn_ra_svn_conn_t *conn,
1080                    apr_pool_t *pool,
1081                    const char *path,
1082                    const svn_string_t *parent_token,
1083                    const svn_string_t *token,
1084                    const char *copy_path,
1085                    svn_revnum_t copy_rev)
1086 {
1087   SVN_ERR(write_tuple_cstring(conn, pool, path));
1088   SVN_ERR(write_tuple_string(conn, pool, parent_token));
1089   SVN_ERR(write_tuple_string(conn, pool, token));
1090   SVN_ERR(write_tuple_start_list(conn, pool));
1091   SVN_ERR(write_tuple_cstring_opt(conn, pool, copy_path));
1092   SVN_ERR(write_tuple_revision_opt(conn, pool, copy_rev));
1093   SVN_ERR(write_tuple_end_list(conn, pool));
1094 
1095   return SVN_NO_ERROR;
1096 }
1097 
1098 static svn_error_t *
write_cmd_open_node(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,const svn_string_t * parent_token,const svn_string_t * token,svn_revnum_t rev)1099 write_cmd_open_node(svn_ra_svn_conn_t *conn,
1100                     apr_pool_t *pool,
1101                     const char *path,
1102                     const svn_string_t *parent_token,
1103                     const svn_string_t *token,
1104                     svn_revnum_t rev)
1105 {
1106   SVN_ERR(write_tuple_cstring(conn, pool, path));
1107   SVN_ERR(write_tuple_string(conn, pool, parent_token));
1108   SVN_ERR(write_tuple_string(conn, pool, token));
1109   SVN_ERR(write_tuple_start_list(conn, pool));
1110   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
1111   SVN_ERR(write_tuple_end_list(conn, pool));
1112 
1113   return SVN_NO_ERROR;
1114 }
1115 
1116 static svn_error_t *
write_cmd_change_node_prop(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * token,const char * name,const svn_string_t * value)1117 write_cmd_change_node_prop(svn_ra_svn_conn_t *conn,
1118                            apr_pool_t *pool,
1119                            const svn_string_t *token,
1120                            const char *name,
1121                            const svn_string_t *value)
1122 {
1123   SVN_ERR(write_tuple_string(conn, pool, token));
1124   SVN_ERR(write_tuple_cstring(conn, pool, name));
1125   SVN_ERR(write_tuple_string_opt_list(conn, pool, value));
1126 
1127   return SVN_NO_ERROR;
1128 }
1129 
1130 static svn_error_t *
write_cmd_absent_node(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,const svn_string_t * token)1131 write_cmd_absent_node(svn_ra_svn_conn_t *conn,
1132                       apr_pool_t *pool,
1133                       const char *path,
1134                       const svn_string_t *token)
1135 {
1136   SVN_ERR(write_tuple_cstring(conn, pool, path));
1137   SVN_ERR(write_tuple_string(conn, pool, token));
1138 
1139   return SVN_NO_ERROR;
1140 }
1141 
1142 
1143 
1144 
vwrite_tuple(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * fmt,va_list * ap)1145 static svn_error_t *vwrite_tuple(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1146                                  const char *fmt, va_list *ap)
1147 {
1148   svn_boolean_t opt = FALSE;
1149 
1150   if (*fmt == '!')
1151     fmt++;
1152   else
1153     SVN_ERR(svn_ra_svn__start_list(conn, pool));
1154   for (; *fmt; fmt++)
1155     {
1156       if (*fmt == 'c')
1157         SVN_ERR(opt ? vwrite_tuple_cstring_opt(conn, pool, ap)
1158                     : vwrite_tuple_cstring(conn, pool, ap));
1159       else if (*fmt == 's')
1160         SVN_ERR(opt ? vwrite_tuple_string_opt(conn, pool, ap)
1161                     : vwrite_tuple_string(conn, pool, ap));
1162       else if (*fmt == '(' && !opt)
1163         {
1164           /* Optional sub-tuples are not supported.
1165            * If OPT was set, we would fall through to the malfunction call. */
1166           SVN_ERR(write_tuple_start_list(conn, pool));
1167         }
1168       else if (*fmt == ')')
1169         {
1170           SVN_ERR(write_tuple_end_list(conn, pool));
1171 
1172           /* OPT could not have been set when opening the list (see above),
1173            * hence this is correct and handles nested tuples just fine. */
1174           opt = FALSE;
1175         }
1176       else if (*fmt == '?')
1177         opt = TRUE;
1178       else if (*fmt == 'w')
1179         SVN_ERR(opt ? vwrite_tuple_word_opt(conn, pool, ap)
1180                     : vwrite_tuple_word(conn, pool, ap));
1181       else if (*fmt == 'r')
1182         SVN_ERR(opt ? vwrite_tuple_revision_opt(conn, pool, ap)
1183                     : vwrite_tuple_revision(conn, pool, ap));
1184       else if (*fmt == 'n' && !opt)
1185         SVN_ERR(vwrite_tuple_number(conn, pool, ap));
1186       else if (*fmt == 'b' && !opt)
1187         SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
1188       else if (*fmt == '!' && !*(fmt + 1))
1189         return SVN_NO_ERROR;
1190       else
1191         SVN_ERR_MALFUNCTION();
1192     }
1193   SVN_ERR(svn_ra_svn__end_list(conn, pool));
1194   return SVN_NO_ERROR;
1195 }
1196 
1197 svn_error_t *
svn_ra_svn__write_tuple(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * fmt,...)1198 svn_ra_svn__write_tuple(svn_ra_svn_conn_t *conn,
1199                         apr_pool_t *pool,
1200                         const char *fmt, ...)
1201 {
1202   svn_error_t *err;
1203   va_list ap;
1204 
1205   va_start(ap, fmt);
1206   err = vwrite_tuple(conn, pool, fmt, &ap);
1207   va_end(ap);
1208   return err;
1209 }
1210 
1211 /* --- READING DATA ITEMS --- */
1212 
1213 /* Read LEN bytes from CONN into already-allocated structure ITEM.
1214  * Afterwards, *ITEM is of type 'SVN_RA_SVN_STRING', and its string
1215  * data is allocated in POOL. */
read_string(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_ra_svn__item_t * item,apr_uint64_t len64)1216 static svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1217                                 svn_ra_svn__item_t *item, apr_uint64_t len64)
1218 {
1219   apr_size_t len = (apr_size_t)len64;
1220   apr_size_t readbuf_len;
1221   char *dest;
1222   apr_size_t buflen;
1223 
1224   /* We can't store strings longer than the maximum size of apr_size_t,
1225    * so check before using the truncated value. */
1226   if (len64 > APR_SIZE_MAX)
1227     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1228                             _("String length larger than maximum"));
1229 
1230   buflen = conn->read_end - conn->read_ptr;
1231   /* Shorter strings can be copied directly from the read buffer. */
1232   if (len <= buflen)
1233     {
1234       item->kind = SVN_RA_SVN_STRING;
1235       item->u.string.data = apr_pstrmemdup(pool, conn->read_ptr, len);
1236       item->u.string.len = len;
1237       conn->read_ptr += len;
1238     }
1239   else
1240     {
1241       svn_stringbuf_t *stringbuf;
1242 
1243       /* Don't even attempt to read anything that exceeds the I/O limit.
1244        * So, we can terminate the transfer at an early point, saving
1245        * everybody's time and resources. */
1246       if (conn->max_in && (conn->max_in < len64))
1247         return svn_error_create(SVN_ERR_RA_SVN_REQUEST_SIZE, NULL,
1248                                 "The client request size exceeds the "
1249                                 "configured limit");
1250 
1251       /* Read the string in chunks.  The chunk size is large enough to avoid
1252        * re-allocation in typical cases, and small enough to ensure we do
1253        * not pre-allocate an unreasonable amount of memory if (perhaps due
1254        * to network data corruption or a DOS attack), we receive a bogus
1255        * claim that a very long string is going to follow.  In that case, we
1256        * start small and wait for all that data to actually show up.  This
1257        * does not fully prevent DOS attacks but makes them harder (you have
1258        * to actually send gigabytes of data). */
1259       stringbuf = svn_stringbuf_create_empty(pool);
1260 
1261       /* Read string data directly into the string structure.
1262        * Do it iteratively.  */
1263       do
1264         {
1265           /* Determine length of chunk to read and re-alloc the buffer. */
1266           readbuf_len
1267             = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD
1268                   ? len
1269                   : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD;
1270 
1271           svn_stringbuf_ensure(stringbuf, stringbuf->len + readbuf_len);
1272           dest = stringbuf->data + stringbuf->len;
1273 
1274           /* read data & update length info */
1275           SVN_ERR(readbuf_read(conn, pool, dest, readbuf_len));
1276 
1277           stringbuf->len += readbuf_len;
1278           len -= readbuf_len;
1279         }
1280       while (len);
1281 
1282       /* zero-terminate the string */
1283       stringbuf->data[stringbuf->len] = '\0';
1284 
1285       /* Return the string properly wrapped into an RA_SVN item. */
1286       item->kind = SVN_RA_SVN_STRING;
1287       item->u.string.data = stringbuf->data;
1288       item->u.string.len = stringbuf->len;
1289     }
1290 
1291   return SVN_NO_ERROR;
1292 }
1293 
1294 /* Given the first non-whitespace character FIRST_CHAR, read an item
1295  * into the already allocated structure ITEM.  LEVEL should be set
1296  * to 0 for the first call and is used to enforce a recursion limit
1297  * on the parser. */
read_item(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_ra_svn__item_t * item,char first_char,int level)1298 static svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1299                               svn_ra_svn__item_t *item, char first_char,
1300                               int level)
1301 {
1302   char c = first_char;
1303   apr_uint64_t val;
1304   svn_ra_svn__item_t *listitem;
1305 
1306   if (++level >= ITEM_NESTING_LIMIT)
1307     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1308                             _("Items are nested too deeply"));
1309 
1310 
1311   /* Determine the item type and read it in.  Make sure that c is the
1312    * first character at the end of the item so we can test to make
1313    * sure it's whitespace. */
1314   if (svn_ctype_isdigit(c))
1315     {
1316       /* It's a number or a string.  Read the number part, either way. */
1317       val = c - '0';
1318       while (1)
1319         {
1320           apr_uint64_t prev_val = val;
1321           SVN_ERR(readbuf_getchar(conn, pool, &c));
1322           if (!svn_ctype_isdigit(c))
1323             break;
1324           val = val * 10 + (c - '0');
1325           /* val wrapped past maximum value? */
1326           if ((prev_val >= (APR_UINT64_MAX / 10))
1327               && (val < APR_UINT64_MAX - 10))
1328             return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1329                                     _("Number is larger than maximum"));
1330         }
1331       if (c == ':')
1332         {
1333           /* It's a string. */
1334           SVN_ERR(read_string(conn, pool, item, val));
1335           SVN_ERR(readbuf_getchar(conn, pool, &c));
1336         }
1337       else
1338         {
1339           /* It's a number. */
1340           item->kind = SVN_RA_SVN_NUMBER;
1341           item->u.number = val;
1342         }
1343     }
1344   else if (svn_ctype_isalpha(c))
1345     {
1346       /* It's a word.  Read it into a buffer of limited size. */
1347       char *buffer = apr_palloc(pool, MAX_WORD_LENGTH + 1);
1348       char *end = buffer + MAX_WORD_LENGTH;
1349       char *p = buffer + 1;
1350 
1351       buffer[0] = c;
1352       if (conn->read_ptr + MAX_WORD_LENGTH <= conn->read_end)
1353         {
1354           /* Fast path: we can simply take a chunk from the read
1355            * buffer and inspect it with no overflow checks etc.
1356            *
1357            * Copying these 24 bytes unconditionally is also faster
1358            * than a variable-sized memcpy.  Note that P is at BUFFER[1].
1359            */
1360           memcpy(p, conn->read_ptr, MAX_WORD_LENGTH - 1);
1361           *end = 0;
1362 
1363           /* This will terminate at P == END because of *END == NUL. */
1364           while (svn_ctype_isalnum(*p) || *p == '-')
1365             ++p;
1366 
1367           /* Only now do we mark data as actually read. */
1368           conn->read_ptr += p - buffer;
1369         }
1370       else
1371         {
1372           /* Slow path. Byte-by-byte copying and checking for
1373            * input and output buffer boundaries. */
1374           for (p = buffer + 1; p != end; ++p)
1375             {
1376               SVN_ERR(readbuf_getchar(conn, pool, p));
1377               if (!svn_ctype_isalnum(*p) && *p != '-')
1378                 break;
1379             }
1380         }
1381 
1382       if (p == end)
1383         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1384                                 _("Word is too long"));
1385 
1386       c = *p;
1387       *p = '\0';
1388 
1389       /* Store the word in ITEM. */
1390       item->kind = SVN_RA_SVN_WORD;
1391       item->u.word.data = buffer;
1392       item->u.word.len = p - buffer;
1393     }
1394   else if (c == '(')
1395     {
1396       /* The largest struct that the protocol currently defines has 10
1397        * elements (log-entry) and add some headroom for future extensions.
1398        * At a maximum nesting level of 64 this use <= 18kB of stack.
1399        *
1400        * All system-defined data structures will fit into this and will be
1401        * copied into ITEM after a single apr_palloc with no over-provision.
1402        * Unbounded lists with more than 12 but less than 25 entries will
1403        * also see only a single allocation from POOL.  However, there will
1404        * be some over-provision.  Longer lists will see log N resizes and
1405        * O(N) total cost.
1406        */
1407       svn_ra_svn__item_t stack_items[12];
1408       svn_ra_svn__item_t *items = stack_items;
1409       int capacity = sizeof(stack_items) / sizeof(stack_items[0]);
1410       int count = 0;
1411 
1412       /* Read in the list items. */
1413       item->kind = SVN_RA_SVN_LIST;
1414       while (1)
1415         {
1416           SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1417           if (c == ')')
1418             break;
1419 
1420           /* Auto-expand the list. */
1421           if (count == capacity)
1422             {
1423               svn_ra_svn__item_t *new_items
1424                 = apr_palloc(pool, 2 * capacity * sizeof(*new_items));
1425               memcpy(new_items, items, capacity * sizeof(*new_items));
1426               items = new_items;
1427               capacity = 2 * capacity;
1428             }
1429 
1430           listitem = &items[count];
1431           ++count;
1432 
1433           SVN_ERR(read_item(conn, pool, listitem, c, level));
1434         }
1435 
1436       /* Store the list in ITEM - if not empty (= default). */
1437       if (count)
1438         {
1439           item->u.list.nelts = count;
1440 
1441           /* If we haven't allocated from POOL, yet, do it now. */
1442           if (items == stack_items)
1443             item->u.list.items = apr_pmemdup(pool, items,
1444                                              count * sizeof(*items));
1445           else
1446             item->u.list.items = items;
1447         }
1448       else
1449         {
1450           item->u.list.items = NULL;
1451           item->u.list.nelts = 0;
1452         }
1453 
1454       SVN_ERR(readbuf_getchar(conn, pool, &c));
1455     }
1456 
1457   if (!svn_iswhitespace(c))
1458     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1459                             _("Malformed network data"));
1460   return SVN_NO_ERROR;
1461 }
1462 
1463 /* Given the first non-whitespace character FIRST_CHAR, read the first
1464  * command (word) encountered in CONN into *ITEM.  If ITEM is NULL, skip
1465  * to the end of the current list.  Use POOL for allocations. */
1466 static svn_error_t *
read_command_only(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char ** item,char first_char)1467 read_command_only(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1468                   const char **item, char first_char)
1469 {
1470   char c = first_char;
1471 
1472   /* Determine the item type and read it in.  Make sure that c is the
1473   * first character at the end of the item so we can test to make
1474   * sure it's whitespace. */
1475   if (svn_ctype_isdigit(c))
1476     {
1477       /* It's a number or a string.  Read the number part, either way. */
1478       apr_uint64_t val, prev_val=0;
1479       val = c - '0';
1480       while (1)
1481         {
1482           prev_val = val;
1483           SVN_ERR(readbuf_getchar(conn, pool, &c));
1484           if (!svn_ctype_isdigit(c))
1485             break;
1486           val = val * 10 + (c - '0');
1487           if (prev_val >= (APR_UINT64_MAX / 10)) /* > maximum value? */
1488             return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1489                                     _("Number is larger than maximum"));
1490         }
1491       if (c == ':')
1492         {
1493           /* It's a string. */
1494           SVN_ERR(readbuf_skip(conn, val));
1495           SVN_ERR(readbuf_getchar(conn, pool, &c));
1496         }
1497     }
1498   else if (svn_ctype_isalpha(c))
1499     {
1500       /* It's a word. */
1501       if (item)
1502         {
1503           /* This is the word we want to read */
1504 
1505           char *buf = apr_palloc(pool, 32);
1506           apr_size_t len = 1;
1507           buf[0] = c;
1508 
1509           while (1)
1510             {
1511               SVN_ERR(readbuf_getchar(conn, pool, &c));
1512               if (!svn_ctype_isalnum(c) && c != '-')
1513                 break;
1514               buf[len] = c;
1515               if (++len == 32)
1516                 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1517                                         _("Word too long"));
1518             }
1519           buf[len] = 0;
1520           *item = buf;
1521         }
1522       else
1523         {
1524           /* we don't need the actual word, just skip it */
1525           do
1526           {
1527             SVN_ERR(readbuf_getchar(conn, pool, &c));
1528           }
1529           while (svn_ctype_isalnum(c) || c == '-');
1530         }
1531     }
1532   else if (c == '(')
1533     {
1534       /* Read in the list items. */
1535       while (1)
1536         {
1537           SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1538           if (c == ')')
1539             break;
1540 
1541           if (item && *item == NULL)
1542             SVN_ERR(read_command_only(conn, pool, item, c));
1543           else
1544             SVN_ERR(read_command_only(conn, pool, NULL, c));
1545         }
1546       SVN_ERR(readbuf_getchar(conn, pool, &c));
1547     }
1548 
1549   return SVN_NO_ERROR;
1550 }
1551 
1552 svn_error_t *
svn_ra_svn__read_item(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_ra_svn__item_t ** item)1553 svn_ra_svn__read_item(svn_ra_svn_conn_t *conn,
1554                       apr_pool_t *pool,
1555                       svn_ra_svn__item_t **item)
1556 {
1557   char c;
1558 
1559   /* Allocate space, read the first character, and then do the rest of
1560    * the work.  This makes sense because of the way lists are read. */
1561   *item = apr_palloc(pool, sizeof(**item));
1562   SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1563   return read_item(conn, pool, *item, c, 0);
1564 }
1565 
1566 /* Drain existing whitespace from the receive buffer of CONN until either
1567    there is no data in the underlying receive socket anymore or we found
1568    a non-whitespace char.  Set *HAS_ITEM to TRUE in the latter case.
1569  */
1570 static svn_error_t *
svn_ra_svn__has_item(svn_boolean_t * has_item,svn_ra_svn_conn_t * conn,apr_pool_t * pool)1571 svn_ra_svn__has_item(svn_boolean_t *has_item,
1572                      svn_ra_svn_conn_t *conn,
1573                      apr_pool_t *pool)
1574 {
1575   do
1576     {
1577       if (conn->read_ptr == conn->read_end)
1578         {
1579           svn_boolean_t available;
1580           if (conn->write_pos)
1581             SVN_ERR(writebuf_flush(conn, pool));
1582 
1583           SVN_ERR(svn_ra_svn__data_available(conn, &available));
1584           if (!available)
1585             break;
1586 
1587           SVN_ERR(readbuf_fill(conn, pool));
1588         }
1589     }
1590   while (svn_iswhitespace(*conn->read_ptr) && ++conn->read_ptr);
1591 
1592   *has_item = conn->read_ptr != conn->read_end;
1593   return SVN_NO_ERROR;
1594 }
1595 
1596 svn_error_t *
svn_ra_svn__skip_leading_garbage(svn_ra_svn_conn_t * conn,apr_pool_t * pool)1597 svn_ra_svn__skip_leading_garbage(svn_ra_svn_conn_t *conn,
1598                                  apr_pool_t *pool)
1599 {
1600   return readbuf_skip_leading_garbage(conn, pool);
1601 }
1602 
1603 /* --- READING AND PARSING TUPLES --- */
1604 
1605 /* Parse a tuple of svn_ra_svn__item_t *'s.  Advance *FMT to the end of the
1606  * tuple specification and advance AP by the corresponding arguments. */
1607 static svn_error_t *
vparse_tuple(const svn_ra_svn__list_t * items,const char ** fmt,va_list * ap)1608 vparse_tuple(const svn_ra_svn__list_t *items,
1609              const char **fmt,
1610              va_list *ap)
1611 {
1612   int count, nesting_level;
1613   svn_ra_svn__item_t *elt;
1614 
1615   for (count = 0; **fmt && count < items->nelts; (*fmt)++, count++)
1616     {
1617       /* '?' just means the tuple may stop; skip past it. */
1618       if (**fmt == '?')
1619         (*fmt)++;
1620       elt = &SVN_RA_SVN__LIST_ITEM(items, count);
1621       if (**fmt == '(' && elt->kind == SVN_RA_SVN_LIST)
1622         {
1623           (*fmt)++;
1624           SVN_ERR(vparse_tuple(&elt->u.list, fmt, ap));
1625         }
1626       else if (**fmt == 'c' && elt->kind == SVN_RA_SVN_STRING)
1627         *va_arg(*ap, const char **) = elt->u.string.data;
1628       else if (**fmt == 's' && elt->kind == SVN_RA_SVN_STRING)
1629         *va_arg(*ap, svn_string_t **) = &elt->u.string;
1630       else if (**fmt == 'w' && elt->kind == SVN_RA_SVN_WORD)
1631         *va_arg(*ap, const char **) = elt->u.word.data;
1632       else if (**fmt == 'b' && elt->kind == SVN_RA_SVN_WORD)
1633         {
1634           if (svn_string_compare(&elt->u.word, &str_true))
1635             *va_arg(*ap, svn_boolean_t *) = TRUE;
1636           else if (svn_string_compare(&elt->u.word, &str_false))
1637             *va_arg(*ap, svn_boolean_t *) = FALSE;
1638           else
1639             break;
1640         }
1641       else if (**fmt == 'n' && elt->kind == SVN_RA_SVN_NUMBER)
1642         *va_arg(*ap, apr_uint64_t *) = elt->u.number;
1643       else if (**fmt == 'r' && elt->kind == SVN_RA_SVN_NUMBER)
1644         *va_arg(*ap, svn_revnum_t *) = (svn_revnum_t) elt->u.number;
1645       else if (**fmt == 'B' && elt->kind == SVN_RA_SVN_WORD)
1646         {
1647           if (svn_string_compare(&elt->u.word, &str_true))
1648             *va_arg(*ap, apr_uint64_t *) = TRUE;
1649           else if (svn_string_compare(&elt->u.word, &str_false))
1650             *va_arg(*ap, apr_uint64_t *) = FALSE;
1651           else
1652             break;
1653         }
1654       else if (**fmt == '3' && elt->kind == SVN_RA_SVN_WORD)
1655         {
1656           if (svn_string_compare(&elt->u.word, &str_true))
1657             *va_arg(*ap, svn_tristate_t *) = svn_tristate_true;
1658           else if (svn_string_compare(&elt->u.word, &str_false))
1659             *va_arg(*ap, svn_tristate_t *) = svn_tristate_false;
1660           else
1661             break;
1662         }
1663       else if (**fmt == 'l' && elt->kind == SVN_RA_SVN_LIST)
1664         *va_arg(*ap, svn_ra_svn__list_t **) = &elt->u.list;
1665       else if (**fmt == ')')
1666         return SVN_NO_ERROR;
1667       else
1668         break;
1669     }
1670   if (**fmt == '?')
1671     {
1672       nesting_level = 0;
1673       for (; **fmt; (*fmt)++)
1674         {
1675           switch (**fmt)
1676             {
1677             case '?':
1678               break;
1679             case 'r':
1680               *va_arg(*ap, svn_revnum_t *) = SVN_INVALID_REVNUM;
1681               break;
1682             case 's':
1683               *va_arg(*ap, svn_string_t **) = NULL;
1684               break;
1685             case 'c':
1686             case 'w':
1687               *va_arg(*ap, const char **) = NULL;
1688               break;
1689             case 'l':
1690               *va_arg(*ap, svn_ra_svn__list_t **) = NULL;
1691               break;
1692             case 'B':
1693             case 'n':
1694               *va_arg(*ap, apr_uint64_t *) = SVN_RA_SVN_UNSPECIFIED_NUMBER;
1695               break;
1696             case '3':
1697               *va_arg(*ap, svn_tristate_t *) = svn_tristate_unknown;
1698               break;
1699             case 'b':
1700               *va_arg(*ap, svn_boolean_t *) = FALSE;
1701               break;
1702             case '(':
1703               nesting_level++;
1704               break;
1705             case ')':
1706               if (--nesting_level < 0)
1707                 return SVN_NO_ERROR;
1708               break;
1709             default:
1710               SVN_ERR_MALFUNCTION();
1711             }
1712         }
1713     }
1714   if (**fmt && **fmt != ')')
1715     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1716                             _("Malformed network data"));
1717   return SVN_NO_ERROR;
1718 }
1719 
1720 svn_error_t *
svn_ra_svn__parse_tuple(const svn_ra_svn__list_t * list,const char * fmt,...)1721 svn_ra_svn__parse_tuple(const svn_ra_svn__list_t *list,
1722                         const char *fmt, ...)
1723 {
1724   svn_error_t *err;
1725   va_list ap;
1726 
1727   va_start(ap, fmt);
1728   err = vparse_tuple(list, &fmt, &ap);
1729   va_end(ap);
1730   return err;
1731 }
1732 
1733 svn_error_t *
svn_ra_svn__read_tuple(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * fmt,...)1734 svn_ra_svn__read_tuple(svn_ra_svn_conn_t *conn,
1735                        apr_pool_t *pool,
1736                        const char *fmt, ...)
1737 {
1738   va_list ap;
1739   svn_ra_svn__item_t *item;
1740   svn_error_t *err;
1741 
1742   SVN_ERR(svn_ra_svn__read_item(conn, pool, &item));
1743   if (item->kind != SVN_RA_SVN_LIST)
1744     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1745                             _("Malformed network data"));
1746   va_start(ap, fmt);
1747   err = vparse_tuple(&item->u.list, &fmt, &ap);
1748   va_end(ap);
1749   return err;
1750 }
1751 
1752 svn_error_t *
svn_ra_svn__read_command_only(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char ** command)1753 svn_ra_svn__read_command_only(svn_ra_svn_conn_t *conn,
1754                               apr_pool_t *pool,
1755                               const char **command)
1756 {
1757   char c;
1758   SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1759 
1760   *command = NULL;
1761   return read_command_only(conn, pool, command, c);
1762 }
1763 
1764 
1765 svn_error_t *
svn_ra_svn__parse_proplist(const svn_ra_svn__list_t * list,apr_pool_t * pool,apr_hash_t ** props)1766 svn_ra_svn__parse_proplist(const svn_ra_svn__list_t *list,
1767                            apr_pool_t *pool,
1768                            apr_hash_t **props)
1769 {
1770   svn_string_t *name;
1771   svn_string_t *value;
1772   svn_ra_svn__item_t *elt;
1773   int i;
1774 
1775   *props = svn_hash__make(pool);
1776   for (i = 0; i < list->nelts; i++)
1777     {
1778       elt = &SVN_RA_SVN__LIST_ITEM(list, i);
1779       if (elt->kind != SVN_RA_SVN_LIST)
1780         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1781                                 _("Proplist element not a list"));
1782       SVN_ERR(svn_ra_svn__parse_tuple(&elt->u.list, "ss", &name, &value));
1783       apr_hash_set(*props, name->data, name->len, value);
1784     }
1785 
1786   return SVN_NO_ERROR;
1787 }
1788 
1789 
1790 /* --- READING AND WRITING COMMANDS AND RESPONSES --- */
1791 
svn_ra_svn__locate_real_error_child(svn_error_t * err)1792 svn_error_t *svn_ra_svn__locate_real_error_child(svn_error_t *err)
1793 {
1794   svn_error_t *this_link;
1795 
1796   SVN_ERR_ASSERT(err);
1797 
1798   for (this_link = err;
1799        this_link && (this_link->apr_err == SVN_ERR_RA_SVN_CMD_ERR);
1800        this_link = this_link->child)
1801     ;
1802 
1803   SVN_ERR_ASSERT(this_link);
1804   return this_link;
1805 }
1806 
1807 svn_error_t *
svn_ra_svn__handle_failure_status(const svn_ra_svn__list_t * params)1808 svn_ra_svn__handle_failure_status(const svn_ra_svn__list_t *params)
1809 {
1810   const char *message, *file;
1811   svn_error_t *err = NULL;
1812   svn_ra_svn__item_t *elt;
1813   int i;
1814   apr_uint64_t apr_err, line;
1815 
1816   if (params->nelts == 0)
1817     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1818                             _("Empty error list"));
1819 
1820   /* Rebuild the error list from the end, to avoid reversing the order. */
1821   for (i = params->nelts - 1; i >= 0; i--)
1822     {
1823       elt = &SVN_RA_SVN__LIST_ITEM(params, i);
1824       if (elt->kind != SVN_RA_SVN_LIST)
1825         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1826                                 _("Malformed error list"));
1827       SVN_ERR(svn_ra_svn__parse_tuple(&elt->u.list, "nccn",
1828                                       &apr_err, &message, &file, &line));
1829       /* The message field should have been optional, but we can't
1830          easily change that, so "" means a nonexistent message. */
1831       if (!*message)
1832         message = NULL;
1833 
1834       /* Skip over links in the error chain that were intended only to
1835          exist on the server (to wrap real errors intended for the
1836          client) but accidentally got included in the server's actual
1837          response. */
1838       if ((apr_status_t)apr_err != SVN_ERR_RA_SVN_CMD_ERR)
1839         {
1840           err = svn_error_create((apr_status_t)apr_err, err, message);
1841           err->file = apr_pstrdup(err->pool, file);
1842           err->line = (long)line;
1843         }
1844     }
1845 
1846   /* If we get here, then we failed to find a real error in the error
1847      chain that the server proported to be sending us.  That's bad. */
1848   if (! err)
1849     err = svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1850                            _("Malformed error list"));
1851 
1852   return err;
1853 }
1854 
1855 svn_error_t *
svn_ra_svn__read_cmd_response(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * fmt,...)1856 svn_ra_svn__read_cmd_response(svn_ra_svn_conn_t *conn,
1857                               apr_pool_t *pool,
1858                               const char *fmt, ...)
1859 {
1860   va_list ap;
1861   const char *status;
1862   svn_ra_svn__list_t *params;
1863   svn_error_t *err;
1864 
1865   SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "wl", &status, &params));
1866   if (strcmp(status, "success") == 0)
1867     {
1868       va_start(ap, fmt);
1869       err = vparse_tuple(params, &fmt, &ap);
1870       va_end(ap);
1871       return err;
1872     }
1873   else if (strcmp(status, "failure") == 0)
1874     {
1875       return svn_error_trace(svn_ra_svn__handle_failure_status(params));
1876     }
1877 
1878   return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1879                            _("Unknown status '%s' in command response"),
1880                            status);
1881 }
1882 
1883 svn_error_t *
svn_ra_svn__has_command(svn_boolean_t * has_command,svn_boolean_t * terminated,svn_ra_svn_conn_t * conn,apr_pool_t * pool)1884 svn_ra_svn__has_command(svn_boolean_t *has_command,
1885                         svn_boolean_t *terminated,
1886                         svn_ra_svn_conn_t *conn,
1887                         apr_pool_t *pool)
1888 {
1889   svn_error_t *err;
1890 
1891   /* Don't make whitespace between commands trigger I/O limitiations. */
1892   svn_ra_svn__reset_command_io_counters(conn);
1893 
1894   err = svn_ra_svn__has_item(has_command, conn, pool);
1895   if (err && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED)
1896     {
1897       *terminated = TRUE;
1898       svn_error_clear(err);
1899       return SVN_NO_ERROR;
1900     }
1901 
1902   *terminated = FALSE;
1903   return svn_error_trace(err);
1904 }
1905 
1906 svn_error_t *
svn_ra_svn__handle_command(svn_boolean_t * terminate,apr_hash_t * cmd_hash,void * baton,svn_ra_svn_conn_t * conn,svn_boolean_t error_on_disconnect,apr_pool_t * pool)1907 svn_ra_svn__handle_command(svn_boolean_t *terminate,
1908                            apr_hash_t *cmd_hash,
1909                            void *baton,
1910                            svn_ra_svn_conn_t *conn,
1911                            svn_boolean_t error_on_disconnect,
1912                            apr_pool_t *pool)
1913 {
1914   const char *cmdname;
1915   svn_error_t *err, *write_err;
1916   svn_ra_svn__list_t *params;
1917   const svn_ra_svn__cmd_entry_t *command;
1918 
1919   *terminate = FALSE;
1920 
1921   /* Limit I/O for every command separately. */
1922   svn_ra_svn__reset_command_io_counters(conn);
1923 
1924   err = svn_ra_svn__read_tuple(conn, pool, "wl", &cmdname, &params);
1925   if (err)
1926     {
1927       if (!error_on_disconnect
1928           && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED)
1929         {
1930           svn_error_clear(err);
1931           *terminate = TRUE;
1932           return SVN_NO_ERROR;
1933         }
1934       return err;
1935     }
1936 
1937   command = svn_hash_gets(cmd_hash, cmdname);
1938   if (command)
1939     {
1940       /* Call the standard command handler.
1941        * If that is not set, then this is a lecagy API call and we invoke
1942        * the legacy command handler. */
1943       if (command->handler)
1944         {
1945           err = (*command->handler)(conn, pool, params, baton);
1946         }
1947       else
1948         {
1949           apr_array_header_t *deprecated_params
1950             = svn_ra_svn__to_public_array(params, pool);
1951           err = (*command->deprecated_handler)(conn, pool, deprecated_params,
1952                                                baton);
1953         }
1954 
1955       /* The command implementation may have swallowed or wrapped the I/O
1956        * error not knowing that we may no longer be able to send data.
1957        *
1958        * So, check again for the limit violations and exit the command
1959        * processing quickly if we may have truncated data. */
1960       err = svn_error_compose_create(check_io_limits(conn), err);
1961 
1962       *terminate = command->terminate;
1963     }
1964   else
1965     {
1966       err = svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL,
1967                               _("Unknown editor command '%s'"), cmdname);
1968       err = svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, err, NULL);
1969     }
1970 
1971   if (err && err->apr_err == SVN_ERR_RA_SVN_CMD_ERR)
1972     {
1973       write_err = svn_ra_svn__write_cmd_failure(
1974                       conn, pool,
1975                       svn_ra_svn__locate_real_error_child(err));
1976       svn_error_clear(err);
1977       return write_err ? write_err : SVN_NO_ERROR;
1978     }
1979 
1980   return err;
1981 }
1982 
1983 svn_error_t *
svn_ra_svn__handle_commands2(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_ra_svn__cmd_entry_t * commands,void * baton,svn_boolean_t error_on_disconnect)1984 svn_ra_svn__handle_commands2(svn_ra_svn_conn_t *conn,
1985                              apr_pool_t *pool,
1986                              const svn_ra_svn__cmd_entry_t *commands,
1987                              void *baton,
1988                              svn_boolean_t error_on_disconnect)
1989 {
1990   apr_pool_t *subpool = svn_pool_create(pool);
1991   apr_pool_t *iterpool = svn_pool_create(subpool);
1992   const svn_ra_svn__cmd_entry_t *command;
1993   apr_hash_t *cmd_hash = apr_hash_make(subpool);
1994 
1995   for (command = commands; command->cmdname; command++)
1996     svn_hash_sets(cmd_hash, command->cmdname, command);
1997 
1998   while (1)
1999     {
2000       svn_boolean_t terminate;
2001       svn_error_t *err;
2002       svn_pool_clear(iterpool);
2003 
2004       err = svn_ra_svn__handle_command(&terminate, cmd_hash, baton, conn,
2005                                        error_on_disconnect, iterpool);
2006       if (err)
2007         {
2008           svn_pool_destroy(subpool);
2009           return svn_error_trace(err);
2010         }
2011       if (terminate)
2012         break;
2013     }
2014   svn_pool_destroy(iterpool);
2015   svn_pool_destroy(subpool);
2016   return SVN_NO_ERROR;
2017 }
2018 
2019 svn_error_t *
svn_ra_svn__write_cmd_target_rev(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t rev)2020 svn_ra_svn__write_cmd_target_rev(svn_ra_svn_conn_t *conn,
2021                                  apr_pool_t *pool,
2022                                  svn_revnum_t rev)
2023 {
2024   SVN_ERR(writebuf_write_literal(conn, pool, "( target-rev ( "));
2025   SVN_ERR(write_tuple_revision(conn, pool, rev));
2026   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2027 
2028   return SVN_NO_ERROR;
2029 }
2030 
2031 svn_error_t *
svn_ra_svn__write_cmd_open_root(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t rev,const svn_string_t * token)2032 svn_ra_svn__write_cmd_open_root(svn_ra_svn_conn_t *conn,
2033                                 apr_pool_t *pool,
2034                                 svn_revnum_t rev,
2035                                 const svn_string_t *token)
2036 {
2037   SVN_ERR(writebuf_write_literal(conn, pool, "( open-root ( "));
2038   SVN_ERR(write_tuple_start_list(conn, pool));
2039   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2040   SVN_ERR(write_tuple_end_list(conn, pool));
2041   SVN_ERR(write_tuple_string(conn, pool, token));
2042   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2043 
2044   return SVN_NO_ERROR;
2045 }
2046 
2047 svn_error_t *
svn_ra_svn__write_cmd_delete_entry(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,svn_revnum_t rev,const svn_string_t * token)2048 svn_ra_svn__write_cmd_delete_entry(svn_ra_svn_conn_t *conn,
2049                                    apr_pool_t *pool,
2050                                    const char *path,
2051                                    svn_revnum_t rev,
2052                                    const svn_string_t *token)
2053 {
2054   SVN_ERR(writebuf_write_literal(conn, pool, "( delete-entry ( "));
2055   SVN_ERR(write_tuple_cstring(conn, pool, path));
2056   SVN_ERR(write_tuple_start_list(conn, pool));
2057   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2058   SVN_ERR(write_tuple_end_list(conn, pool));
2059   SVN_ERR(write_tuple_string(conn, pool, token));
2060   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2061 
2062   return SVN_NO_ERROR;
2063 }
2064 
2065 svn_error_t *
svn_ra_svn__write_cmd_add_dir(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,const svn_string_t * parent_token,const svn_string_t * token,const char * copy_path,svn_revnum_t copy_rev)2066 svn_ra_svn__write_cmd_add_dir(svn_ra_svn_conn_t *conn,
2067                               apr_pool_t *pool,
2068                               const char *path,
2069                               const svn_string_t *parent_token,
2070                               const svn_string_t *token,
2071                               const char *copy_path,
2072                               svn_revnum_t copy_rev)
2073 {
2074   SVN_ERR(writebuf_write_literal(conn, pool, "( add-dir ( "));
2075   SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token,
2076                               copy_path, copy_rev));
2077   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2078 
2079   return SVN_NO_ERROR;
2080 }
2081 
2082 svn_error_t *
svn_ra_svn__write_cmd_open_dir(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,const svn_string_t * parent_token,const svn_string_t * token,svn_revnum_t rev)2083 svn_ra_svn__write_cmd_open_dir(svn_ra_svn_conn_t *conn,
2084                                apr_pool_t *pool,
2085                                const char *path,
2086                                const svn_string_t *parent_token,
2087                                const svn_string_t *token,
2088                                svn_revnum_t rev)
2089 {
2090   SVN_ERR(writebuf_write_literal(conn, pool, "( open-dir ( "));
2091   SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev));
2092   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2093 
2094   return SVN_NO_ERROR;
2095 }
2096 
2097 svn_error_t *
svn_ra_svn__write_cmd_change_dir_prop(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * token,const char * name,const svn_string_t * value)2098 svn_ra_svn__write_cmd_change_dir_prop(svn_ra_svn_conn_t *conn,
2099                                       apr_pool_t *pool,
2100                                       const svn_string_t *token,
2101                                       const char *name,
2102                                       const svn_string_t *value)
2103 {
2104   SVN_ERR(writebuf_write_literal(conn, pool, "( change-dir-prop ( "));
2105   SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value));
2106   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2107 
2108   return SVN_NO_ERROR;
2109 }
2110 
2111 svn_error_t *
svn_ra_svn__write_cmd_close_dir(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * token)2112 svn_ra_svn__write_cmd_close_dir(svn_ra_svn_conn_t *conn,
2113                                 apr_pool_t *pool,
2114                                 const svn_string_t *token)
2115 {
2116   SVN_ERR(writebuf_write_literal(conn, pool, "( close-dir ( "));
2117   SVN_ERR(write_tuple_string(conn, pool, token));
2118   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2119 
2120   return SVN_NO_ERROR;
2121 }
2122 
2123 svn_error_t *
svn_ra_svn__write_cmd_absent_dir(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,const svn_string_t * parent_token)2124 svn_ra_svn__write_cmd_absent_dir(svn_ra_svn_conn_t *conn,
2125                                  apr_pool_t *pool,
2126                                  const char *path,
2127                                  const svn_string_t *parent_token)
2128 {
2129   SVN_ERR(writebuf_write_literal(conn, pool, "( absent-dir ( "));
2130   SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token));
2131   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2132 
2133   return SVN_NO_ERROR;
2134 }
2135 
2136 svn_error_t *
svn_ra_svn__write_cmd_add_file(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,const svn_string_t * parent_token,const svn_string_t * token,const char * copy_path,svn_revnum_t copy_rev)2137 svn_ra_svn__write_cmd_add_file(svn_ra_svn_conn_t *conn,
2138                                apr_pool_t *pool,
2139                                const char *path,
2140                                const svn_string_t *parent_token,
2141                                const svn_string_t *token,
2142                                const char *copy_path,
2143                                svn_revnum_t copy_rev)
2144 {
2145   SVN_ERR(writebuf_write_literal(conn, pool, "( add-file ( "));
2146   SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token,
2147                               copy_path, copy_rev));
2148   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2149 
2150   return SVN_NO_ERROR;
2151 }
2152 
2153 svn_error_t *
svn_ra_svn__write_cmd_open_file(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,const svn_string_t * parent_token,const svn_string_t * token,svn_revnum_t rev)2154 svn_ra_svn__write_cmd_open_file(svn_ra_svn_conn_t *conn,
2155                                 apr_pool_t *pool,
2156                                 const char *path,
2157                                 const svn_string_t *parent_token,
2158                                 const svn_string_t *token,
2159                                 svn_revnum_t rev)
2160 {
2161   SVN_ERR(writebuf_write_literal(conn, pool, "( open-file ( "));
2162   SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev));
2163   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2164 
2165   return SVN_NO_ERROR;
2166 }
2167 
2168 svn_error_t *
svn_ra_svn__write_cmd_change_file_prop(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * token,const char * name,const svn_string_t * value)2169 svn_ra_svn__write_cmd_change_file_prop(svn_ra_svn_conn_t *conn,
2170                                        apr_pool_t *pool,
2171                                        const svn_string_t *token,
2172                                        const char *name,
2173                                        const svn_string_t *value)
2174 {
2175   SVN_ERR(writebuf_write_literal(conn, pool, "( change-file-prop ( "));
2176   SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value));
2177   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2178 
2179   return SVN_NO_ERROR;
2180 }
2181 
2182 svn_error_t *
svn_ra_svn__write_cmd_close_file(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * token,const char * text_checksum)2183 svn_ra_svn__write_cmd_close_file(svn_ra_svn_conn_t *conn,
2184                                  apr_pool_t *pool,
2185                                  const svn_string_t *token,
2186                                  const char *text_checksum)
2187 {
2188   SVN_ERR(writebuf_write_literal(conn, pool, "( close-file ( "));
2189   SVN_ERR(write_tuple_string(conn, pool, token));
2190   SVN_ERR(write_tuple_start_list(conn, pool));
2191   SVN_ERR(write_tuple_cstring_opt(conn, pool, text_checksum));
2192   SVN_ERR(write_tuple_end_list(conn, pool));
2193   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2194 
2195   return SVN_NO_ERROR;
2196 }
2197 
2198 svn_error_t *
svn_ra_svn__write_cmd_absent_file(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,const svn_string_t * parent_token)2199 svn_ra_svn__write_cmd_absent_file(svn_ra_svn_conn_t *conn,
2200                                   apr_pool_t *pool,
2201                                   const char *path,
2202                                   const svn_string_t *parent_token)
2203 {
2204   SVN_ERR(writebuf_write_literal(conn, pool, "( absent-file ( "));
2205   SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token));
2206   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2207 
2208   return SVN_NO_ERROR;
2209 }
2210 
2211 svn_error_t *
svn_ra_svn__write_cmd_textdelta_chunk(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * token,const svn_string_t * chunk)2212 svn_ra_svn__write_cmd_textdelta_chunk(svn_ra_svn_conn_t *conn,
2213                                       apr_pool_t *pool,
2214                                       const svn_string_t *token,
2215                                       const svn_string_t *chunk)
2216 {
2217   SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-chunk ( "));
2218   SVN_ERR(write_tuple_string(conn, pool, token));
2219   SVN_ERR(write_tuple_string(conn, pool, chunk));
2220   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2221 
2222   return SVN_NO_ERROR;
2223 }
2224 
2225 svn_error_t *
svn_ra_svn__write_cmd_textdelta_end(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * token)2226 svn_ra_svn__write_cmd_textdelta_end(svn_ra_svn_conn_t *conn,
2227                                     apr_pool_t *pool,
2228                                     const svn_string_t *token)
2229 {
2230   SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-end ( "));
2231   SVN_ERR(write_tuple_string(conn, pool, token));
2232   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2233 
2234   return SVN_NO_ERROR;
2235 }
2236 
2237 svn_error_t *
svn_ra_svn__write_cmd_apply_textdelta(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * token,const char * base_checksum)2238 svn_ra_svn__write_cmd_apply_textdelta(svn_ra_svn_conn_t *conn,
2239                                       apr_pool_t *pool,
2240                                       const svn_string_t *token,
2241                                       const char *base_checksum)
2242 {
2243   SVN_ERR(writebuf_write_literal(conn, pool, "( apply-textdelta ( "));
2244   SVN_ERR(write_tuple_string(conn, pool, token));
2245   SVN_ERR(write_tuple_start_list(conn, pool));
2246   SVN_ERR(write_tuple_cstring_opt(conn, pool, base_checksum));
2247   SVN_ERR(write_tuple_end_list(conn, pool));
2248   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2249 
2250   return SVN_NO_ERROR;
2251 }
2252 
2253 svn_error_t *
svn_ra_svn__write_cmd_close_edit(svn_ra_svn_conn_t * conn,apr_pool_t * pool)2254 svn_ra_svn__write_cmd_close_edit(svn_ra_svn_conn_t *conn,
2255                                  apr_pool_t *pool)
2256 {
2257   return writebuf_write_literal(conn, pool, "( close-edit ( ) ) ");
2258 }
2259 
2260 svn_error_t *
svn_ra_svn__write_cmd_abort_edit(svn_ra_svn_conn_t * conn,apr_pool_t * pool)2261 svn_ra_svn__write_cmd_abort_edit(svn_ra_svn_conn_t *conn,
2262                                  apr_pool_t *pool)
2263 {
2264   return writebuf_write_literal(conn, pool, "( abort-edit ( ) ) ");
2265 }
2266 
2267 svn_error_t *
svn_ra_svn__write_cmd_set_path(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,svn_revnum_t rev,svn_boolean_t start_empty,const char * lock_token,svn_depth_t depth)2268 svn_ra_svn__write_cmd_set_path(svn_ra_svn_conn_t *conn,
2269                                apr_pool_t *pool,
2270                                const char *path,
2271                                svn_revnum_t rev,
2272                                svn_boolean_t start_empty,
2273                                const char *lock_token,
2274                                svn_depth_t depth)
2275 {
2276   SVN_ERR(writebuf_write_literal(conn, pool, "( set-path ( "));
2277   SVN_ERR(write_tuple_cstring(conn, pool, path));
2278   SVN_ERR(write_tuple_revision(conn, pool, rev));
2279   SVN_ERR(write_tuple_boolean(conn, pool, start_empty));
2280   SVN_ERR(write_tuple_start_list(conn, pool));
2281   SVN_ERR(write_tuple_cstring_opt(conn, pool, lock_token));
2282   SVN_ERR(write_tuple_end_list(conn, pool));
2283   SVN_ERR(write_tuple_depth(conn, pool, depth));
2284   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2285 
2286   return SVN_NO_ERROR;
2287 }
2288 
2289 svn_error_t *
svn_ra_svn__write_cmd_delete_path(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path)2290 svn_ra_svn__write_cmd_delete_path(svn_ra_svn_conn_t *conn,
2291                                   apr_pool_t *pool,
2292                                   const char *path)
2293 {
2294   SVN_ERR(writebuf_write_literal(conn, pool, "( delete-path ( "));
2295   SVN_ERR(write_tuple_cstring(conn, pool, path));
2296   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2297 
2298   return SVN_NO_ERROR;
2299 }
2300 
2301 svn_error_t *
svn_ra_svn__write_cmd_link_path(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,const char * url,svn_revnum_t rev,svn_boolean_t start_empty,const char * lock_token,svn_depth_t depth)2302 svn_ra_svn__write_cmd_link_path(svn_ra_svn_conn_t *conn,
2303                                 apr_pool_t *pool,
2304                                 const char *path,
2305                                 const char *url,
2306                                 svn_revnum_t rev,
2307                                 svn_boolean_t start_empty,
2308                                 const char *lock_token,
2309                                 svn_depth_t depth)
2310 {
2311   SVN_ERR(writebuf_write_literal(conn, pool, "( link-path ( "));
2312   SVN_ERR(write_tuple_cstring(conn, pool, path));
2313   SVN_ERR(write_tuple_cstring(conn, pool, url));
2314   SVN_ERR(write_tuple_revision(conn, pool, rev));
2315   SVN_ERR(write_tuple_boolean(conn, pool, start_empty));
2316   SVN_ERR(write_tuple_start_list(conn, pool));
2317   SVN_ERR(write_tuple_cstring_opt(conn, pool,lock_token));
2318   SVN_ERR(write_tuple_end_list(conn, pool));
2319   SVN_ERR(write_tuple_depth(conn, pool, depth));
2320   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2321 
2322   return SVN_NO_ERROR;
2323 }
2324 
2325 svn_error_t *
svn_ra_svn__write_cmd_finish_report(svn_ra_svn_conn_t * conn,apr_pool_t * pool)2326 svn_ra_svn__write_cmd_finish_report(svn_ra_svn_conn_t *conn,
2327                                     apr_pool_t *pool)
2328 {
2329   return writebuf_write_literal(conn, pool, "( finish-report ( ) ) ");
2330 }
2331 
2332 svn_error_t *
svn_ra_svn__write_cmd_abort_report(svn_ra_svn_conn_t * conn,apr_pool_t * pool)2333 svn_ra_svn__write_cmd_abort_report(svn_ra_svn_conn_t *conn,
2334                                    apr_pool_t *pool)
2335 {
2336   return writebuf_write_literal(conn, pool, "( abort-report ( ) ) ");
2337 }
2338 
2339 svn_error_t *
svn_ra_svn__write_cmd_reparent(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * url)2340 svn_ra_svn__write_cmd_reparent(svn_ra_svn_conn_t *conn,
2341                                apr_pool_t *pool,
2342                                const char *url)
2343 {
2344   SVN_ERR(writebuf_write_literal(conn, pool, "( reparent ( "));
2345   SVN_ERR(write_tuple_cstring(conn, pool, url));
2346   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2347 
2348   return SVN_NO_ERROR;
2349 }
2350 
2351 svn_error_t *
svn_ra_svn__write_cmd_get_latest_rev(svn_ra_svn_conn_t * conn,apr_pool_t * pool)2352 svn_ra_svn__write_cmd_get_latest_rev(svn_ra_svn_conn_t *conn,
2353                                    apr_pool_t *pool)
2354 {
2355   return writebuf_write_literal(conn, pool, "( get-latest-rev ( ) ) ");
2356 }
2357 
2358 svn_error_t *
svn_ra_svn__write_cmd_get_dated_rev(svn_ra_svn_conn_t * conn,apr_pool_t * pool,apr_time_t tm)2359 svn_ra_svn__write_cmd_get_dated_rev(svn_ra_svn_conn_t *conn,
2360                                     apr_pool_t *pool,
2361                                     apr_time_t tm)
2362 {
2363   SVN_ERR(writebuf_write_literal(conn, pool, "( get-dated-rev ( "));
2364   SVN_ERR(write_tuple_cstring(conn, pool, svn_time_to_cstring(tm, pool)));
2365   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2366 
2367   return SVN_NO_ERROR;
2368 }
2369 
2370 svn_error_t *
svn_ra_svn__write_cmd_change_rev_prop2(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t rev,const char * name,const svn_string_t * value,svn_boolean_t dont_care,const svn_string_t * old_value)2371 svn_ra_svn__write_cmd_change_rev_prop2(svn_ra_svn_conn_t *conn,
2372                                        apr_pool_t *pool,
2373                                        svn_revnum_t rev,
2374                                        const char *name,
2375                                        const svn_string_t *value,
2376                                        svn_boolean_t dont_care,
2377                                        const svn_string_t *old_value)
2378 {
2379   SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop2 ( "));
2380   SVN_ERR(write_tuple_revision(conn, pool, rev));
2381   SVN_ERR(write_tuple_cstring(conn, pool, name));
2382   SVN_ERR(write_tuple_string_opt_list(conn, pool, value));
2383   SVN_ERR(write_tuple_start_list(conn, pool));
2384   SVN_ERR(write_tuple_boolean(conn, pool, dont_care));
2385   SVN_ERR(write_tuple_string_opt(conn, pool, old_value));
2386   SVN_ERR(write_tuple_end_list(conn, pool));
2387   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2388 
2389   return SVN_NO_ERROR;
2390 }
2391 
2392 svn_error_t *
svn_ra_svn__write_cmd_change_rev_prop(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t rev,const char * name,const svn_string_t * value)2393 svn_ra_svn__write_cmd_change_rev_prop(svn_ra_svn_conn_t *conn,
2394                                       apr_pool_t *pool,
2395                                       svn_revnum_t rev,
2396                                       const char *name,
2397                                       const svn_string_t *value)
2398 {
2399   SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop ( "));
2400   SVN_ERR(write_tuple_revision(conn, pool, rev));
2401   SVN_ERR(write_tuple_cstring(conn, pool, name));
2402   SVN_ERR(write_tuple_string_opt(conn, pool, value));
2403   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2404 
2405   return SVN_NO_ERROR;
2406 }
2407 
2408 svn_error_t *
svn_ra_svn__write_cmd_rev_proplist(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t rev)2409 svn_ra_svn__write_cmd_rev_proplist(svn_ra_svn_conn_t *conn,
2410                                    apr_pool_t *pool,
2411                                    svn_revnum_t rev)
2412 {
2413   SVN_ERR(writebuf_write_literal(conn, pool, "( rev-proplist ( "));
2414   SVN_ERR(write_tuple_revision(conn, pool, rev));
2415   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2416 
2417   return SVN_NO_ERROR;
2418 }
2419 
2420 svn_error_t *
svn_ra_svn__write_cmd_rev_prop(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t rev,const char * name)2421 svn_ra_svn__write_cmd_rev_prop(svn_ra_svn_conn_t *conn,
2422                                apr_pool_t *pool,
2423                                svn_revnum_t rev,
2424                                const char *name)
2425 {
2426   SVN_ERR(writebuf_write_literal(conn, pool, "( rev-prop ( "));
2427   SVN_ERR(write_tuple_revision(conn, pool, rev));
2428   SVN_ERR(write_tuple_cstring(conn, pool, name));
2429   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2430 
2431   return SVN_NO_ERROR;
2432 }
2433 
2434 svn_error_t *
svn_ra_svn__write_cmd_get_file(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,svn_revnum_t rev,svn_boolean_t props,svn_boolean_t stream)2435 svn_ra_svn__write_cmd_get_file(svn_ra_svn_conn_t *conn,
2436                                apr_pool_t *pool,
2437                                const char *path,
2438                                svn_revnum_t rev,
2439                                svn_boolean_t props,
2440                                svn_boolean_t stream)
2441 {
2442   SVN_ERR(writebuf_write_literal(conn, pool, "( get-file ( "));
2443   SVN_ERR(write_tuple_cstring(conn, pool, path));
2444   SVN_ERR(write_tuple_start_list(conn, pool));
2445   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2446   SVN_ERR(write_tuple_end_list(conn, pool));
2447   SVN_ERR(write_tuple_boolean(conn, pool, props));
2448   SVN_ERR(write_tuple_boolean(conn, pool, stream));
2449 
2450   /* Always send the, nominally optional, want-iprops as "false" to
2451      workaround a bug in svnserve 1.8.0-1.8.8 that causes the server
2452      to see "true" if it is omitted. */
2453   SVN_ERR(writebuf_write_literal(conn, pool, " false ) ) "));
2454 
2455   return SVN_NO_ERROR;
2456 }
2457 
2458 svn_error_t *
svn_ra_svn__write_cmd_update(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t rev,const char * target,svn_boolean_t recurse,svn_depth_t depth,svn_boolean_t send_copyfrom_args,svn_boolean_t ignore_ancestry)2459 svn_ra_svn__write_cmd_update(svn_ra_svn_conn_t *conn,
2460                              apr_pool_t *pool,
2461                              svn_revnum_t rev,
2462                              const char *target,
2463                              svn_boolean_t recurse,
2464                              svn_depth_t depth,
2465                              svn_boolean_t send_copyfrom_args,
2466                              svn_boolean_t ignore_ancestry)
2467 {
2468   SVN_ERR(writebuf_write_literal(conn, pool, "( update ( "));
2469   SVN_ERR(write_tuple_start_list(conn, pool));
2470   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2471   SVN_ERR(write_tuple_end_list(conn, pool));
2472   SVN_ERR(write_tuple_cstring(conn, pool, target));
2473   SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2474   SVN_ERR(write_tuple_depth(conn, pool, depth));
2475   SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args));
2476   SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2477   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2478 
2479   return SVN_NO_ERROR;
2480 }
2481 
2482 svn_error_t *
svn_ra_svn__write_cmd_switch(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t rev,const char * target,svn_boolean_t recurse,const char * switch_url,svn_depth_t depth,svn_boolean_t send_copyfrom_args,svn_boolean_t ignore_ancestry)2483 svn_ra_svn__write_cmd_switch(svn_ra_svn_conn_t *conn,
2484                              apr_pool_t *pool,
2485                              svn_revnum_t rev,
2486                              const char *target,
2487                              svn_boolean_t recurse,
2488                              const char *switch_url,
2489                              svn_depth_t depth,
2490                              svn_boolean_t send_copyfrom_args,
2491                              svn_boolean_t ignore_ancestry)
2492 {
2493   SVN_ERR(writebuf_write_literal(conn, pool, "( switch ( "));
2494   SVN_ERR(write_tuple_start_list(conn, pool));
2495   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2496   SVN_ERR(write_tuple_end_list(conn, pool));
2497   SVN_ERR(write_tuple_cstring(conn, pool, target));
2498   SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2499   SVN_ERR(write_tuple_cstring(conn, pool, switch_url));
2500   SVN_ERR(write_tuple_depth(conn, pool, depth));
2501   SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args));
2502   SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2503   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2504 
2505   return SVN_NO_ERROR;
2506 }
2507 
2508 svn_error_t *
svn_ra_svn__write_cmd_status(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * target,svn_boolean_t recurse,svn_revnum_t rev,svn_depth_t depth)2509 svn_ra_svn__write_cmd_status(svn_ra_svn_conn_t *conn,
2510                              apr_pool_t *pool,
2511                              const char *target,
2512                              svn_boolean_t recurse,
2513                              svn_revnum_t rev,
2514                              svn_depth_t depth)
2515 {
2516   SVN_ERR(writebuf_write_literal(conn, pool, "( status ( "));
2517   SVN_ERR(write_tuple_cstring(conn, pool, target));
2518   SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2519   SVN_ERR(write_tuple_start_list(conn, pool));
2520   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2521   SVN_ERR(write_tuple_end_list(conn, pool));
2522   SVN_ERR(write_tuple_depth(conn, pool, depth));
2523   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2524 
2525   return SVN_NO_ERROR;
2526 }
2527 
2528 svn_error_t *
svn_ra_svn__write_cmd_diff(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t rev,const char * target,svn_boolean_t recurse,svn_boolean_t ignore_ancestry,const char * versus_url,svn_boolean_t text_deltas,svn_depth_t depth)2529 svn_ra_svn__write_cmd_diff(svn_ra_svn_conn_t *conn,
2530                            apr_pool_t *pool,
2531                            svn_revnum_t rev,
2532                            const char *target,
2533                            svn_boolean_t recurse,
2534                            svn_boolean_t ignore_ancestry,
2535                            const char *versus_url,
2536                            svn_boolean_t text_deltas,
2537                            svn_depth_t depth)
2538 {
2539   SVN_ERR(writebuf_write_literal(conn, pool, "( diff ( "));
2540   SVN_ERR(write_tuple_start_list(conn, pool));
2541   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2542   SVN_ERR(write_tuple_end_list(conn, pool));
2543   SVN_ERR(write_tuple_cstring(conn, pool, target));
2544   SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2545   SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2546   SVN_ERR(write_tuple_cstring(conn, pool, versus_url));
2547   SVN_ERR(write_tuple_boolean(conn, pool, text_deltas));
2548   SVN_ERR(write_tuple_depth(conn, pool, depth));
2549   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2550 
2551   return SVN_NO_ERROR;
2552 }
2553 
2554 svn_error_t *
svn_ra_svn__write_cmd_check_path(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,svn_revnum_t rev)2555 svn_ra_svn__write_cmd_check_path(svn_ra_svn_conn_t *conn,
2556                                  apr_pool_t *pool,
2557                                  const char *path,
2558                                  svn_revnum_t rev)
2559 {
2560   SVN_ERR(writebuf_write_literal(conn, pool, "( check-path ( "));
2561   SVN_ERR(write_tuple_cstring(conn, pool, path));
2562   SVN_ERR(write_tuple_start_list(conn, pool));
2563   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2564   SVN_ERR(write_tuple_end_list(conn, pool));
2565   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2566 
2567   return SVN_NO_ERROR;
2568 }
2569 
2570 svn_error_t *
svn_ra_svn__write_cmd_stat(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,svn_revnum_t rev)2571 svn_ra_svn__write_cmd_stat(svn_ra_svn_conn_t *conn,
2572                            apr_pool_t *pool,
2573                            const char *path,
2574                            svn_revnum_t rev)
2575 {
2576   SVN_ERR(writebuf_write_literal(conn, pool, "( stat ( "));
2577   SVN_ERR(write_tuple_cstring(conn, pool, path));
2578   SVN_ERR(write_tuple_start_list(conn, pool));
2579   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2580   SVN_ERR(write_tuple_end_list(conn, pool));
2581   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2582 
2583   return SVN_NO_ERROR;
2584 }
2585 
2586 svn_error_t *
svn_ra_svn__write_cmd_get_file_revs(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,svn_revnum_t start,svn_revnum_t end,svn_boolean_t include_merged_revisions)2587 svn_ra_svn__write_cmd_get_file_revs(svn_ra_svn_conn_t *conn,
2588                                     apr_pool_t *pool,
2589                                     const char *path,
2590                                     svn_revnum_t start,
2591                                     svn_revnum_t end,
2592                                     svn_boolean_t include_merged_revisions)
2593 {
2594   SVN_ERR(writebuf_write_literal(conn, pool, "( get-file-revs ( "));
2595   SVN_ERR(write_tuple_cstring(conn, pool, path));
2596   SVN_ERR(write_tuple_start_list(conn, pool));
2597   SVN_ERR(write_tuple_revision_opt(conn, pool, start));
2598   SVN_ERR(write_tuple_end_list(conn, pool));
2599   SVN_ERR(write_tuple_start_list(conn, pool));
2600   SVN_ERR(write_tuple_revision_opt(conn, pool, end));
2601   SVN_ERR(write_tuple_end_list(conn, pool));
2602   SVN_ERR(write_tuple_boolean(conn, pool, include_merged_revisions));
2603   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2604 
2605   return SVN_NO_ERROR;
2606 }
2607 
2608 svn_error_t *
svn_ra_svn__write_cmd_lock(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,const char * comment,svn_boolean_t steal_lock,svn_revnum_t revnum)2609 svn_ra_svn__write_cmd_lock(svn_ra_svn_conn_t *conn,
2610                            apr_pool_t *pool,
2611                            const char *path,
2612                            const char *comment,
2613                            svn_boolean_t steal_lock,
2614                            svn_revnum_t revnum)
2615 {
2616   SVN_ERR(writebuf_write_literal(conn, pool, "( lock ( "));
2617   SVN_ERR(write_tuple_cstring(conn, pool, path));
2618   SVN_ERR(write_tuple_start_list(conn, pool));
2619   SVN_ERR(write_tuple_cstring_opt(conn, pool, comment));
2620   SVN_ERR(write_tuple_end_list(conn, pool));
2621   SVN_ERR(write_tuple_boolean(conn, pool, steal_lock));
2622   SVN_ERR(write_tuple_start_list(conn, pool));
2623   SVN_ERR(write_tuple_revision_opt(conn, pool, revnum));
2624   SVN_ERR(write_tuple_end_list(conn, pool));
2625   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2626 
2627   return SVN_NO_ERROR;
2628 }
2629 
2630 svn_error_t *
svn_ra_svn__write_cmd_unlock(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,const svn_string_t * token,svn_boolean_t break_lock)2631 svn_ra_svn__write_cmd_unlock(svn_ra_svn_conn_t *conn,
2632                              apr_pool_t *pool,
2633                              const char *path,
2634                              const svn_string_t *token,
2635                              svn_boolean_t break_lock)
2636 {
2637   SVN_ERR(writebuf_write_literal(conn, pool, "( unlock ( "));
2638   SVN_ERR(write_tuple_cstring(conn, pool, path));
2639   SVN_ERR(write_tuple_string_opt_list(conn, pool, token));
2640   SVN_ERR(write_tuple_boolean(conn, pool, break_lock));
2641   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2642 
2643   return SVN_NO_ERROR;
2644 }
2645 
2646 svn_error_t *
svn_ra_svn__write_cmd_get_lock(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path)2647 svn_ra_svn__write_cmd_get_lock(svn_ra_svn_conn_t *conn,
2648                                apr_pool_t *pool,
2649                                const char *path)
2650 {
2651   SVN_ERR(writebuf_write_literal(conn, pool, "( get-lock ( "));
2652   SVN_ERR(write_tuple_cstring(conn, pool, path));
2653   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2654 
2655   return SVN_NO_ERROR;
2656 }
2657 
2658 svn_error_t *
svn_ra_svn__write_cmd_get_locks(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,svn_depth_t depth)2659 svn_ra_svn__write_cmd_get_locks(svn_ra_svn_conn_t *conn,
2660                                 apr_pool_t *pool,
2661                                 const char *path,
2662                                 svn_depth_t depth)
2663 {
2664   SVN_ERR(writebuf_write_literal(conn, pool, "( get-locks ( "));
2665   SVN_ERR(write_tuple_cstring(conn, pool, path));
2666   SVN_ERR(write_tuple_start_list(conn, pool));
2667   SVN_ERR(write_tuple_depth(conn, pool, depth));
2668   SVN_ERR(write_tuple_end_list(conn, pool));
2669   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2670 
2671   return SVN_NO_ERROR;
2672 }
2673 
2674 svn_error_t *
svn_ra_svn__write_cmd_replay(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t rev,svn_revnum_t low_water_mark,svn_boolean_t send_deltas)2675 svn_ra_svn__write_cmd_replay(svn_ra_svn_conn_t *conn,
2676                              apr_pool_t *pool,
2677                              svn_revnum_t rev,
2678                              svn_revnum_t low_water_mark,
2679                              svn_boolean_t send_deltas)
2680 {
2681   SVN_ERR(writebuf_write_literal(conn, pool, "( replay ( "));
2682   SVN_ERR(write_tuple_revision(conn, pool, rev));
2683   SVN_ERR(write_tuple_revision(conn, pool, low_water_mark));
2684   SVN_ERR(write_tuple_boolean(conn, pool, send_deltas));
2685   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2686 
2687   return SVN_NO_ERROR;
2688 }
2689 
2690 svn_error_t *
svn_ra_svn__write_cmd_replay_range(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t start_revision,svn_revnum_t end_revision,svn_revnum_t low_water_mark,svn_boolean_t send_deltas)2691 svn_ra_svn__write_cmd_replay_range(svn_ra_svn_conn_t *conn,
2692                                    apr_pool_t *pool,
2693                                    svn_revnum_t start_revision,
2694                                    svn_revnum_t end_revision,
2695                                    svn_revnum_t low_water_mark,
2696                                    svn_boolean_t send_deltas)
2697 {
2698   SVN_ERR(writebuf_write_literal(conn, pool, "( replay-range ( "));
2699   SVN_ERR(write_tuple_revision(conn, pool, start_revision));
2700   SVN_ERR(write_tuple_revision(conn, pool, end_revision));
2701   SVN_ERR(write_tuple_revision(conn, pool, low_water_mark));
2702   SVN_ERR(write_tuple_boolean(conn, pool, send_deltas));
2703   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2704 
2705   return SVN_NO_ERROR;
2706 }
2707 
2708 svn_error_t *
svn_ra_svn__write_cmd_get_deleted_rev(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,svn_revnum_t peg_revision,svn_revnum_t end_revision)2709 svn_ra_svn__write_cmd_get_deleted_rev(svn_ra_svn_conn_t *conn,
2710                                       apr_pool_t *pool,
2711                                       const char *path,
2712                                       svn_revnum_t peg_revision,
2713                                       svn_revnum_t end_revision)
2714 {
2715   SVN_ERR(writebuf_write_literal(conn, pool, "( get-deleted-rev ( "));
2716   SVN_ERR(write_tuple_cstring(conn, pool, path));
2717   SVN_ERR(write_tuple_revision(conn, pool, peg_revision));
2718   SVN_ERR(write_tuple_revision(conn, pool, end_revision));
2719   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2720 
2721   return SVN_NO_ERROR;
2722 }
2723 
2724 svn_error_t *
svn_ra_svn__write_cmd_get_iprops(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,svn_revnum_t revision)2725 svn_ra_svn__write_cmd_get_iprops(svn_ra_svn_conn_t *conn,
2726                                  apr_pool_t *pool,
2727                                  const char *path,
2728                                  svn_revnum_t revision)
2729 {
2730   SVN_ERR(writebuf_write_literal(conn, pool, "( get-iprops ( "));
2731   SVN_ERR(write_tuple_cstring(conn, pool, path));
2732   SVN_ERR(write_tuple_start_list(conn, pool));
2733   SVN_ERR(write_tuple_revision_opt(conn, pool, revision));
2734   SVN_ERR(write_tuple_end_list(conn, pool));
2735   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2736 
2737   return SVN_NO_ERROR;
2738 }
2739 
2740 svn_error_t *
svn_ra_svn__write_cmd_finish_replay(svn_ra_svn_conn_t * conn,apr_pool_t * pool)2741 svn_ra_svn__write_cmd_finish_replay(svn_ra_svn_conn_t *conn,
2742                                     apr_pool_t *pool)
2743 {
2744   return writebuf_write_literal(conn, pool, "( finish-replay ( ) ) ");
2745 }
2746 
svn_ra_svn__write_cmd_response(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * fmt,...)2747 svn_error_t *svn_ra_svn__write_cmd_response(svn_ra_svn_conn_t *conn,
2748                                             apr_pool_t *pool,
2749                                             const char *fmt, ...)
2750 {
2751   va_list ap;
2752   svn_error_t *err;
2753 
2754   SVN_ERR(writebuf_write_literal(conn, pool, "( success "));
2755   va_start(ap, fmt);
2756   err = vwrite_tuple(conn, pool, fmt, &ap);
2757   va_end(ap);
2758   return err ? svn_error_trace(err) : svn_ra_svn__end_list(conn, pool);
2759 }
2760 
svn_ra_svn__write_cmd_failure(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_error_t * err)2761 svn_error_t *svn_ra_svn__write_cmd_failure(svn_ra_svn_conn_t *conn,
2762                                            apr_pool_t *pool,
2763                                            const svn_error_t *err)
2764 {
2765   char buffer[128];
2766   SVN_ERR(writebuf_write_literal(conn, pool, "( failure ( "));
2767   for (; err; err = err->child)
2768     {
2769       const char *msg;
2770 
2771 #ifdef SVN_ERR__TRACING
2772       if (svn_error__is_tracing_link(err))
2773         msg = err->message;
2774       else
2775 #endif
2776         msg = svn_err_best_message(err, buffer, sizeof(buffer));
2777 
2778       /* The message string should have been optional, but we can't
2779          easily change that, so marshal nonexistent messages as "". */
2780       SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "nccn",
2781                                       (apr_uint64_t) err->apr_err,
2782                                       msg ? msg : "",
2783                                       err->file ? err->file : "",
2784                                       (apr_uint64_t) err->line));
2785     }
2786   return writebuf_write_literal(conn, pool, ") ) ");
2787 }
2788 
2789 /* Initializer for static svn_string_t . */
2790 #define STATIC_SVN_STRING(x) { x, sizeof(x) - 1 }
2791 
2792 /* Return a pre-cooked serialized representation for the changed path
2793    flags NODE_KIND, TEXT_MODIFIED and PROPS_MODIFIED.  If we don't
2794    have a suitable pre-cooked string, return an empty string. */
2795 static const svn_string_t *
changed_path_flags(svn_node_kind_t node_kind,svn_boolean_t text_modified,svn_boolean_t props_modified)2796 changed_path_flags(svn_node_kind_t node_kind,
2797                    svn_boolean_t text_modified,
2798                    svn_boolean_t props_modified)
2799 {
2800   static const svn_string_t file_flags[4]
2801     = { STATIC_SVN_STRING(" ) ( 4:file false false ) ) "),
2802         STATIC_SVN_STRING(" ) ( 4:file false true ) ) "),
2803         STATIC_SVN_STRING(" ) ( 4:file true false ) ) "),
2804         STATIC_SVN_STRING(" ) ( 4:file true true ) ) ") };
2805 
2806   static const svn_string_t dir_flags[4]
2807     = { STATIC_SVN_STRING(" ) ( 3:dir false false ) ) "),
2808         STATIC_SVN_STRING(" ) ( 3:dir false true ) ) "),
2809         STATIC_SVN_STRING(" ) ( 3:dir true false ) ) "),
2810         STATIC_SVN_STRING(" ) ( 3:dir true true ) ) ") };
2811 
2812   static const svn_string_t no_flags = STATIC_SVN_STRING("");
2813 
2814   /* Select the array based on the NODE_KIND. */
2815   const svn_string_t *flags;
2816   if (node_kind == svn_node_file)
2817     flags = file_flags;
2818   else if (node_kind == svn_node_dir)
2819     flags = dir_flags;
2820   else
2821     return &no_flags;
2822 
2823   /* Select the correct array entry. */
2824   if (text_modified)
2825     flags += 2;
2826   if (props_modified)
2827     flags++;
2828 
2829   return flags;
2830 }
2831 
2832 svn_error_t *
svn_ra_svn__write_data_log_changed_path(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * path,char action,const char * copyfrom_path,svn_revnum_t copyfrom_rev,svn_node_kind_t node_kind,svn_boolean_t text_modified,svn_boolean_t props_modified)2833 svn_ra_svn__write_data_log_changed_path(svn_ra_svn_conn_t *conn,
2834                                         apr_pool_t *pool,
2835                                         const svn_string_t *path,
2836                                         char action,
2837                                         const char *copyfrom_path,
2838                                         svn_revnum_t copyfrom_rev,
2839                                         svn_node_kind_t node_kind,
2840                                         svn_boolean_t text_modified,
2841                                         svn_boolean_t props_modified)
2842 {
2843   apr_size_t path_len = path->len;
2844   apr_size_t copyfrom_len = copyfrom_path ? strlen(copyfrom_path) : 0;
2845   const svn_string_t *flags_str = changed_path_flags(node_kind,
2846                                                      text_modified,
2847                                                      props_modified);
2848   apr_size_t flags_len = flags_str->len;
2849 
2850   /* How much buffer space can we use for non-string data (worst case)? */
2851   apr_size_t max_fill = sizeof(conn->write_buf)
2852                       - 2                          /* list start */
2853                       - 2 - SVN_INT64_BUFFER_SIZE  /* path */
2854                       - 2                          /* action */
2855                       - 2                          /* list start */
2856                       - 2 - SVN_INT64_BUFFER_SIZE  /* copy-from path */
2857                       - 1 - SVN_INT64_BUFFER_SIZE; /* copy-from rev */
2858 
2859   /* If the remaining buffer is big enough and we've got all parts,
2860      directly copy into the buffer.   On platforms with segmented memory,
2861      PATH_LEN + COPYFROM_LEN might actually be close to APR_SIZE_MAX.
2862      Blindly doing arithmetic on them might cause an overflow.
2863      The sum in here cannot overflow because WRITE_BUF is small, i.e.
2864      MAX_FILL and WRITE_POS are much smaller than APR_SIZE_MAX. */
2865   if (   (path_len <= max_fill) && (copyfrom_len <= max_fill)
2866       && (conn->write_pos + path_len + copyfrom_len + flags_len <= max_fill)
2867       && (flags_len > 0))
2868     {
2869       /* Quick path. */
2870       /* Open list. */
2871       char *p = conn->write_buf + conn->write_pos;
2872       p[0] = '(';
2873       p[1] = ' ';
2874 
2875       /* Write path. */
2876       p = write_ncstring_quick(p + 2, path->data, path_len);
2877 
2878       /* Action */
2879       p[0] = action;
2880       p[1] = ' ';
2881       p[2] = '(';
2882 
2883       /* Copy-from info (if given) */
2884       if (copyfrom_path)
2885         {
2886           p[3] = ' ';
2887           p = write_ncstring_quick(p + 4, copyfrom_path, copyfrom_len);
2888           p += svn__ui64toa(p, copyfrom_rev);
2889         }
2890       else
2891         {
2892           p += 3;
2893         }
2894 
2895       /* Close with flags. */
2896       memcpy(p, flags_str->data, flags_str->len);
2897       conn->write_pos = p + flags_str->len - conn->write_buf;
2898     }
2899   else
2900     {
2901       /* Standard code path (fallback). */
2902       SVN_ERR(write_tuple_start_list(conn, pool));
2903 
2904       SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, path->data, path_len));
2905       SVN_ERR(writebuf_writechar(conn, pool, action));
2906       SVN_ERR(writebuf_writechar(conn, pool, ' '));
2907       SVN_ERR(write_tuple_start_list(conn, pool));
2908       SVN_ERR(write_tuple_cstring_opt(conn, pool, copyfrom_path));
2909       SVN_ERR(write_tuple_revision_opt(conn, pool, copyfrom_rev));
2910       SVN_ERR(write_tuple_end_list(conn, pool));
2911       SVN_ERR(write_tuple_start_list(conn, pool));
2912       SVN_ERR(write_tuple_cstring(conn, pool, svn_node_kind_to_word(node_kind)));
2913       SVN_ERR(write_tuple_boolean(conn, pool, text_modified));
2914       SVN_ERR(write_tuple_boolean(conn, pool, props_modified));
2915 
2916       SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2917     }
2918 
2919   return SVN_NO_ERROR;
2920 }
2921 
2922 svn_error_t *
svn_ra_svn__write_data_log_entry(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t revision,const svn_string_t * author,const svn_string_t * date,const svn_string_t * message,svn_boolean_t has_children,svn_boolean_t invalid_revnum,unsigned revprop_count)2923 svn_ra_svn__write_data_log_entry(svn_ra_svn_conn_t *conn,
2924                                  apr_pool_t *pool,
2925                                  svn_revnum_t revision,
2926                                  const svn_string_t *author,
2927                                  const svn_string_t *date,
2928                                  const svn_string_t *message,
2929                                  svn_boolean_t has_children,
2930                                  svn_boolean_t invalid_revnum,
2931                                  unsigned revprop_count)
2932 {
2933   SVN_ERR(write_tuple_revision(conn, pool, revision));
2934   SVN_ERR(write_tuple_string_opt_list(conn, pool, author));
2935   SVN_ERR(write_tuple_string_opt_list(conn, pool, date));
2936   SVN_ERR(write_tuple_string_opt_list(conn, pool, message));
2937   SVN_ERR(write_tuple_boolean(conn, pool, has_children));
2938   SVN_ERR(write_tuple_boolean(conn, pool, invalid_revnum));
2939   SVN_ERR(svn_ra_svn__write_number(conn, pool, revprop_count));
2940 
2941   return SVN_NO_ERROR;
2942 }
2943 
2944 svn_error_t *
svn_ra_svn__write_dirent(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,svn_dirent_t * dirent,apr_uint32_t dirent_fields)2945 svn_ra_svn__write_dirent(svn_ra_svn_conn_t *conn,
2946                          apr_pool_t *pool,
2947                          const char *path,
2948                          svn_dirent_t *dirent,
2949                          apr_uint32_t dirent_fields)
2950 {
2951   const char *kind = (dirent_fields & SVN_DIRENT_KIND)
2952                    ? svn_node_kind_to_word(dirent->kind)
2953                    : "unknown";
2954 
2955   if (dirent_fields & ~SVN_DIRENT_KIND)
2956     {
2957       SVN_ERR(write_tuple_start_list(conn, pool));
2958       SVN_ERR(write_tuple_cstring(conn, pool, path));
2959       SVN_ERR(writebuf_write(conn, pool, kind, strlen(kind)));
2960 
2961       SVN_ERR(writebuf_write_literal(conn, pool, " ( "));
2962       if (dirent_fields & SVN_DIRENT_SIZE)
2963         SVN_ERR(svn_ra_svn__write_number(conn, pool, dirent->size));
2964 
2965       SVN_ERR(writebuf_write_literal(conn, pool, ") ( "));
2966       if (dirent_fields & SVN_DIRENT_HAS_PROPS)
2967         SVN_ERR(write_tuple_boolean(conn, pool, dirent->has_props));
2968 
2969       SVN_ERR(writebuf_write_literal(conn, pool, ") ( "));
2970       if (dirent_fields & SVN_DIRENT_CREATED_REV)
2971         SVN_ERR(write_tuple_revision(conn, pool, dirent->created_rev));
2972 
2973       SVN_ERR(writebuf_write_literal(conn, pool, ") ( "));
2974       if (dirent_fields & SVN_DIRENT_TIME)
2975         SVN_ERR(write_tuple_cstring_opt(conn, pool,
2976                                   svn_time_to_cstring(dirent->time, pool)));
2977 
2978       SVN_ERR(writebuf_write_literal(conn, pool, ") ( "));
2979       if (dirent_fields & SVN_DIRENT_LAST_AUTHOR)
2980         SVN_ERR(write_tuple_cstring_opt(conn, pool, dirent->last_author));
2981 
2982       SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2983     }
2984   else
2985     {
2986       SVN_ERR(write_tuple_start_list(conn, pool));
2987       SVN_ERR(write_tuple_cstring(conn, pool, path));
2988       SVN_ERR(writebuf_write(conn, pool, kind, strlen(kind)));
2989       SVN_ERR(writebuf_write_literal(conn, pool, " ) "));
2990     }
2991 
2992   return SVN_NO_ERROR;
2993 }
2994 
2995 /* If condition COND is not met, return a "malformed network data" error.
2996  */
2997 #define CHECK_PROTOCOL_COND(cond)\
2998   if (!(cond)) \
2999     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, \
3000                             _("Malformed network data"));
3001 
3002 /* In *RESULT, return the SVN-style string at index IDX in tuple ITEMS.
3003  */
3004 static svn_error_t *
svn_ra_svn__read_string(const svn_ra_svn__list_t * items,int idx,svn_string_t ** result)3005 svn_ra_svn__read_string(const svn_ra_svn__list_t *items,
3006                         int idx,
3007                         svn_string_t **result)
3008 {
3009   svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
3010   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING);
3011   *result = &elt->u.string;
3012 
3013   return SVN_NO_ERROR;
3014 }
3015 
3016 /* In *RESULT, return the C-style string at index IDX in tuple ITEMS.
3017  */
3018 static svn_error_t *
svn_ra_svn__read_cstring(const svn_ra_svn__list_t * items,int idx,const char ** result)3019 svn_ra_svn__read_cstring(const svn_ra_svn__list_t *items,
3020                          int idx,
3021                          const char **result)
3022 {
3023   svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
3024   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING);
3025   *result = elt->u.string.data;
3026 
3027   return SVN_NO_ERROR;
3028 }
3029 
3030 /* In *RESULT, return the word at index IDX in tuple ITEMS.
3031  */
3032 static svn_error_t *
svn_ra_svn__read_word(const svn_ra_svn__list_t * items,int idx,const char ** result)3033 svn_ra_svn__read_word(const svn_ra_svn__list_t *items,
3034                       int idx,
3035                       const char **result)
3036 {
3037   svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
3038   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD);
3039   *result = elt->u.word.data;
3040 
3041   return SVN_NO_ERROR;
3042 }
3043 
3044 /* In *RESULT, return the revision at index IDX in tuple ITEMS.
3045  */
3046 static svn_error_t *
svn_ra_svn__read_revision(const svn_ra_svn__list_t * items,int idx,svn_revnum_t * result)3047 svn_ra_svn__read_revision(const svn_ra_svn__list_t *items,
3048                           int idx,
3049                           svn_revnum_t *result)
3050 {
3051   svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
3052   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_NUMBER);
3053   *result = (svn_revnum_t)elt->u.number;
3054 
3055   return SVN_NO_ERROR;
3056 }
3057 
3058 /* In *RESULT, return the boolean at index IDX in tuple ITEMS.
3059  */
3060 static svn_error_t *
svn_ra_svn__read_boolean(const svn_ra_svn__list_t * items,int idx,apr_uint64_t * result)3061 svn_ra_svn__read_boolean(const svn_ra_svn__list_t *items,
3062                          int idx,
3063                          apr_uint64_t *result)
3064 {
3065   svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
3066   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD);
3067   if (svn_string_compare(&elt->u.word, &str_true))
3068     *result = TRUE;
3069   else if (svn_string_compare(&elt->u.word, &str_false))
3070     *result = FALSE;
3071   else
3072     CHECK_PROTOCOL_COND(FALSE);
3073 
3074   return SVN_NO_ERROR;
3075 }
3076 
3077 /* In *RESULT, return the tuple at index IDX in tuple ITEMS.
3078  */
3079 static svn_error_t *
svn_ra_svn__read_list(const svn_ra_svn__list_t * items,int idx,const svn_ra_svn__list_t ** result)3080 svn_ra_svn__read_list(const svn_ra_svn__list_t *items,
3081                       int idx,
3082                       const svn_ra_svn__list_t **result)
3083 {
3084   svn_ra_svn__item_t *elt  = &SVN_RA_SVN__LIST_ITEM(items, idx);
3085   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_LIST);
3086 
3087   *result = &elt->u.list;
3088   return SVN_NO_ERROR;
3089 }
3090 
3091 /* Verify the tuple ITEMS contains at least MIN and at most MAX elements.
3092  */
3093 static svn_error_t *
svn_ra_svn__read_check_array_size(const svn_ra_svn__list_t * items,int min,int max)3094 svn_ra_svn__read_check_array_size(const svn_ra_svn__list_t *items,
3095                                   int min,
3096                                   int max)
3097 {
3098   CHECK_PROTOCOL_COND(items->nelts >= min && items->nelts <= max);
3099   return SVN_NO_ERROR;
3100 }
3101 
3102 svn_error_t *
svn_ra_svn__read_data_log_changed_entry(const svn_ra_svn__list_t * items,svn_string_t ** cpath,const char ** action,const char ** copy_path,svn_revnum_t * copy_rev,const char ** kind_str,apr_uint64_t * text_mods,apr_uint64_t * prop_mods)3103 svn_ra_svn__read_data_log_changed_entry(const svn_ra_svn__list_t *items,
3104                                         svn_string_t **cpath,
3105                                         const char **action,
3106                                         const char **copy_path,
3107                                         svn_revnum_t *copy_rev,
3108                                         const char **kind_str,
3109                                         apr_uint64_t *text_mods,
3110                                         apr_uint64_t *prop_mods)
3111 {
3112   const svn_ra_svn__list_t *sub_items;
3113 
3114   /* initialize optional values */
3115   *copy_path = NULL;
3116   *copy_rev = SVN_INVALID_REVNUM;
3117   *kind_str = NULL;
3118   *text_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER;
3119   *prop_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER;
3120 
3121   /* top-level elements (mandatory) */
3122   SVN_ERR(svn_ra_svn__read_check_array_size(items, 3, INT_MAX));
3123   SVN_ERR(svn_ra_svn__read_string(items, 0, cpath));
3124   SVN_ERR(svn_ra_svn__read_word(items, 1, action));
3125 
3126   /* first sub-structure (mandatory) */
3127   SVN_ERR(svn_ra_svn__read_list(items, 2, &sub_items));
3128   if (sub_items->nelts)
3129     {
3130       SVN_ERR(svn_ra_svn__read_check_array_size(sub_items, 2, 2));
3131       SVN_ERR(svn_ra_svn__read_cstring(sub_items, 0, copy_path));
3132       SVN_ERR(svn_ra_svn__read_revision(sub_items, 1, copy_rev));
3133     }
3134 
3135   /* second sub-structure (optional) */
3136   if (items->nelts >= 4)
3137     {
3138       SVN_ERR(svn_ra_svn__read_list(items, 3, &sub_items));
3139       switch (MIN(3, sub_items->nelts))
3140         {
3141           case 3 : SVN_ERR(svn_ra_svn__read_boolean(sub_items, 2, prop_mods));
3142           case 2 : SVN_ERR(svn_ra_svn__read_boolean(sub_items, 1, text_mods));
3143           case 1 : SVN_ERR(svn_ra_svn__read_cstring(sub_items, 0, kind_str));
3144           default: break;
3145         }
3146     }
3147 
3148   return SVN_NO_ERROR;
3149 }
3150