2 * marshal.c : Marshalling routines for Subversion protocol
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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
21 * ====================================================================
29 #define APR_WANT_STRFUNC
31 #include <apr_general.h>
33 #include <apr_strings.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"
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"
53 #define svn_iswhitespace(c) ((c) == ' ' || (c) == '\n')
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. */
59 #define SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD (0x100000)
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.
66 #define MAX_WORD_LENGTH 25
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.
73 #define ITEM_NESTING_LIMIT 64
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");
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
82 get_timeout(svn_ra_svn_conn_t *conn)
84 return conn->block_handler ? 0 : -1;
87 /* --- Public / private API data conversion --- */
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)
94 target->kind = source->kind;
97 case SVN_RA_SVN_STRING:
98 target->u.string = svn_string_dup(&source->u.string, result_pool);
100 case SVN_RA_SVN_NUMBER:
101 target->u.number = source->u.number;
103 case SVN_RA_SVN_WORD:
104 target->u.word = source->u.word.data;
106 case SVN_RA_SVN_LIST:
107 target->u.list = svn_ra_svn__to_public_array(&source->u.list,
114 svn_ra_svn__to_public_array(const svn_ra_svn__list_t *source,
115 apr_pool_t *result_pool)
117 apr_array_header_t *result = apr_array_make(result_pool, source->nelts,
118 sizeof(svn_ra_svn_item_t));
121 for (i = 0; i < source->nelts; ++i)
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);
126 svn_ra_svn__to_public_item(sub_target, sub_source, 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)
137 target->kind = source->kind;
138 switch (source->kind)
140 case SVN_RA_SVN_STRING:
141 target->u.string = *source->u.string;
143 case SVN_RA_SVN_NUMBER:
144 target->u.number = source->u.number;
146 case SVN_RA_SVN_WORD:
147 target->u.word.data = source->u.word;
148 target->u.word.len = strlen(source->u.word);
150 case SVN_RA_SVN_LIST:
151 target->u.list = *svn_ra_svn__to_private_array(source->u.list,
158 svn_ra_svn__to_private_array(const apr_array_header_t *source,
159 apr_pool_t *result_pool)
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));
168 for (i = 0; i < source->nelts; ++i)
170 svn_ra_svn__item_t *sub_target = &result->items[i];
171 svn_ra_svn_item_t *sub_source = &APR_ARRAY_IDX(source, i,
174 svn_ra_svn__to_private_item(sub_target, sub_source, result_pool);
180 /* --- CONNECTION INITIALIZATION --- */
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,
189 apr_uint64_t max_out,
190 apr_pool_t *result_pool)
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);
196 assert((sock && !in_stream && !out_stream)
197 || (!sock && in_stream && out_stream));
200 conn->encrypted = FALSE;
202 conn->session = NULL;
203 conn->read_ptr = conn->read_buf;
204 conn->read_end = conn->read_buf;
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;
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));
231 conn->stream = svn_ra_svn__stream_from_streams(in_stream, out_stream,
233 conn->remote_ip = NULL;
240 svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn,
241 const apr_array_header_t *list)
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));
249 svn_ra_svn__set_capabilities(svn_ra_svn_conn_t *conn,
250 const svn_ra_svn__list_t *list)
253 svn_ra_svn__item_t *item;
256 for (i = 0; i < list->nelts; i++)
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);
269 svn_ra_svn__svndiff_version(svn_ra_svn_conn_t *conn)
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)
276 /* Prefer SVNDIFF2 over SVNDIFF1. */
277 if (svn_ra_svn_has_capability(conn, SVN_RA_SVN_CAP_SVNDIFF2_ACCEPTED))
279 if (svn_ra_svn_has_capability(conn, SVN_RA_SVN_CAP_SVNDIFF1))
282 /* The connection does not support SVNDIFF1/2; default to "version 0". */
287 svn_ra_svn__get_pool(svn_ra_svn_conn_t *conn)
293 svn_ra_svn__set_shim_callbacks(svn_ra_svn_conn_t *conn,
294 svn_delta_shim_callbacks_t *shim_callbacks)
296 conn->shim_callbacks = shim_callbacks;
300 svn_boolean_t svn_ra_svn_has_capability(svn_ra_svn_conn_t *conn,
301 const char *capability)
303 return (svn_hash_gets(conn->capabilities, capability) != NULL);
307 svn_ra_svn_compression_level(svn_ra_svn_conn_t *conn)
309 return conn->compression_level;
313 svn_ra_svn_zero_copy_limit(svn_ra_svn_conn_t *conn)
315 return conn->zero_copy_limit;
318 const char *svn_ra_svn_conn_remote_host(svn_ra_svn_conn_t *conn)
320 return conn->remote_ip;
324 svn_ra_svn__set_block_handler(svn_ra_svn_conn_t *conn,
325 ra_svn_block_handler_t handler,
328 conn->block_handler = handler;
329 conn->block_baton = baton;
330 svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn));
333 svn_error_t *svn_ra_svn__data_available(svn_ra_svn_conn_t *conn,
334 svn_boolean_t *data_available)
336 return svn_ra_svn__stream_data_available(conn->stream, data_available);
340 svn_ra_svn__reset_command_io_counters(svn_ra_svn_conn_t *conn)
342 conn->current_in = 0;
343 conn->current_out = 0;
347 /* --- WRITE BUFFER MANAGEMENT --- */
349 /* Return an error object if CONN exceeded its send or receive limits. */
351 check_io_limits(svn_ra_svn_conn_t *conn)
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 "
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 "
366 /* Write data to socket or output file as appropriate. */
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)
370 const char *end = data + len;
372 apr_pool_t *subpool = NULL;
373 svn_ra_svn__session_baton_t *session = conn->session;
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));
385 if (session && session->callbacks && session->callbacks->cancel_func)
386 SVN_ERR((session->callbacks->cancel_func)(session->callbacks_baton));
388 SVN_ERR(svn_ra_svn__stream_write(conn->stream, data, &count));
392 subpool = svn_pool_create(pool);
394 svn_pool_clear(subpool);
395 SVN_ERR(conn->block_handler(conn, subpool, conn->block_baton));
401 const svn_ra_callbacks2_t *cb = session->callbacks;
402 session->bytes_written += count;
404 if (cb && cb->progress_func)
405 (cb->progress_func)(session->bytes_written + session->bytes_read,
406 -1, cb->progress_baton, subpool);
410 conn->written_since_error_check += len;
411 conn->may_check_for_error
412 = conn->written_since_error_check >= conn->error_check_interval;
415 svn_pool_destroy(subpool);
419 /* Write data from the write buffer out to the socket. */
420 static svn_error_t *writebuf_flush(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
422 apr_size_t write_pos = conn->write_pos;
424 /* Clear conn->write_pos first in case the block handler does a read. */
426 SVN_ERR(writebuf_output(conn, pool, conn->write_buf, write_pos));
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)
433 /* data >= 8k is sent immediately */
434 if (len >= sizeof(conn->write_buf) / 2)
436 if (conn->write_pos > 0)
437 SVN_ERR(writebuf_flush(conn, pool));
439 return writebuf_output(conn, pool, data, len);
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));
446 /* buffer the new data block as well */
447 memcpy(conn->write_buf + conn->write_pos, data, len);
448 conn->write_pos += len;
453 /* Write STRING_LITERAL, which is a string literal argument.
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)
461 static APR_INLINE svn_error_t *
462 writebuf_writechar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char data)
464 if (conn->write_pos < sizeof(conn->write_buf))
466 conn->write_buf[conn->write_pos] = data;
474 return writebuf_write(conn, pool, &temp, 1);
478 /* --- READ BUFFER MANAGEMENT --- */
480 /* Read bytes into DATA until either the read buffer is empty or
482 static char *readbuf_drain(svn_ra_svn_conn_t *conn, char *data, char *end)
484 apr_ssize_t buflen, copylen;
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;
493 /* Read data from socket or input file as appropriate. */
494 static svn_error_t *readbuf_input(svn_ra_svn_conn_t *conn, char *data,
495 apr_size_t *len, apr_pool_t *pool)
497 svn_ra_svn__session_baton_t *session = conn->session;
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));
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));
507 /* Actually fill the buffer. */
508 SVN_ERR(svn_ra_svn__stream_read(conn->stream, data, len));
510 return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL);
511 conn->current_in += *len;
515 const svn_ra_callbacks2_t *cb = session->callbacks;
516 session->bytes_read += *len;
518 if (cb && cb->progress_func)
519 (cb->progress_func)(session->bytes_read + session->bytes_written,
520 -1, cb->progress_baton, pool);
526 /* Treat the next LEN input bytes from CONN as "read" */
527 static svn_error_t *readbuf_skip(svn_ra_svn_conn_t *conn, apr_uint64_t len)
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;
538 buflen = sizeof(conn->read_buf);
539 SVN_ERR(svn_ra_svn__stream_read(conn->stream, conn->read_buf, &buflen));
541 return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL);
543 conn->read_end = conn->read_buf + buflen;
544 conn->read_ptr = conn->read_buf;
551 /* Read data from the socket into the read buffer, which must be empty. */
552 static svn_error_t *readbuf_fill(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
556 SVN_ERR_ASSERT(conn->read_ptr == conn->read_end);
558 /* Make sure we tell the other side everything we have to say before
559 * reading / waiting for an answer. */
561 SVN_ERR(writebuf_flush(conn, pool));
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;
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.
575 static SVN__FORCE_INLINE svn_error_t *
576 readbuf_getchar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char *result)
578 if (conn->read_ptr == conn->read_end)
579 SVN_ERR(readbuf_fill(conn, pool));
580 *result = *conn->read_ptr++;
584 static svn_error_t *readbuf_getchar_skip_whitespace(svn_ra_svn_conn_t *conn,
589 SVN_ERR(readbuf_getchar(conn, pool, result));
590 while (svn_iswhitespace(*result));
594 /* Read the next LEN bytes from CONN and copy them to *DATA. */
595 static svn_error_t *readbuf_read(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
596 char *data, apr_size_t len)
598 char *end = data + len;
601 /* Copy in an appropriate amount of data from the buffer. */
602 data = readbuf_drain(conn, data, end);
604 /* Read large chunks directly into buffer. */
605 while (end - data > (apr_ssize_t)sizeof(conn->read_buf))
607 SVN_ERR(writebuf_flush(conn, pool));
609 SVN_ERR(readbuf_input(conn, data, &count, pool));
615 /* The remaining amount to read is small; fill the buffer and
617 SVN_ERR(readbuf_fill(conn, pool));
618 data = readbuf_drain(conn, data, end);
624 static svn_error_t *readbuf_skip_leading_garbage(svn_ra_svn_conn_t *conn,
627 char buf[256]; /* Must be smaller than sizeof(conn->read_buf) - 1. */
630 svn_boolean_t lparen = FALSE;
632 SVN_ERR_ASSERT(conn->read_ptr == conn->read_end);
635 /* Read some data directly from the connection input source. */
637 SVN_ERR(readbuf_input(conn, buf, &len, pool));
640 /* Scan the data for '(' WS with a very simple state machine. */
641 for (p = buf; p < end; p++)
643 if (lparen && svn_iswhitespace(*p))
646 lparen = (*p == '(');
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
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);
662 /* --- WRITING DATA ITEMS --- */
664 static svn_error_t *write_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
665 apr_uint64_t number, char follow)
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));
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;
682 svn_ra_svn__write_number(svn_ra_svn_conn_t *conn,
686 return write_number(conn, pool, number, ' ');
689 /* Write string S of length LEN to TARGET and return the first position
690 after the written data.
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
698 write_ncstring_quick(char *target,
702 /* Write string length. */
705 *target = (char)(len + '0');
710 target += svn__ui64toa(target, len);
713 /* Separator & contents. */
715 memcpy(target + 1, s, len);
716 target[len + 1] = ' ';
718 /* First location after the string. */
719 return target + len + 2;
724 svn_ra_svn__write_ncstring(svn_ra_svn_conn_t *conn,
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;
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))
740 conn->write_pos = write_ncstring_quick(conn->write_buf
741 + conn->write_pos, s, len)
746 /* Slower fallback code. */
747 SVN_ERR(write_number(conn, pool, len, ':'));
749 SVN_ERR(writebuf_write(conn, pool, s, len));
750 SVN_ERR(writebuf_writechar(conn, pool, ' '));
757 svn_ra_svn__write_string(svn_ra_svn_conn_t *conn,
759 const svn_string_t *str)
761 SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, str->data, str->len));
766 svn_ra_svn__write_cstring(svn_ra_svn_conn_t *conn,
770 SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, s, strlen(s)));
775 svn_ra_svn__write_word(svn_ra_svn_conn_t *conn,
779 SVN_ERR(writebuf_write(conn, pool, word, strlen(word)));
780 SVN_ERR(writebuf_writechar(conn, pool, ' '));
786 svn_ra_svn__write_boolean(svn_ra_svn_conn_t *conn,
791 SVN_ERR(writebuf_write_literal(conn, pool, "true "));
793 SVN_ERR(writebuf_write_literal(conn, pool, "false "));
799 svn_ra_svn__write_proplist(svn_ra_svn_conn_t *conn,
803 apr_hash_index_t *hi;
804 const char *propname;
805 svn_string_t *propval;
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.
814 for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
816 apr_hash_this(hi, (const void **)&propname,
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));
830 svn_ra_svn__start_list(svn_ra_svn_conn_t *conn,
833 if (conn->write_pos + 2 <= sizeof(conn->write_buf))
835 conn->write_buf[conn->write_pos] = '(';
836 conn->write_buf[conn->write_pos+1] = ' ';
837 conn->write_pos += 2;
841 return writebuf_write(conn, pool, "( ", 2);
845 svn_ra_svn__end_list(svn_ra_svn_conn_t *conn,
848 if (conn->write_pos + 2 <= sizeof(conn->write_buf))
850 conn->write_buf[conn->write_pos] = ')';
851 conn->write_buf[conn->write_pos+1] = ' ';
852 conn->write_pos += 2;
856 return writebuf_write(conn, pool, ") ", 2);
860 svn_ra_svn__flush(svn_ra_svn_conn_t *conn,
863 SVN_ERR(writebuf_flush(conn, pool));
864 conn->may_check_for_error = TRUE;
869 /* --- WRITING TUPLES --- */
872 vwrite_tuple_cstring(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
874 const char *cstr = va_arg(*ap, const char *);
875 SVN_ERR_ASSERT(cstr);
876 return svn_ra_svn__write_cstring(conn, pool, cstr);
880 vwrite_tuple_cstring_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
882 const char *cstr = va_arg(*ap, const char *);
883 return cstr ? svn_ra_svn__write_cstring(conn, pool, cstr) : SVN_NO_ERROR;
887 vwrite_tuple_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
889 const svn_string_t *str = va_arg(*ap, const svn_string_t *);
891 return svn_ra_svn__write_string(conn, pool, str);
895 vwrite_tuple_string_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
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;
902 vwrite_tuple_word(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
904 const char *cstr = va_arg(*ap, const char *);
905 SVN_ERR_ASSERT(cstr);
906 return svn_ra_svn__write_word(conn, pool, cstr);
910 vwrite_tuple_word_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
912 const char *cstr = va_arg(*ap, const char *);
913 return cstr ? svn_ra_svn__write_word(conn, pool, cstr) : SVN_NO_ERROR;
917 vwrite_tuple_revision(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
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);
925 vwrite_tuple_revision_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
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)
934 vwrite_tuple_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
936 return svn_ra_svn__write_number(conn, pool, va_arg(*ap, apr_uint64_t));
940 vwrite_tuple_boolean(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
942 return svn_ra_svn__write_boolean(conn, pool, va_arg(*ap, svn_boolean_t));
946 write_tuple_cstring(svn_ra_svn_conn_t *conn,
950 SVN_ERR_ASSERT(cstr);
951 return svn_ra_svn__write_cstring(conn, pool, cstr);
955 write_tuple_cstring_opt(svn_ra_svn_conn_t *conn,
959 return cstr ? svn_ra_svn__write_cstring(conn, pool, cstr) : SVN_NO_ERROR;
963 write_tuple_string(svn_ra_svn_conn_t *conn,
965 const svn_string_t *str)
968 return svn_ra_svn__write_string(conn, pool, str);
972 write_tuple_string_opt(svn_ra_svn_conn_t *conn,
974 const svn_string_t *str)
976 return str ? svn_ra_svn__write_string(conn, pool, str) : SVN_NO_ERROR;
979 /* Optimized sending code for the "(s?)" pattern. */
981 write_tuple_string_opt_list(svn_ra_svn_conn_t *conn,
983 const svn_string_t *str)
989 return writebuf_write(conn, pool, "( ) ", 4);
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)
995 - SVN_INT64_BUFFER_SIZE /* string length + separator */
996 - 2; /* close list */
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))
1005 char *p = conn->write_buf + conn->write_pos;
1010 p = write_ncstring_quick(p + 2, str->data, str->len);
1015 conn->write_pos = p + 2 - conn->write_buf;
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));
1025 return SVN_NO_ERROR;
1028 static svn_error_t *
1029 write_tuple_start_list(svn_ra_svn_conn_t *conn,
1032 return svn_ra_svn__start_list(conn, pool);
1035 static svn_error_t *
1036 write_tuple_end_list(svn_ra_svn_conn_t *conn,
1039 return svn_ra_svn__end_list(conn, pool);
1042 static svn_error_t *
1043 write_tuple_revision(svn_ra_svn_conn_t *conn,
1047 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
1048 return svn_ra_svn__write_number(conn, pool, rev);
1051 static svn_error_t *
1052 write_tuple_revision_opt(svn_ra_svn_conn_t *conn,
1056 return SVN_IS_VALID_REVNUM(rev)
1057 ? svn_ra_svn__write_number(conn, pool, rev)
1061 static svn_error_t *
1062 write_tuple_boolean(svn_ra_svn_conn_t *conn,
1064 svn_boolean_t value)
1066 return svn_ra_svn__write_boolean(conn, pool, value);
1069 static svn_error_t *
1070 write_tuple_depth(svn_ra_svn_conn_t *conn,
1074 return svn_ra_svn__write_word(conn, pool, svn_depth_to_word(depth));
1078 static svn_error_t *
1079 write_cmd_add_node(svn_ra_svn_conn_t *conn,
1082 const svn_string_t *parent_token,
1083 const svn_string_t *token,
1084 const char *copy_path,
1085 svn_revnum_t copy_rev)
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));
1095 return SVN_NO_ERROR;
1098 static svn_error_t *
1099 write_cmd_open_node(svn_ra_svn_conn_t *conn,
1102 const svn_string_t *parent_token,
1103 const svn_string_t *token,
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));
1113 return SVN_NO_ERROR;
1116 static svn_error_t *
1117 write_cmd_change_node_prop(svn_ra_svn_conn_t *conn,
1119 const svn_string_t *token,
1121 const svn_string_t *value)
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));
1127 return SVN_NO_ERROR;
1130 static svn_error_t *
1131 write_cmd_absent_node(svn_ra_svn_conn_t *conn,
1134 const svn_string_t *token)
1136 SVN_ERR(write_tuple_cstring(conn, pool, path));
1137 SVN_ERR(write_tuple_string(conn, pool, token));
1139 return SVN_NO_ERROR;
1145 static svn_error_t *vwrite_tuple(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1146 const char *fmt, va_list *ap)
1148 svn_boolean_t opt = FALSE;
1153 SVN_ERR(svn_ra_svn__start_list(conn, pool));
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)
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));
1168 else if (*fmt == ')')
1170 SVN_ERR(write_tuple_end_list(conn, pool));
1172 /* OPT could not have been set when opening the list (see above),
1173 * hence this is correct and handles nested tuples just fine. */
1176 else if (*fmt == '?')
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;
1191 SVN_ERR_MALFUNCTION();
1193 SVN_ERR(svn_ra_svn__end_list(conn, pool));
1194 return SVN_NO_ERROR;
1198 svn_ra_svn__write_tuple(svn_ra_svn_conn_t *conn,
1200 const char *fmt, ...)
1206 err = vwrite_tuple(conn, pool, fmt, &ap);
1211 /* --- READING DATA ITEMS --- */
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. */
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)
1219 apr_size_t len = (apr_size_t)len64;
1220 apr_size_t readbuf_len;
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"));
1230 buflen = conn->read_end - conn->read_ptr;
1231 /* Shorter strings can be copied directly from the read buffer. */
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;
1241 svn_stringbuf_t *stringbuf;
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");
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);
1261 /* Read string data directly into the string structure.
1262 * Do it iteratively. */
1265 /* Determine length of chunk to read and re-alloc the buffer. */
1267 = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD
1269 : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD;
1271 svn_stringbuf_ensure(stringbuf, stringbuf->len + readbuf_len);
1272 dest = stringbuf->data + stringbuf->len;
1274 /* read data & update length info */
1275 SVN_ERR(readbuf_read(conn, pool, dest, readbuf_len));
1277 stringbuf->len += readbuf_len;
1282 /* zero-terminate the string */
1283 stringbuf->data[stringbuf->len] = '\0';
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;
1291 return SVN_NO_ERROR;
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
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,
1302 char c = first_char;
1304 svn_ra_svn__item_t *listitem;
1306 if (++level >= ITEM_NESTING_LIMIT)
1307 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1308 _("Items are nested too deeply"));
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))
1316 /* It's a number or a string. Read the number part, either way. */
1320 apr_uint64_t prev_val = val;
1321 SVN_ERR(readbuf_getchar(conn, pool, &c));
1322 if (!svn_ctype_isdigit(c))
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"));
1333 /* It's a string. */
1334 SVN_ERR(read_string(conn, pool, item, val));
1335 SVN_ERR(readbuf_getchar(conn, pool, &c));
1339 /* It's a number. */
1340 item->kind = SVN_RA_SVN_NUMBER;
1341 item->u.number = val;
1344 else if (svn_ctype_isalpha(c))
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;
1352 if (conn->read_ptr + MAX_WORD_LENGTH <= conn->read_end)
1354 /* Fast path: we can simply take a chunk from the read
1355 * buffer and inspect it with no overflow checks etc.
1357 * Copying these 24 bytes unconditionally is also faster
1358 * than a variable-sized memcpy. Note that P is at BUFFER[1].
1360 memcpy(p, conn->read_ptr, MAX_WORD_LENGTH - 1);
1363 /* This will terminate at P == END because of *END == NUL. */
1364 while (svn_ctype_isalnum(*p) || *p == '-')
1367 /* Only now do we mark data as actually read. */
1368 conn->read_ptr += p - buffer;
1372 /* Slow path. Byte-by-byte copying and checking for
1373 * input and output buffer boundaries. */
1374 for (p = buffer + 1; p != end; ++p)
1376 SVN_ERR(readbuf_getchar(conn, pool, p));
1377 if (!svn_ctype_isalnum(*p) && *p != '-')
1383 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1384 _("Word is too long"));
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;
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.
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
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]);
1412 /* Read in the list items. */
1413 item->kind = SVN_RA_SVN_LIST;
1416 SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1420 /* Auto-expand the list. */
1421 if (count == capacity)
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));
1427 capacity = 2 * capacity;
1430 listitem = &items[count];
1433 SVN_ERR(read_item(conn, pool, listitem, c, level));
1436 /* Store the list in ITEM - if not empty (= default). */
1439 item->u.list.nelts = count;
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));
1446 item->u.list.items = items;
1450 item->u.list.items = NULL;
1451 item->u.list.nelts = 0;
1454 SVN_ERR(readbuf_getchar(conn, pool, &c));
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;
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 *
1467 read_command_only(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1468 const char **item, char first_char)
1470 char c = first_char;
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))
1477 /* It's a number or a string. Read the number part, either way. */
1478 apr_uint64_t val, prev_val=0;
1483 SVN_ERR(readbuf_getchar(conn, pool, &c));
1484 if (!svn_ctype_isdigit(c))
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"));
1493 /* It's a string. */
1494 SVN_ERR(readbuf_skip(conn, val));
1495 SVN_ERR(readbuf_getchar(conn, pool, &c));
1498 else if (svn_ctype_isalpha(c))
1503 /* This is the word we want to read */
1505 char *buf = apr_palloc(pool, 32);
1511 SVN_ERR(readbuf_getchar(conn, pool, &c));
1512 if (!svn_ctype_isalnum(c) && c != '-')
1516 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1517 _("Word too long"));
1524 /* we don't need the actual word, just skip it */
1527 SVN_ERR(readbuf_getchar(conn, pool, &c));
1529 while (svn_ctype_isalnum(c) || c == '-');
1534 /* Read in the list items. */
1537 SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1541 if (item && *item == NULL)
1542 SVN_ERR(read_command_only(conn, pool, item, c));
1544 SVN_ERR(read_command_only(conn, pool, NULL, c));
1546 SVN_ERR(readbuf_getchar(conn, pool, &c));
1549 return SVN_NO_ERROR;
1553 svn_ra_svn__read_item(svn_ra_svn_conn_t *conn,
1555 svn_ra_svn__item_t **item)
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);
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.
1570 static svn_error_t *
1571 svn_ra_svn__has_item(svn_boolean_t *has_item,
1572 svn_ra_svn_conn_t *conn,
1577 if (conn->read_ptr == conn->read_end)
1579 svn_boolean_t available;
1580 if (conn->write_pos)
1581 SVN_ERR(writebuf_flush(conn, pool));
1583 SVN_ERR(svn_ra_svn__data_available(conn, &available));
1587 SVN_ERR(readbuf_fill(conn, pool));
1590 while (svn_iswhitespace(*conn->read_ptr) && ++conn->read_ptr);
1592 *has_item = conn->read_ptr != conn->read_end;
1593 return SVN_NO_ERROR;
1597 svn_ra_svn__skip_leading_garbage(svn_ra_svn_conn_t *conn,
1600 return readbuf_skip_leading_garbage(conn, pool);
1603 /* --- READING AND PARSING TUPLES --- */
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 *
1608 vparse_tuple(const svn_ra_svn__list_t *items,
1612 int count, nesting_level;
1613 svn_ra_svn__item_t *elt;
1615 for (count = 0; **fmt && count < items->nelts; (*fmt)++, count++)
1617 /* '?' just means the tuple may stop; skip past it. */
1620 elt = &SVN_RA_SVN__LIST_ITEM(items, count);
1621 if (**fmt == '(' && elt->kind == SVN_RA_SVN_LIST)
1624 SVN_ERR(vparse_tuple(&elt->u.list, fmt, ap));
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)
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;
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)
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;
1654 else if (**fmt == '3' && elt->kind == SVN_RA_SVN_WORD)
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;
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;
1673 for (; **fmt; (*fmt)++)
1680 *va_arg(*ap, svn_revnum_t *) = SVN_INVALID_REVNUM;
1683 *va_arg(*ap, svn_string_t **) = NULL;
1687 *va_arg(*ap, const char **) = NULL;
1690 *va_arg(*ap, svn_ra_svn__list_t **) = NULL;
1694 *va_arg(*ap, apr_uint64_t *) = SVN_RA_SVN_UNSPECIFIED_NUMBER;
1697 *va_arg(*ap, svn_tristate_t *) = svn_tristate_unknown;
1700 *va_arg(*ap, svn_boolean_t *) = FALSE;
1706 if (--nesting_level < 0)
1707 return SVN_NO_ERROR;
1710 SVN_ERR_MALFUNCTION();
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;
1721 svn_ra_svn__parse_tuple(const svn_ra_svn__list_t *list,
1722 const char *fmt, ...)
1728 err = vparse_tuple(list, &fmt, &ap);
1734 svn_ra_svn__read_tuple(svn_ra_svn_conn_t *conn,
1736 const char *fmt, ...)
1739 svn_ra_svn__item_t *item;
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"));
1747 err = vparse_tuple(&item->u.list, &fmt, &ap);
1753 svn_ra_svn__read_command_only(svn_ra_svn_conn_t *conn,
1755 const char **command)
1758 SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1761 return read_command_only(conn, pool, command, c);
1766 svn_ra_svn__parse_proplist(const svn_ra_svn__list_t *list,
1771 svn_string_t *value;
1772 svn_ra_svn__item_t *elt;
1775 *props = svn_hash__make(pool);
1776 for (i = 0; i < list->nelts; i++)
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);
1786 return SVN_NO_ERROR;
1790 /* --- READING AND WRITING COMMANDS AND RESPONSES --- */
1792 svn_error_t *svn_ra_svn__locate_real_error_child(svn_error_t *err)
1794 svn_error_t *this_link;
1796 SVN_ERR_ASSERT(err);
1798 for (this_link = err;
1799 this_link && (this_link->apr_err == SVN_ERR_RA_SVN_CMD_ERR);
1800 this_link = this_link->child)
1803 SVN_ERR_ASSERT(this_link);
1808 svn_ra_svn__handle_failure_status(const svn_ra_svn__list_t *params)
1810 const char *message, *file;
1811 svn_error_t *err = NULL;
1812 svn_ra_svn__item_t *elt;
1814 apr_uint64_t apr_err, line;
1816 if (params->nelts == 0)
1817 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1818 _("Empty error list"));
1820 /* Rebuild the error list from the end, to avoid reversing the order. */
1821 for (i = params->nelts - 1; i >= 0; i--)
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. */
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
1838 if ((apr_status_t)apr_err != SVN_ERR_RA_SVN_CMD_ERR)
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;
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. */
1849 err = svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1850 _("Malformed error list"));
1856 svn_ra_svn__read_cmd_response(svn_ra_svn_conn_t *conn,
1858 const char *fmt, ...)
1862 svn_ra_svn__list_t *params;
1865 SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "wl", &status, ¶ms));
1866 if (strcmp(status, "success") == 0)
1869 err = vparse_tuple(params, &fmt, &ap);
1873 else if (strcmp(status, "failure") == 0)
1875 return svn_error_trace(svn_ra_svn__handle_failure_status(params));
1878 return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1879 _("Unknown status '%s' in command response"),
1884 svn_ra_svn__has_command(svn_boolean_t *has_command,
1885 svn_boolean_t *terminated,
1886 svn_ra_svn_conn_t *conn,
1891 /* Don't make whitespace between commands trigger I/O limitiations. */
1892 svn_ra_svn__reset_command_io_counters(conn);
1894 err = svn_ra_svn__has_item(has_command, conn, pool);
1895 if (err && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED)
1898 svn_error_clear(err);
1899 return SVN_NO_ERROR;
1902 *terminated = FALSE;
1903 return svn_error_trace(err);
1907 svn_ra_svn__handle_command(svn_boolean_t *terminate,
1908 apr_hash_t *cmd_hash,
1910 svn_ra_svn_conn_t *conn,
1911 svn_boolean_t error_on_disconnect,
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;
1921 /* Limit I/O for every command separately. */
1922 svn_ra_svn__reset_command_io_counters(conn);
1924 err = svn_ra_svn__read_tuple(conn, pool, "wl", &cmdname, ¶ms);
1927 if (!error_on_disconnect
1928 && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED)
1930 svn_error_clear(err);
1932 return SVN_NO_ERROR;
1937 command = svn_hash_gets(cmd_hash, cmdname);
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)
1945 err = (*command->handler)(conn, pool, params, baton);
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,
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.
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);
1962 *terminate = command->terminate;
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);
1971 if (err && err->apr_err == SVN_ERR_RA_SVN_CMD_ERR)
1973 write_err = svn_ra_svn__write_cmd_failure(
1975 svn_ra_svn__locate_real_error_child(err));
1976 svn_error_clear(err);
1977 return write_err ? write_err : SVN_NO_ERROR;
1984 svn_ra_svn__handle_commands2(svn_ra_svn_conn_t *conn,
1986 const svn_ra_svn__cmd_entry_t *commands,
1988 svn_boolean_t error_on_disconnect)
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);
1995 for (command = commands; command->cmdname; command++)
1996 svn_hash_sets(cmd_hash, command->cmdname, command);
2000 svn_boolean_t terminate;
2002 svn_pool_clear(iterpool);
2004 err = svn_ra_svn__handle_command(&terminate, cmd_hash, baton, conn,
2005 error_on_disconnect, iterpool);
2008 svn_pool_destroy(subpool);
2009 return svn_error_trace(err);
2014 svn_pool_destroy(iterpool);
2015 svn_pool_destroy(subpool);
2016 return SVN_NO_ERROR;
2020 svn_ra_svn__write_cmd_target_rev(svn_ra_svn_conn_t *conn,
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, ") ) "));
2028 return SVN_NO_ERROR;
2032 svn_ra_svn__write_cmd_open_root(svn_ra_svn_conn_t *conn,
2035 const svn_string_t *token)
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, ") ) "));
2044 return SVN_NO_ERROR;
2048 svn_ra_svn__write_cmd_delete_entry(svn_ra_svn_conn_t *conn,
2052 const svn_string_t *token)
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, ") ) "));
2062 return SVN_NO_ERROR;
2066 svn_ra_svn__write_cmd_add_dir(svn_ra_svn_conn_t *conn,
2069 const svn_string_t *parent_token,
2070 const svn_string_t *token,
2071 const char *copy_path,
2072 svn_revnum_t copy_rev)
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, ") ) "));
2079 return SVN_NO_ERROR;
2083 svn_ra_svn__write_cmd_open_dir(svn_ra_svn_conn_t *conn,
2086 const svn_string_t *parent_token,
2087 const svn_string_t *token,
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, ") ) "));
2094 return SVN_NO_ERROR;
2098 svn_ra_svn__write_cmd_change_dir_prop(svn_ra_svn_conn_t *conn,
2100 const svn_string_t *token,
2102 const svn_string_t *value)
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, ") ) "));
2108 return SVN_NO_ERROR;
2112 svn_ra_svn__write_cmd_close_dir(svn_ra_svn_conn_t *conn,
2114 const svn_string_t *token)
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, ") ) "));
2120 return SVN_NO_ERROR;
2124 svn_ra_svn__write_cmd_absent_dir(svn_ra_svn_conn_t *conn,
2127 const svn_string_t *parent_token)
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, ") ) "));
2133 return SVN_NO_ERROR;
2137 svn_ra_svn__write_cmd_add_file(svn_ra_svn_conn_t *conn,
2140 const svn_string_t *parent_token,
2141 const svn_string_t *token,
2142 const char *copy_path,
2143 svn_revnum_t copy_rev)
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, ") ) "));
2150 return SVN_NO_ERROR;
2154 svn_ra_svn__write_cmd_open_file(svn_ra_svn_conn_t *conn,
2157 const svn_string_t *parent_token,
2158 const svn_string_t *token,
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, ") ) "));
2165 return SVN_NO_ERROR;
2169 svn_ra_svn__write_cmd_change_file_prop(svn_ra_svn_conn_t *conn,
2171 const svn_string_t *token,
2173 const svn_string_t *value)
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, ") ) "));
2179 return SVN_NO_ERROR;
2183 svn_ra_svn__write_cmd_close_file(svn_ra_svn_conn_t *conn,
2185 const svn_string_t *token,
2186 const char *text_checksum)
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, ") ) "));
2195 return SVN_NO_ERROR;
2199 svn_ra_svn__write_cmd_absent_file(svn_ra_svn_conn_t *conn,
2202 const svn_string_t *parent_token)
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, ") ) "));
2208 return SVN_NO_ERROR;
2212 svn_ra_svn__write_cmd_textdelta_chunk(svn_ra_svn_conn_t *conn,
2214 const svn_string_t *token,
2215 const svn_string_t *chunk)
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, ") ) "));
2222 return SVN_NO_ERROR;
2226 svn_ra_svn__write_cmd_textdelta_end(svn_ra_svn_conn_t *conn,
2228 const svn_string_t *token)
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, ") ) "));
2234 return SVN_NO_ERROR;
2238 svn_ra_svn__write_cmd_apply_textdelta(svn_ra_svn_conn_t *conn,
2240 const svn_string_t *token,
2241 const char *base_checksum)
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, ") ) "));
2250 return SVN_NO_ERROR;
2254 svn_ra_svn__write_cmd_close_edit(svn_ra_svn_conn_t *conn,
2257 return writebuf_write_literal(conn, pool, "( close-edit ( ) ) ");
2261 svn_ra_svn__write_cmd_abort_edit(svn_ra_svn_conn_t *conn,
2264 return writebuf_write_literal(conn, pool, "( abort-edit ( ) ) ");
2268 svn_ra_svn__write_cmd_set_path(svn_ra_svn_conn_t *conn,
2272 svn_boolean_t start_empty,
2273 const char *lock_token,
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, ") ) "));
2286 return SVN_NO_ERROR;
2290 svn_ra_svn__write_cmd_delete_path(svn_ra_svn_conn_t *conn,
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, ") ) "));
2298 return SVN_NO_ERROR;
2302 svn_ra_svn__write_cmd_link_path(svn_ra_svn_conn_t *conn,
2307 svn_boolean_t start_empty,
2308 const char *lock_token,
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, ") ) "));
2322 return SVN_NO_ERROR;
2326 svn_ra_svn__write_cmd_finish_report(svn_ra_svn_conn_t *conn,
2329 return writebuf_write_literal(conn, pool, "( finish-report ( ) ) ");
2333 svn_ra_svn__write_cmd_abort_report(svn_ra_svn_conn_t *conn,
2336 return writebuf_write_literal(conn, pool, "( abort-report ( ) ) ");
2340 svn_ra_svn__write_cmd_reparent(svn_ra_svn_conn_t *conn,
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, ") ) "));
2348 return SVN_NO_ERROR;
2352 svn_ra_svn__write_cmd_get_latest_rev(svn_ra_svn_conn_t *conn,
2355 return writebuf_write_literal(conn, pool, "( get-latest-rev ( ) ) ");
2359 svn_ra_svn__write_cmd_get_dated_rev(svn_ra_svn_conn_t *conn,
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, ") ) "));
2367 return SVN_NO_ERROR;
2371 svn_ra_svn__write_cmd_change_rev_prop2(svn_ra_svn_conn_t *conn,
2375 const svn_string_t *value,
2376 svn_boolean_t dont_care,
2377 const svn_string_t *old_value)
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, ") ) "));
2389 return SVN_NO_ERROR;
2393 svn_ra_svn__write_cmd_change_rev_prop(svn_ra_svn_conn_t *conn,
2397 const svn_string_t *value)
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, ") ) "));
2405 return SVN_NO_ERROR;
2409 svn_ra_svn__write_cmd_rev_proplist(svn_ra_svn_conn_t *conn,
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, ") ) "));
2417 return SVN_NO_ERROR;
2421 svn_ra_svn__write_cmd_rev_prop(svn_ra_svn_conn_t *conn,
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, ") ) "));
2431 return SVN_NO_ERROR;
2435 svn_ra_svn__write_cmd_get_file(svn_ra_svn_conn_t *conn,
2439 svn_boolean_t props,
2440 svn_boolean_t stream)
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));
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 ) ) "));
2455 return SVN_NO_ERROR;
2459 svn_ra_svn__write_cmd_update(svn_ra_svn_conn_t *conn,
2463 svn_boolean_t recurse,
2465 svn_boolean_t send_copyfrom_args,
2466 svn_boolean_t ignore_ancestry)
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, ") ) "));
2479 return SVN_NO_ERROR;
2483 svn_ra_svn__write_cmd_switch(svn_ra_svn_conn_t *conn,
2487 svn_boolean_t recurse,
2488 const char *switch_url,
2490 svn_boolean_t send_copyfrom_args,
2491 svn_boolean_t ignore_ancestry)
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, ") ) "));
2505 return SVN_NO_ERROR;
2509 svn_ra_svn__write_cmd_status(svn_ra_svn_conn_t *conn,
2512 svn_boolean_t recurse,
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, ") ) "));
2525 return SVN_NO_ERROR;
2529 svn_ra_svn__write_cmd_diff(svn_ra_svn_conn_t *conn,
2533 svn_boolean_t recurse,
2534 svn_boolean_t ignore_ancestry,
2535 const char *versus_url,
2536 svn_boolean_t text_deltas,
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, ") ) "));
2551 return SVN_NO_ERROR;
2555 svn_ra_svn__write_cmd_check_path(svn_ra_svn_conn_t *conn,
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, ") ) "));
2567 return SVN_NO_ERROR;
2571 svn_ra_svn__write_cmd_stat(svn_ra_svn_conn_t *conn,
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, ") ) "));
2583 return SVN_NO_ERROR;
2587 svn_ra_svn__write_cmd_get_file_revs(svn_ra_svn_conn_t *conn,
2592 svn_boolean_t include_merged_revisions)
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, ") ) "));
2605 return SVN_NO_ERROR;
2609 svn_ra_svn__write_cmd_lock(svn_ra_svn_conn_t *conn,
2612 const char *comment,
2613 svn_boolean_t steal_lock,
2614 svn_revnum_t revnum)
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, ") ) "));
2627 return SVN_NO_ERROR;
2631 svn_ra_svn__write_cmd_unlock(svn_ra_svn_conn_t *conn,
2634 const svn_string_t *token,
2635 svn_boolean_t break_lock)
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, ") ) "));
2643 return SVN_NO_ERROR;
2647 svn_ra_svn__write_cmd_get_lock(svn_ra_svn_conn_t *conn,
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, ") ) "));
2655 return SVN_NO_ERROR;
2659 svn_ra_svn__write_cmd_get_locks(svn_ra_svn_conn_t *conn,
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, ") ) "));
2671 return SVN_NO_ERROR;
2675 svn_ra_svn__write_cmd_replay(svn_ra_svn_conn_t *conn,
2678 svn_revnum_t low_water_mark,
2679 svn_boolean_t send_deltas)
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, ") ) "));
2687 return SVN_NO_ERROR;
2691 svn_ra_svn__write_cmd_replay_range(svn_ra_svn_conn_t *conn,
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)
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, ") ) "));
2705 return SVN_NO_ERROR;
2709 svn_ra_svn__write_cmd_get_deleted_rev(svn_ra_svn_conn_t *conn,
2712 svn_revnum_t peg_revision,
2713 svn_revnum_t end_revision)
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, ") ) "));
2721 return SVN_NO_ERROR;
2725 svn_ra_svn__write_cmd_get_iprops(svn_ra_svn_conn_t *conn,
2728 svn_revnum_t revision)
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, ") ) "));
2737 return SVN_NO_ERROR;
2741 svn_ra_svn__write_cmd_finish_replay(svn_ra_svn_conn_t *conn,
2744 return writebuf_write_literal(conn, pool, "( finish-replay ( ) ) ");
2747 svn_error_t *svn_ra_svn__write_cmd_response(svn_ra_svn_conn_t *conn,
2749 const char *fmt, ...)
2754 SVN_ERR(writebuf_write_literal(conn, pool, "( success "));
2756 err = vwrite_tuple(conn, pool, fmt, &ap);
2758 return err ? svn_error_trace(err) : svn_ra_svn__end_list(conn, pool);
2761 svn_error_t *svn_ra_svn__write_cmd_failure(svn_ra_svn_conn_t *conn,
2763 const svn_error_t *err)
2766 SVN_ERR(writebuf_write_literal(conn, pool, "( failure ( "));
2767 for (; err; err = err->child)
2771 #ifdef SVN_ERR__TRACING
2772 if (svn_error__is_tracing_link(err))
2776 msg = svn_err_best_message(err, buffer, sizeof(buffer));
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,
2783 err->file ? err->file : "",
2784 (apr_uint64_t) err->line));
2786 return writebuf_write_literal(conn, pool, ") ) ");
2789 /* Initializer for static svn_string_t . */
2790 #define STATIC_SVN_STRING(x) { x, sizeof(x) - 1 }
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 *
2796 changed_path_flags(svn_node_kind_t node_kind,
2797 svn_boolean_t text_modified,
2798 svn_boolean_t props_modified)
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 ) ) ") };
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 ) ) ") };
2812 static const svn_string_t no_flags = STATIC_SVN_STRING("");
2814 /* Select the array based on the NODE_KIND. */
2815 const svn_string_t *flags;
2816 if (node_kind == svn_node_file)
2818 else if (node_kind == svn_node_dir)
2823 /* Select the correct array entry. */
2833 svn_ra_svn__write_data_log_changed_path(svn_ra_svn_conn_t *conn,
2835 const svn_string_t *path,
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)
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,
2848 apr_size_t flags_len = flags_str->len;
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 */
2855 - 2 /* list start */
2856 - 2 - SVN_INT64_BUFFER_SIZE /* copy-from path */
2857 - 1 - SVN_INT64_BUFFER_SIZE; /* copy-from rev */
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)
2871 char *p = conn->write_buf + conn->write_pos;
2876 p = write_ncstring_quick(p + 2, path->data, path_len);
2883 /* Copy-from info (if given) */
2887 p = write_ncstring_quick(p + 4, copyfrom_path, copyfrom_len);
2888 p += svn__ui64toa(p, copyfrom_rev);
2895 /* Close with flags. */
2896 memcpy(p, flags_str->data, flags_str->len);
2897 conn->write_pos = p + flags_str->len - conn->write_buf;
2901 /* Standard code path (fallback). */
2902 SVN_ERR(write_tuple_start_list(conn, pool));
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));
2916 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2919 return SVN_NO_ERROR;
2923 svn_ra_svn__write_data_log_entry(svn_ra_svn_conn_t *conn,
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)
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));
2941 return SVN_NO_ERROR;
2945 svn_ra_svn__write_dirent(svn_ra_svn_conn_t *conn,
2948 svn_dirent_t *dirent,
2949 apr_uint32_t dirent_fields)
2951 const char *kind = (dirent_fields & SVN_DIRENT_KIND)
2952 ? svn_node_kind_to_word(dirent->kind)
2955 if (dirent_fields & ~SVN_DIRENT_KIND)
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)));
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));
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));
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));
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)));
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));
2982 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
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, " ) "));
2992 return SVN_NO_ERROR;
2995 /* If condition COND is not met, return a "malformed network data" error.
2997 #define CHECK_PROTOCOL_COND(cond)\
2999 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, \
3000 _("Malformed network data"));
3002 /* In *RESULT, return the SVN-style string at index IDX in tuple ITEMS.
3004 static svn_error_t *
3005 svn_ra_svn__read_string(const svn_ra_svn__list_t *items,
3007 svn_string_t **result)
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;
3013 return SVN_NO_ERROR;
3016 /* In *RESULT, return the C-style string at index IDX in tuple ITEMS.
3018 static svn_error_t *
3019 svn_ra_svn__read_cstring(const svn_ra_svn__list_t *items,
3021 const char **result)
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;
3027 return SVN_NO_ERROR;
3030 /* In *RESULT, return the word at index IDX in tuple ITEMS.
3032 static svn_error_t *
3033 svn_ra_svn__read_word(const svn_ra_svn__list_t *items,
3035 const char **result)
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;
3041 return SVN_NO_ERROR;
3044 /* In *RESULT, return the revision at index IDX in tuple ITEMS.
3046 static svn_error_t *
3047 svn_ra_svn__read_revision(const svn_ra_svn__list_t *items,
3049 svn_revnum_t *result)
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;
3055 return SVN_NO_ERROR;
3058 /* In *RESULT, return the boolean at index IDX in tuple ITEMS.
3060 static svn_error_t *
3061 svn_ra_svn__read_boolean(const svn_ra_svn__list_t *items,
3063 apr_uint64_t *result)
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))
3069 else if (svn_string_compare(&elt->u.word, &str_false))
3072 CHECK_PROTOCOL_COND(FALSE);
3074 return SVN_NO_ERROR;
3077 /* In *RESULT, return the tuple at index IDX in tuple ITEMS.
3079 static svn_error_t *
3080 svn_ra_svn__read_list(const svn_ra_svn__list_t *items,
3082 const svn_ra_svn__list_t **result)
3084 svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
3085 CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_LIST);
3087 *result = &elt->u.list;
3088 return SVN_NO_ERROR;
3091 /* Verify the tuple ITEMS contains at least MIN and at most MAX elements.
3093 static svn_error_t *
3094 svn_ra_svn__read_check_array_size(const svn_ra_svn__list_t *items,
3098 CHECK_PROTOCOL_COND(items->nelts >= min && items->nelts <= max);
3099 return SVN_NO_ERROR;
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)
3112 const svn_ra_svn__list_t *sub_items;
3114 /* initialize optional values */
3116 *copy_rev = SVN_INVALID_REVNUM;
3118 *text_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER;
3119 *prop_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER;
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));
3126 /* first sub-structure (mandatory) */
3127 SVN_ERR(svn_ra_svn__read_list(items, 2, &sub_items));
3128 if (sub_items->nelts)
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));
3135 /* second sub-structure (optional) */
3136 if (items->nelts >= 4)
3138 SVN_ERR(svn_ra_svn__read_list(items, 3, &sub_items));
3139 switch (MIN(3, sub_items->nelts))
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));
3148 return SVN_NO_ERROR;