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