marshal.c revision 299742
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. */ 76static apr_interval_time_t 77get_timeout(svn_ra_svn_conn_t *conn) 78{ 79 return conn->block_handler ? 0 : -1; 80} 81 82/* --- CONNECTION INITIALIZATION --- */ 83 84svn_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 135svn_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 154apr_pool_t * 155svn_ra_svn__get_pool(svn_ra_svn_conn_t *conn) 156{ 157 return conn->pool; 158} 159 160svn_error_t * 161svn_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 168svn_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 174int 175svn_ra_svn_compression_level(svn_ra_svn_conn_t *conn) 176{ 177 return conn->compression_level; 178} 179 180apr_size_t 181svn_ra_svn_zero_copy_limit(svn_ra_svn_conn_t *conn) 182{ 183 return conn->zero_copy_limit; 184} 185 186const char *svn_ra_svn_conn_remote_host(svn_ra_svn_conn_t *conn) 187{ 188 return conn->remote_ip; 189} 190 191void 192svn_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 201svn_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. */ 210static 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. */ 257static 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 267static 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 298static APR_INLINE svn_error_t * 299writebuf_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. */ 319static 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. */ 331static 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" */ 357static 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. */ 382static 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 */ 401static SVN__FORCE_INLINE svn_error_t * 402readbuf_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 410static 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. */ 421static 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 450static 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 490static 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 507svn_error_t * 508svn_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 515static svn_error_t * 516svn_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 535svn_error_t * 536svn_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 544svn_error_t * 545svn_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 553svn_error_t * 554svn_ra_svn__write_word(svn_ra_svn_conn_t *conn, 555 apr_pool_t *pool, 556 const char *word) 557{ 558 SVN_ERR(writebuf_write(conn, pool, word, strlen(word))); 559 SVN_ERR(writebuf_writechar(conn, pool, ' ')); 560 561 return SVN_NO_ERROR; 562} 563 564svn_error_t * 565svn_ra_svn__write_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 577svn_error_t * 578svn_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 608svn_error_t * 609svn_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 623svn_error_t * 624svn_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 638svn_error_t * 639svn_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 650static svn_error_t * 651vwrite_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 658static svn_error_t * 659vwrite_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 665static svn_error_t * 666vwrite_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 673static svn_error_t * 674vwrite_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 680static svn_error_t * 681vwrite_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 688static svn_error_t * 689vwrite_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 695static svn_error_t * 696vwrite_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 703static svn_error_t * 704vwrite_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 712static svn_error_t * 713vwrite_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 718static svn_error_t * 719vwrite_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 724static svn_error_t * 725write_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 733static svn_error_t * 734write_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 741static svn_error_t * 742write_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 750static svn_error_t * 751write_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 758static svn_error_t * 759write_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 765static svn_error_t * 766write_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 772static svn_error_t * 773write_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 781static svn_error_t * 782write_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 791static svn_error_t * 792write_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 799static svn_error_t * 800write_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 808static svn_error_t * 809write_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 828static svn_error_t * 829write_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 846static svn_error_t * 847write_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 862static svn_error_t * 863write_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 877static 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 922svn_error_t * 923svn_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. */ 941static 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 apr_size_t buflen; 948 949 /* We can't store strings longer than the maximum size of apr_size_t, 950 * so check for wrapping */ 951 if (len64 > APR_SIZE_MAX) 952 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 953 _("String length larger than maximum")); 954 955 buflen = conn->read_end - conn->read_ptr; 956 /* Shorter strings can be copied directly from the read buffer. */ 957 if (len <= buflen) 958 { 959 item->kind = SVN_RA_SVN_STRING; 960 item->u.string = svn_string_ncreate(conn->read_ptr, len, pool); 961 conn->read_ptr += len; 962 } 963 else 964 { 965 /* Read the string in chunks. The chunk size is large enough to avoid 966 * re-allocation in typical cases, and small enough to ensure we do 967 * not pre-allocate an unreasonable amount of memory if (perhaps due 968 * to network data corruption or a DOS attack), we receive a bogus 969 * claim that a very long string is going to follow. In that case, we 970 * start small and wait for all that data to actually show up. This 971 * does not fully prevent DOS attacks but makes them harder (you have 972 * to actually send gigabytes of data). */ 973 svn_stringbuf_t *stringbuf = svn_stringbuf_create_empty(pool); 974 975 /* Read string data directly into the string structure. 976 * Do it iteratively. */ 977 do 978 { 979 /* Determine length of chunk to read and re-alloc the buffer. */ 980 readbuf_len 981 = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD 982 ? len 983 : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD; 984 985 svn_stringbuf_ensure(stringbuf, stringbuf->len + readbuf_len); 986 dest = stringbuf->data + stringbuf->len; 987 988 /* read data & update length info */ 989 SVN_ERR(readbuf_read(conn, pool, dest, readbuf_len)); 990 991 stringbuf->len += readbuf_len; 992 len -= readbuf_len; 993 } 994 while (len); 995 996 /* zero-terminate the string */ 997 stringbuf->data[stringbuf->len] = '\0'; 998 999 /* Return the string properly wrapped into an RA_SVN item. */ 1000 item->kind = SVN_RA_SVN_STRING; 1001 item->u.string = svn_stringbuf__morph_into_string(stringbuf); 1002 } 1003 1004 return SVN_NO_ERROR; 1005} 1006 1007/* Given the first non-whitespace character FIRST_CHAR, read an item 1008 * into the already allocated structure ITEM. LEVEL should be set 1009 * to 0 for the first call and is used to enforce a recursion limit 1010 * on the parser. */ 1011static svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1012 svn_ra_svn_item_t *item, char first_char, 1013 int level) 1014{ 1015 char c = first_char; 1016 apr_uint64_t val; 1017 svn_ra_svn_item_t *listitem; 1018 1019 if (++level >= ITEM_NESTING_LIMIT) 1020 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1021 _("Items are nested too deeply")); 1022 1023 1024 /* Determine the item type and read it in. Make sure that c is the 1025 * first character at the end of the item so we can test to make 1026 * sure it's whitespace. */ 1027 if (svn_ctype_isdigit(c)) 1028 { 1029 /* It's a number or a string. Read the number part, either way. */ 1030 val = c - '0'; 1031 while (1) 1032 { 1033 apr_uint64_t prev_val = val; 1034 SVN_ERR(readbuf_getchar(conn, pool, &c)); 1035 if (!svn_ctype_isdigit(c)) 1036 break; 1037 val = val * 10 + (c - '0'); 1038 /* val wrapped past maximum value? */ 1039 if ((prev_val >= (APR_UINT64_MAX / 10)) 1040 && (val < APR_UINT64_MAX - 10)) 1041 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1042 _("Number is larger than maximum")); 1043 } 1044 if (c == ':') 1045 { 1046 /* It's a string. */ 1047 SVN_ERR(read_string(conn, pool, item, val)); 1048 SVN_ERR(readbuf_getchar(conn, pool, &c)); 1049 } 1050 else 1051 { 1052 /* It's a number. */ 1053 item->kind = SVN_RA_SVN_NUMBER; 1054 item->u.number = val; 1055 } 1056 } 1057 else if (svn_ctype_isalpha(c)) 1058 { 1059 /* It's a word. Read it into a buffer of limited size. */ 1060 char *buffer = apr_palloc(pool, MAX_WORD_LENGTH + 1); 1061 char *end = buffer + MAX_WORD_LENGTH; 1062 char *p = buffer + 1; 1063 1064 buffer[0] = c; 1065 while (1) 1066 { 1067 SVN_ERR(readbuf_getchar(conn, pool, p)); 1068 if (!svn_ctype_isalnum(*p) && *p != '-') 1069 break; 1070 1071 if (++p == end) 1072 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1073 _("Word is too long")); 1074 } 1075 1076 c = *p; 1077 *p = '\0'; 1078 1079 item->kind = SVN_RA_SVN_WORD; 1080 item->u.word = buffer; 1081 } 1082 else if (c == '(') 1083 { 1084 /* Read in the list items. */ 1085 item->kind = SVN_RA_SVN_LIST; 1086 item->u.list = apr_array_make(pool, 4, sizeof(svn_ra_svn_item_t)); 1087 while (1) 1088 { 1089 SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c)); 1090 if (c == ')') 1091 break; 1092 listitem = apr_array_push(item->u.list); 1093 SVN_ERR(read_item(conn, pool, listitem, c, level)); 1094 } 1095 SVN_ERR(readbuf_getchar(conn, pool, &c)); 1096 } 1097 1098 if (!svn_iswhitespace(c)) 1099 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1100 _("Malformed network data")); 1101 return SVN_NO_ERROR; 1102} 1103 1104/* Given the first non-whitespace character FIRST_CHAR, read the first 1105 * command (word) encountered in CONN into *ITEM. If ITEM is NULL, skip 1106 * to the end of the current list. Use POOL for allocations. */ 1107static svn_error_t * 1108read_command_only(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1109 const char **item, char first_char) 1110{ 1111 char c = first_char; 1112 1113 /* Determine the item type and read it in. Make sure that c is the 1114 * first character at the end of the item so we can test to make 1115 * sure it's whitespace. */ 1116 if (svn_ctype_isdigit(c)) 1117 { 1118 /* It's a number or a string. Read the number part, either way. */ 1119 apr_uint64_t val, prev_val=0; 1120 val = c - '0'; 1121 while (1) 1122 { 1123 prev_val = val; 1124 SVN_ERR(readbuf_getchar(conn, pool, &c)); 1125 if (!svn_ctype_isdigit(c)) 1126 break; 1127 val = val * 10 + (c - '0'); 1128 if (prev_val >= (APR_UINT64_MAX / 10)) /* > maximum value? */ 1129 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1130 _("Number is larger than maximum")); 1131 } 1132 if (c == ':') 1133 { 1134 /* It's a string. */ 1135 SVN_ERR(readbuf_skip(conn, val)); 1136 SVN_ERR(readbuf_getchar(conn, pool, &c)); 1137 } 1138 } 1139 else if (svn_ctype_isalpha(c)) 1140 { 1141 /* It's a word. */ 1142 if (item) 1143 { 1144 /* This is the word we want to read */ 1145 1146 char *buf = apr_palloc(pool, 32); 1147 apr_size_t len = 1; 1148 buf[0] = c; 1149 1150 while (1) 1151 { 1152 SVN_ERR(readbuf_getchar(conn, pool, &c)); 1153 if (!svn_ctype_isalnum(c) && c != '-') 1154 break; 1155 buf[len] = c; 1156 if (++len == 32) 1157 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1158 _("Word too long")); 1159 } 1160 buf[len] = 0; 1161 *item = buf; 1162 } 1163 else 1164 { 1165 /* we don't need the actual word, just skip it */ 1166 do 1167 { 1168 SVN_ERR(readbuf_getchar(conn, pool, &c)); 1169 } 1170 while (svn_ctype_isalnum(c) || c == '-'); 1171 } 1172 } 1173 else if (c == '(') 1174 { 1175 /* Read in the list items. */ 1176 while (1) 1177 { 1178 SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c)); 1179 if (c == ')') 1180 break; 1181 1182 if (item && *item == NULL) 1183 SVN_ERR(read_command_only(conn, pool, item, c)); 1184 else 1185 SVN_ERR(read_command_only(conn, pool, NULL, c)); 1186 } 1187 SVN_ERR(readbuf_getchar(conn, pool, &c)); 1188 } 1189 1190 return SVN_NO_ERROR; 1191} 1192 1193svn_error_t * 1194svn_ra_svn__read_item(svn_ra_svn_conn_t *conn, 1195 apr_pool_t *pool, 1196 svn_ra_svn_item_t **item) 1197{ 1198 char c; 1199 1200 /* Allocate space, read the first character, and then do the rest of 1201 * the work. This makes sense because of the way lists are read. */ 1202 *item = apr_palloc(pool, sizeof(**item)); 1203 SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c)); 1204 return read_item(conn, pool, *item, c, 0); 1205} 1206 1207/* Drain existing whitespace from the receive buffer of CONN until either 1208 there is no data in the underlying receive socket anymore or we found 1209 a non-whitespace char. Set *HAS_ITEM to TRUE in the latter case. 1210 */ 1211static svn_error_t * 1212svn_ra_svn__has_item(svn_boolean_t *has_item, 1213 svn_ra_svn_conn_t *conn, 1214 apr_pool_t *pool) 1215{ 1216 do 1217 { 1218 if (conn->read_ptr == conn->read_end) 1219 { 1220 svn_boolean_t available; 1221 if (conn->write_pos) 1222 SVN_ERR(writebuf_flush(conn, pool)); 1223 1224 SVN_ERR(svn_ra_svn__data_available(conn, &available)); 1225 if (!available) 1226 break; 1227 1228 SVN_ERR(readbuf_fill(conn, pool)); 1229 } 1230 } 1231 while (svn_iswhitespace(*conn->read_ptr) && ++conn->read_ptr); 1232 1233 *has_item = conn->read_ptr != conn->read_end; 1234 return SVN_NO_ERROR; 1235} 1236 1237svn_error_t * 1238svn_ra_svn__skip_leading_garbage(svn_ra_svn_conn_t *conn, 1239 apr_pool_t *pool) 1240{ 1241 return readbuf_skip_leading_garbage(conn, pool); 1242} 1243 1244/* --- READING AND PARSING TUPLES --- */ 1245 1246/* Parse a tuple of svn_ra_svn_item_t *'s. Advance *FMT to the end of the 1247 * tuple specification and advance AP by the corresponding arguments. */ 1248static svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *pool, 1249 const char **fmt, va_list *ap) 1250{ 1251 int count, nesting_level; 1252 svn_ra_svn_item_t *elt; 1253 1254 for (count = 0; **fmt && count < items->nelts; (*fmt)++, count++) 1255 { 1256 /* '?' just means the tuple may stop; skip past it. */ 1257 if (**fmt == '?') 1258 (*fmt)++; 1259 elt = &APR_ARRAY_IDX(items, count, svn_ra_svn_item_t); 1260 if (**fmt == '(' && elt->kind == SVN_RA_SVN_LIST) 1261 { 1262 (*fmt)++; 1263 SVN_ERR(vparse_tuple(elt->u.list, pool, fmt, ap)); 1264 } 1265 else if (**fmt == 'c' && elt->kind == SVN_RA_SVN_STRING) 1266 *va_arg(*ap, const char **) = elt->u.string->data; 1267 else if (**fmt == 's' && elt->kind == SVN_RA_SVN_STRING) 1268 *va_arg(*ap, svn_string_t **) = elt->u.string; 1269 else if (**fmt == 'w' && elt->kind == SVN_RA_SVN_WORD) 1270 *va_arg(*ap, const char **) = elt->u.word; 1271 else if (**fmt == 'b' && elt->kind == SVN_RA_SVN_WORD) 1272 { 1273 if (strcmp(elt->u.word, "true") == 0) 1274 *va_arg(*ap, svn_boolean_t *) = TRUE; 1275 else if (strcmp(elt->u.word, "false") == 0) 1276 *va_arg(*ap, svn_boolean_t *) = FALSE; 1277 else 1278 break; 1279 } 1280 else if (**fmt == 'n' && elt->kind == SVN_RA_SVN_NUMBER) 1281 *va_arg(*ap, apr_uint64_t *) = elt->u.number; 1282 else if (**fmt == 'r' && elt->kind == SVN_RA_SVN_NUMBER) 1283 *va_arg(*ap, svn_revnum_t *) = (svn_revnum_t) elt->u.number; 1284 else if (**fmt == 'B' && elt->kind == SVN_RA_SVN_WORD) 1285 { 1286 if (strcmp(elt->u.word, "true") == 0) 1287 *va_arg(*ap, apr_uint64_t *) = TRUE; 1288 else if (strcmp(elt->u.word, "false") == 0) 1289 *va_arg(*ap, apr_uint64_t *) = FALSE; 1290 else 1291 break; 1292 } 1293 else if (**fmt == '3' && elt->kind == SVN_RA_SVN_WORD) 1294 { 1295 if (strcmp(elt->u.word, "true") == 0) 1296 *va_arg(*ap, svn_tristate_t *) = svn_tristate_true; 1297 else if (strcmp(elt->u.word, "false") == 0) 1298 *va_arg(*ap, svn_tristate_t *) = svn_tristate_false; 1299 else 1300 break; 1301 } 1302 else if (**fmt == 'l' && elt->kind == SVN_RA_SVN_LIST) 1303 *va_arg(*ap, apr_array_header_t **) = elt->u.list; 1304 else if (**fmt == ')') 1305 return SVN_NO_ERROR; 1306 else 1307 break; 1308 } 1309 if (**fmt == '?') 1310 { 1311 nesting_level = 0; 1312 for (; **fmt; (*fmt)++) 1313 { 1314 switch (**fmt) 1315 { 1316 case '?': 1317 break; 1318 case 'r': 1319 *va_arg(*ap, svn_revnum_t *) = SVN_INVALID_REVNUM; 1320 break; 1321 case 's': 1322 *va_arg(*ap, svn_string_t **) = NULL; 1323 break; 1324 case 'c': 1325 case 'w': 1326 *va_arg(*ap, const char **) = NULL; 1327 break; 1328 case 'l': 1329 *va_arg(*ap, apr_array_header_t **) = NULL; 1330 break; 1331 case 'B': 1332 case 'n': 1333 *va_arg(*ap, apr_uint64_t *) = SVN_RA_SVN_UNSPECIFIED_NUMBER; 1334 break; 1335 case '3': 1336 *va_arg(*ap, svn_tristate_t *) = svn_tristate_unknown; 1337 break; 1338 case '(': 1339 nesting_level++; 1340 break; 1341 case ')': 1342 if (--nesting_level < 0) 1343 return SVN_NO_ERROR; 1344 break; 1345 default: 1346 SVN_ERR_MALFUNCTION(); 1347 } 1348 } 1349 } 1350 if (**fmt && **fmt != ')') 1351 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1352 _("Malformed network data")); 1353 return SVN_NO_ERROR; 1354} 1355 1356svn_error_t * 1357svn_ra_svn__parse_tuple(const apr_array_header_t *list, 1358 apr_pool_t *pool, 1359 const char *fmt, ...) 1360{ 1361 svn_error_t *err; 1362 va_list ap; 1363 1364 va_start(ap, fmt); 1365 err = vparse_tuple(list, pool, &fmt, &ap); 1366 va_end(ap); 1367 return err; 1368} 1369 1370svn_error_t * 1371svn_ra_svn__read_tuple(svn_ra_svn_conn_t *conn, 1372 apr_pool_t *pool, 1373 const char *fmt, ...) 1374{ 1375 va_list ap; 1376 svn_ra_svn_item_t *item; 1377 svn_error_t *err; 1378 1379 SVN_ERR(svn_ra_svn__read_item(conn, pool, &item)); 1380 if (item->kind != SVN_RA_SVN_LIST) 1381 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1382 _("Malformed network data")); 1383 va_start(ap, fmt); 1384 err = vparse_tuple(item->u.list, pool, &fmt, &ap); 1385 va_end(ap); 1386 return err; 1387} 1388 1389svn_error_t * 1390svn_ra_svn__read_command_only(svn_ra_svn_conn_t *conn, 1391 apr_pool_t *pool, 1392 const char **command) 1393{ 1394 char c; 1395 SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c)); 1396 1397 *command = NULL; 1398 return read_command_only(conn, pool, command, c); 1399} 1400 1401 1402svn_error_t * 1403svn_ra_svn__parse_proplist(const apr_array_header_t *list, 1404 apr_pool_t *pool, 1405 apr_hash_t **props) 1406{ 1407 svn_string_t *name; 1408 svn_string_t *value; 1409 svn_ra_svn_item_t *elt; 1410 int i; 1411 1412 *props = svn_hash__make(pool); 1413 for (i = 0; i < list->nelts; i++) 1414 { 1415 elt = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t); 1416 if (elt->kind != SVN_RA_SVN_LIST) 1417 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1418 _("Proplist element not a list")); 1419 SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, pool, "ss", 1420 &name, &value)); 1421 apr_hash_set(*props, name->data, name->len, value); 1422 } 1423 1424 return SVN_NO_ERROR; 1425} 1426 1427 1428/* --- READING AND WRITING COMMANDS AND RESPONSES --- */ 1429 1430svn_error_t *svn_ra_svn__locate_real_error_child(svn_error_t *err) 1431{ 1432 svn_error_t *this_link; 1433 1434 SVN_ERR_ASSERT(err); 1435 1436 for (this_link = err; 1437 this_link && (this_link->apr_err == SVN_ERR_RA_SVN_CMD_ERR); 1438 this_link = this_link->child) 1439 ; 1440 1441 SVN_ERR_ASSERT(this_link); 1442 return this_link; 1443} 1444 1445svn_error_t *svn_ra_svn__handle_failure_status(const apr_array_header_t *params, 1446 apr_pool_t *pool) 1447{ 1448 const char *message, *file; 1449 svn_error_t *err = NULL; 1450 svn_ra_svn_item_t *elt; 1451 int i; 1452 apr_uint64_t apr_err, line; 1453 apr_pool_t *subpool = svn_pool_create(pool); 1454 1455 if (params->nelts == 0) 1456 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1457 _("Empty error list")); 1458 1459 /* Rebuild the error list from the end, to avoid reversing the order. */ 1460 for (i = params->nelts - 1; i >= 0; i--) 1461 { 1462 svn_pool_clear(subpool); 1463 elt = &APR_ARRAY_IDX(params, i, svn_ra_svn_item_t); 1464 if (elt->kind != SVN_RA_SVN_LIST) 1465 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1466 _("Malformed error list")); 1467 SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, subpool, "nccn", 1468 &apr_err, &message, &file, &line)); 1469 /* The message field should have been optional, but we can't 1470 easily change that, so "" means a nonexistent message. */ 1471 if (!*message) 1472 message = NULL; 1473 1474 /* Skip over links in the error chain that were intended only to 1475 exist on the server (to wrap real errors intended for the 1476 client) but accidentally got included in the server's actual 1477 response. */ 1478 if ((apr_status_t)apr_err != SVN_ERR_RA_SVN_CMD_ERR) 1479 { 1480 err = svn_error_create((apr_status_t)apr_err, err, message); 1481 err->file = apr_pstrdup(err->pool, file); 1482 err->line = (long)line; 1483 } 1484 } 1485 1486 svn_pool_destroy(subpool); 1487 1488 /* If we get here, then we failed to find a real error in the error 1489 chain that the server proported to be sending us. That's bad. */ 1490 if (! err) 1491 err = svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1492 _("Malformed error list")); 1493 1494 return err; 1495} 1496 1497svn_error_t * 1498svn_ra_svn__read_cmd_response(svn_ra_svn_conn_t *conn, 1499 apr_pool_t *pool, 1500 const char *fmt, ...) 1501{ 1502 va_list ap; 1503 const char *status; 1504 apr_array_header_t *params; 1505 svn_error_t *err; 1506 1507 SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "wl", &status, ¶ms)); 1508 if (strcmp(status, "success") == 0) 1509 { 1510 va_start(ap, fmt); 1511 err = vparse_tuple(params, pool, &fmt, &ap); 1512 va_end(ap); 1513 return err; 1514 } 1515 else if (strcmp(status, "failure") == 0) 1516 { 1517 return svn_error_trace(svn_ra_svn__handle_failure_status(params, pool)); 1518 } 1519 1520 return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1521 _("Unknown status '%s' in command response"), 1522 status); 1523} 1524 1525svn_error_t * 1526svn_ra_svn__has_command(svn_boolean_t *has_command, 1527 svn_boolean_t *terminated, 1528 svn_ra_svn_conn_t *conn, 1529 apr_pool_t *pool) 1530{ 1531 svn_error_t *err = svn_ra_svn__has_item(has_command, conn, pool); 1532 if (err && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED) 1533 { 1534 *terminated = TRUE; 1535 svn_error_clear(err); 1536 return SVN_NO_ERROR; 1537 } 1538 1539 *terminated = FALSE; 1540 return svn_error_trace(err); 1541} 1542 1543svn_error_t * 1544svn_ra_svn__handle_command(svn_boolean_t *terminate, 1545 apr_hash_t *cmd_hash, 1546 void *baton, 1547 svn_ra_svn_conn_t *conn, 1548 svn_boolean_t error_on_disconnect, 1549 apr_pool_t *pool) 1550{ 1551 const char *cmdname; 1552 svn_error_t *err, *write_err; 1553 apr_array_header_t *params; 1554 const svn_ra_svn_cmd_entry_t *command; 1555 1556 *terminate = FALSE; 1557 err = svn_ra_svn__read_tuple(conn, pool, "wl", &cmdname, ¶ms); 1558 if (err) 1559 { 1560 if (!error_on_disconnect 1561 && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED) 1562 { 1563 svn_error_clear(err); 1564 *terminate = TRUE; 1565 return SVN_NO_ERROR; 1566 } 1567 return err; 1568 } 1569 1570 command = svn_hash_gets(cmd_hash, cmdname); 1571 if (command) 1572 { 1573 err = (*command->handler)(conn, pool, params, baton); 1574 *terminate = command->terminate; 1575 } 1576 else 1577 { 1578 err = svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL, 1579 _("Unknown editor command '%s'"), cmdname); 1580 err = svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, err, NULL); 1581 } 1582 1583 if (err && err->apr_err == SVN_ERR_RA_SVN_CMD_ERR) 1584 { 1585 write_err = svn_ra_svn__write_cmd_failure( 1586 conn, pool, 1587 svn_ra_svn__locate_real_error_child(err)); 1588 svn_error_clear(err); 1589 return write_err ? write_err : SVN_NO_ERROR; 1590 } 1591 1592 return err; 1593} 1594 1595svn_error_t * 1596svn_ra_svn__handle_commands2(svn_ra_svn_conn_t *conn, 1597 apr_pool_t *pool, 1598 const svn_ra_svn_cmd_entry_t *commands, 1599 void *baton, 1600 svn_boolean_t error_on_disconnect) 1601{ 1602 apr_pool_t *subpool = svn_pool_create(pool); 1603 apr_pool_t *iterpool = svn_pool_create(subpool); 1604 const svn_ra_svn_cmd_entry_t *command; 1605 apr_hash_t *cmd_hash = apr_hash_make(subpool); 1606 1607 for (command = commands; command->cmdname; command++) 1608 svn_hash_sets(cmd_hash, command->cmdname, command); 1609 1610 while (1) 1611 { 1612 svn_boolean_t terminate; 1613 svn_error_t *err; 1614 svn_pool_clear(iterpool); 1615 1616 err = svn_ra_svn__handle_command(&terminate, cmd_hash, baton, conn, 1617 error_on_disconnect, iterpool); 1618 if (err) 1619 { 1620 svn_pool_destroy(subpool); 1621 return svn_error_trace(err); 1622 } 1623 if (terminate) 1624 break; 1625 } 1626 svn_pool_destroy(iterpool); 1627 svn_pool_destroy(subpool); 1628 return SVN_NO_ERROR; 1629} 1630 1631svn_error_t * 1632svn_ra_svn__write_cmd_target_rev(svn_ra_svn_conn_t *conn, 1633 apr_pool_t *pool, 1634 svn_revnum_t rev) 1635{ 1636 SVN_ERR(writebuf_write_literal(conn, pool, "( target-rev ( ")); 1637 SVN_ERR(write_tuple_revision(conn, pool, rev)); 1638 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1639 1640 return SVN_NO_ERROR; 1641} 1642 1643svn_error_t * 1644svn_ra_svn__write_cmd_open_root(svn_ra_svn_conn_t *conn, 1645 apr_pool_t *pool, 1646 svn_revnum_t rev, 1647 const char *token) 1648{ 1649 SVN_ERR(writebuf_write_literal(conn, pool, "( open-root ( ")); 1650 SVN_ERR(write_tuple_start_list(conn, pool)); 1651 SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); 1652 SVN_ERR(write_tuple_end_list(conn, pool)); 1653 SVN_ERR(write_tuple_cstring(conn, pool, token)); 1654 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1655 1656 return SVN_NO_ERROR; 1657} 1658 1659svn_error_t * 1660svn_ra_svn__write_cmd_delete_entry(svn_ra_svn_conn_t *conn, 1661 apr_pool_t *pool, 1662 const char *path, 1663 svn_revnum_t rev, 1664 const char *token) 1665{ 1666 SVN_ERR(writebuf_write_literal(conn, pool, "( delete-entry ( ")); 1667 SVN_ERR(write_tuple_cstring(conn, pool, path)); 1668 SVN_ERR(write_tuple_start_list(conn, pool)); 1669 SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); 1670 SVN_ERR(write_tuple_end_list(conn, pool)); 1671 SVN_ERR(write_tuple_cstring(conn, pool, token)); 1672 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1673 1674 return SVN_NO_ERROR; 1675} 1676 1677svn_error_t * 1678svn_ra_svn__write_cmd_add_dir(svn_ra_svn_conn_t *conn, 1679 apr_pool_t *pool, 1680 const char *path, 1681 const char *parent_token, 1682 const char *token, 1683 const char *copy_path, 1684 svn_revnum_t copy_rev) 1685{ 1686 SVN_ERR(writebuf_write_literal(conn, pool, "( add-dir ( ")); 1687 SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token, 1688 copy_path, copy_rev)); 1689 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1690 1691 return SVN_NO_ERROR; 1692} 1693 1694svn_error_t * 1695svn_ra_svn__write_cmd_open_dir(svn_ra_svn_conn_t *conn, 1696 apr_pool_t *pool, 1697 const char *path, 1698 const char *parent_token, 1699 const char *token, 1700 svn_revnum_t rev) 1701{ 1702 SVN_ERR(writebuf_write_literal(conn, pool, "( open-dir ( ")); 1703 SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev)); 1704 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1705 1706 return SVN_NO_ERROR; 1707} 1708 1709svn_error_t * 1710svn_ra_svn__write_cmd_change_dir_prop(svn_ra_svn_conn_t *conn, 1711 apr_pool_t *pool, 1712 const char *token, 1713 const char *name, 1714 const svn_string_t *value) 1715{ 1716 SVN_ERR(writebuf_write_literal(conn, pool, "( change-dir-prop ( ")); 1717 SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value)); 1718 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1719 1720 return SVN_NO_ERROR; 1721} 1722 1723svn_error_t * 1724svn_ra_svn__write_cmd_close_dir(svn_ra_svn_conn_t *conn, 1725 apr_pool_t *pool, 1726 const char *token) 1727{ 1728 SVN_ERR(writebuf_write_literal(conn, pool, "( close-dir ( ")); 1729 SVN_ERR(write_tuple_cstring(conn, pool, token)); 1730 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1731 1732 return SVN_NO_ERROR; 1733} 1734 1735svn_error_t * 1736svn_ra_svn__write_cmd_absent_dir(svn_ra_svn_conn_t *conn, 1737 apr_pool_t *pool, 1738 const char *path, 1739 const char *parent_token) 1740{ 1741 SVN_ERR(writebuf_write_literal(conn, pool, "( absent-dir ( ")); 1742 SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token)); 1743 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1744 1745 return SVN_NO_ERROR; 1746} 1747 1748svn_error_t * 1749svn_ra_svn__write_cmd_add_file(svn_ra_svn_conn_t *conn, 1750 apr_pool_t *pool, 1751 const char *path, 1752 const char *parent_token, 1753 const char *token, 1754 const char *copy_path, 1755 svn_revnum_t copy_rev) 1756{ 1757 SVN_ERR(writebuf_write_literal(conn, pool, "( add-file ( ")); 1758 SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token, 1759 copy_path, copy_rev)); 1760 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1761 1762 return SVN_NO_ERROR; 1763} 1764 1765svn_error_t * 1766svn_ra_svn__write_cmd_open_file(svn_ra_svn_conn_t *conn, 1767 apr_pool_t *pool, 1768 const char *path, 1769 const char *parent_token, 1770 const char *token, 1771 svn_revnum_t rev) 1772{ 1773 SVN_ERR(writebuf_write_literal(conn, pool, "( open-file ( ")); 1774 SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev)); 1775 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1776 1777 return SVN_NO_ERROR; 1778} 1779 1780svn_error_t * 1781svn_ra_svn__write_cmd_change_file_prop(svn_ra_svn_conn_t *conn, 1782 apr_pool_t *pool, 1783 const char *token, 1784 const char *name, 1785 const svn_string_t *value) 1786{ 1787 SVN_ERR(writebuf_write_literal(conn, pool, "( change-file-prop ( ")); 1788 SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value)); 1789 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1790 1791 return SVN_NO_ERROR; 1792} 1793 1794svn_error_t * 1795svn_ra_svn__write_cmd_close_file(svn_ra_svn_conn_t *conn, 1796 apr_pool_t *pool, 1797 const char *token, 1798 const char *text_checksum) 1799{ 1800 SVN_ERR(writebuf_write_literal(conn, pool, "( close-file ( ")); 1801 SVN_ERR(write_tuple_cstring(conn, pool, token)); 1802 SVN_ERR(write_tuple_start_list(conn, pool)); 1803 SVN_ERR(write_tuple_cstring_opt(conn, pool, text_checksum)); 1804 SVN_ERR(write_tuple_end_list(conn, pool)); 1805 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1806 1807 return SVN_NO_ERROR; 1808} 1809 1810svn_error_t * 1811svn_ra_svn__write_cmd_absent_file(svn_ra_svn_conn_t *conn, 1812 apr_pool_t *pool, 1813 const char *path, 1814 const char *parent_token) 1815{ 1816 SVN_ERR(writebuf_write_literal(conn, pool, "( absent-file ( ")); 1817 SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token)); 1818 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1819 1820 return SVN_NO_ERROR; 1821} 1822 1823svn_error_t * 1824svn_ra_svn__write_cmd_textdelta_chunk(svn_ra_svn_conn_t *conn, 1825 apr_pool_t *pool, 1826 const char *token, 1827 const svn_string_t *chunk) 1828{ 1829 SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-chunk ( ")); 1830 SVN_ERR(write_tuple_cstring(conn, pool, token)); 1831 SVN_ERR(write_tuple_string(conn, pool, chunk)); 1832 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1833 1834 return SVN_NO_ERROR; 1835} 1836 1837svn_error_t * 1838svn_ra_svn__write_cmd_textdelta_end(svn_ra_svn_conn_t *conn, 1839 apr_pool_t *pool, 1840 const char *token) 1841{ 1842 SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-end ( ")); 1843 SVN_ERR(write_tuple_cstring(conn, pool, token)); 1844 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1845 1846 return SVN_NO_ERROR; 1847} 1848 1849svn_error_t * 1850svn_ra_svn__write_cmd_apply_textdelta(svn_ra_svn_conn_t *conn, 1851 apr_pool_t *pool, 1852 const char *token, 1853 const char *base_checksum) 1854{ 1855 SVN_ERR(writebuf_write_literal(conn, pool, "( apply-textdelta ( ")); 1856 SVN_ERR(write_tuple_cstring(conn, pool, token)); 1857 SVN_ERR(write_tuple_start_list(conn, pool)); 1858 SVN_ERR(write_tuple_cstring_opt(conn, pool, base_checksum)); 1859 SVN_ERR(write_tuple_end_list(conn, pool)); 1860 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1861 1862 return SVN_NO_ERROR; 1863} 1864 1865svn_error_t * 1866svn_ra_svn__write_cmd_close_edit(svn_ra_svn_conn_t *conn, 1867 apr_pool_t *pool) 1868{ 1869 return writebuf_write_literal(conn, pool, "( close-edit ( ) ) "); 1870} 1871 1872svn_error_t * 1873svn_ra_svn__write_cmd_abort_edit(svn_ra_svn_conn_t *conn, 1874 apr_pool_t *pool) 1875{ 1876 return writebuf_write_literal(conn, pool, "( abort-edit ( ) ) "); 1877} 1878 1879svn_error_t * 1880svn_ra_svn__write_cmd_set_path(svn_ra_svn_conn_t *conn, 1881 apr_pool_t *pool, 1882 const char *path, 1883 svn_revnum_t rev, 1884 svn_boolean_t start_empty, 1885 const char *lock_token, 1886 svn_depth_t depth) 1887{ 1888 SVN_ERR(writebuf_write_literal(conn, pool, "( set-path ( ")); 1889 SVN_ERR(write_tuple_cstring(conn, pool, path)); 1890 SVN_ERR(write_tuple_revision(conn, pool, rev)); 1891 SVN_ERR(write_tuple_boolean(conn, pool, start_empty)); 1892 SVN_ERR(write_tuple_start_list(conn, pool)); 1893 SVN_ERR(write_tuple_cstring_opt(conn, pool, lock_token)); 1894 SVN_ERR(write_tuple_end_list(conn, pool)); 1895 SVN_ERR(write_tuple_depth(conn, pool, depth)); 1896 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1897 1898 return SVN_NO_ERROR; 1899} 1900 1901svn_error_t * 1902svn_ra_svn__write_cmd_delete_path(svn_ra_svn_conn_t *conn, 1903 apr_pool_t *pool, 1904 const char *path) 1905{ 1906 SVN_ERR(writebuf_write_literal(conn, pool, "( delete-path ( ")); 1907 SVN_ERR(write_tuple_cstring(conn, pool, path)); 1908 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1909 1910 return SVN_NO_ERROR; 1911} 1912 1913svn_error_t * 1914svn_ra_svn__write_cmd_link_path(svn_ra_svn_conn_t *conn, 1915 apr_pool_t *pool, 1916 const char *path, 1917 const char *url, 1918 svn_revnum_t rev, 1919 svn_boolean_t start_empty, 1920 const char *lock_token, 1921 svn_depth_t depth) 1922{ 1923 SVN_ERR(writebuf_write_literal(conn, pool, "( link-path ( ")); 1924 SVN_ERR(write_tuple_cstring(conn, pool, path)); 1925 SVN_ERR(write_tuple_cstring(conn, pool, url)); 1926 SVN_ERR(write_tuple_revision(conn, pool, rev)); 1927 SVN_ERR(write_tuple_boolean(conn, pool, start_empty)); 1928 SVN_ERR(write_tuple_start_list(conn, pool)); 1929 SVN_ERR(write_tuple_cstring_opt(conn, pool,lock_token)); 1930 SVN_ERR(write_tuple_end_list(conn, pool)); 1931 SVN_ERR(write_tuple_depth(conn, pool, depth)); 1932 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1933 1934 return SVN_NO_ERROR; 1935} 1936 1937svn_error_t * 1938svn_ra_svn__write_cmd_finish_report(svn_ra_svn_conn_t *conn, 1939 apr_pool_t *pool) 1940{ 1941 return writebuf_write_literal(conn, pool, "( finish-report ( ) ) "); 1942} 1943 1944svn_error_t * 1945svn_ra_svn__write_cmd_abort_report(svn_ra_svn_conn_t *conn, 1946 apr_pool_t *pool) 1947{ 1948 return writebuf_write_literal(conn, pool, "( abort-report ( ) ) "); 1949} 1950 1951svn_error_t * 1952svn_ra_svn__write_cmd_reparent(svn_ra_svn_conn_t *conn, 1953 apr_pool_t *pool, 1954 const char *url) 1955{ 1956 SVN_ERR(writebuf_write_literal(conn, pool, "( reparent ( ")); 1957 SVN_ERR(write_tuple_cstring(conn, pool, url)); 1958 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1959 1960 return SVN_NO_ERROR; 1961} 1962 1963svn_error_t * 1964svn_ra_svn__write_cmd_get_latest_rev(svn_ra_svn_conn_t *conn, 1965 apr_pool_t *pool) 1966{ 1967 return writebuf_write_literal(conn, pool, "( get-latest-rev ( ) ) "); 1968} 1969 1970svn_error_t * 1971svn_ra_svn__write_cmd_get_dated_rev(svn_ra_svn_conn_t *conn, 1972 apr_pool_t *pool, 1973 apr_time_t tm) 1974{ 1975 SVN_ERR(writebuf_write_literal(conn, pool, "( get-dated-rev ( ")); 1976 SVN_ERR(write_tuple_cstring(conn, pool, svn_time_to_cstring(tm, pool))); 1977 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1978 1979 return SVN_NO_ERROR; 1980} 1981 1982svn_error_t * 1983svn_ra_svn__write_cmd_change_rev_prop2(svn_ra_svn_conn_t *conn, 1984 apr_pool_t *pool, 1985 svn_revnum_t rev, 1986 const char *name, 1987 const svn_string_t *value, 1988 svn_boolean_t dont_care, 1989 const svn_string_t *old_value) 1990{ 1991 SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop2 ( ")); 1992 SVN_ERR(write_tuple_revision(conn, pool, rev)); 1993 SVN_ERR(write_tuple_cstring(conn, pool, name)); 1994 SVN_ERR(write_tuple_start_list(conn, pool)); 1995 SVN_ERR(write_tuple_string_opt(conn, pool, value)); 1996 SVN_ERR(write_tuple_end_list(conn, pool)); 1997 SVN_ERR(write_tuple_start_list(conn, pool)); 1998 SVN_ERR(write_tuple_boolean(conn, pool, dont_care)); 1999 SVN_ERR(write_tuple_string_opt(conn, pool, old_value)); 2000 SVN_ERR(write_tuple_end_list(conn, pool)); 2001 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2002 2003 return SVN_NO_ERROR; 2004} 2005 2006svn_error_t * 2007svn_ra_svn__write_cmd_change_rev_prop(svn_ra_svn_conn_t *conn, 2008 apr_pool_t *pool, 2009 svn_revnum_t rev, 2010 const char *name, 2011 const svn_string_t *value) 2012{ 2013 SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop ( ")); 2014 SVN_ERR(write_tuple_revision(conn, pool, rev)); 2015 SVN_ERR(write_tuple_cstring(conn, pool, name)); 2016 SVN_ERR(write_tuple_string_opt(conn, pool, value)); 2017 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2018 2019 return SVN_NO_ERROR; 2020} 2021 2022svn_error_t * 2023svn_ra_svn__write_cmd_rev_proplist(svn_ra_svn_conn_t *conn, 2024 apr_pool_t *pool, 2025 svn_revnum_t rev) 2026{ 2027 SVN_ERR(writebuf_write_literal(conn, pool, "( rev-proplist ( ")); 2028 SVN_ERR(write_tuple_revision(conn, pool, rev)); 2029 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2030 2031 return SVN_NO_ERROR; 2032} 2033 2034svn_error_t * 2035svn_ra_svn__write_cmd_rev_prop(svn_ra_svn_conn_t *conn, 2036 apr_pool_t *pool, 2037 svn_revnum_t rev, 2038 const char *name) 2039{ 2040 SVN_ERR(writebuf_write_literal(conn, pool, "( rev-prop ( ")); 2041 SVN_ERR(write_tuple_revision(conn, pool, rev)); 2042 SVN_ERR(write_tuple_cstring(conn, pool, name)); 2043 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2044 2045 return SVN_NO_ERROR; 2046} 2047 2048svn_error_t * 2049svn_ra_svn__write_cmd_get_file(svn_ra_svn_conn_t *conn, 2050 apr_pool_t *pool, 2051 const char *path, 2052 svn_revnum_t rev, 2053 svn_boolean_t props, 2054 svn_boolean_t stream) 2055{ 2056 SVN_ERR(writebuf_write_literal(conn, pool, "( get-file ( ")); 2057 SVN_ERR(write_tuple_cstring(conn, pool, path)); 2058 SVN_ERR(write_tuple_start_list(conn, pool)); 2059 SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); 2060 SVN_ERR(write_tuple_end_list(conn, pool)); 2061 SVN_ERR(write_tuple_boolean(conn, pool, props)); 2062 SVN_ERR(write_tuple_boolean(conn, pool, stream)); 2063 2064 /* Always send the, nominally optional, want-iprops as "false" to 2065 workaround a bug in svnserve 1.8.0-1.8.8 that causes the server 2066 to see "true" if it is omitted. */ 2067 SVN_ERR(writebuf_write_literal(conn, pool, " false ) ) ")); 2068 2069 return SVN_NO_ERROR; 2070} 2071 2072svn_error_t * 2073svn_ra_svn__write_cmd_update(svn_ra_svn_conn_t *conn, 2074 apr_pool_t *pool, 2075 svn_revnum_t rev, 2076 const char *target, 2077 svn_boolean_t recurse, 2078 svn_depth_t depth, 2079 svn_boolean_t send_copyfrom_args, 2080 svn_boolean_t ignore_ancestry) 2081{ 2082 SVN_ERR(writebuf_write_literal(conn, pool, "( update ( ")); 2083 SVN_ERR(write_tuple_start_list(conn, pool)); 2084 SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); 2085 SVN_ERR(write_tuple_end_list(conn, pool)); 2086 SVN_ERR(write_tuple_cstring(conn, pool, target)); 2087 SVN_ERR(write_tuple_boolean(conn, pool, recurse)); 2088 SVN_ERR(write_tuple_depth(conn, pool, depth)); 2089 SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args)); 2090 SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry)); 2091 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2092 2093 return SVN_NO_ERROR; 2094} 2095 2096svn_error_t * 2097svn_ra_svn__write_cmd_switch(svn_ra_svn_conn_t *conn, 2098 apr_pool_t *pool, 2099 svn_revnum_t rev, 2100 const char *target, 2101 svn_boolean_t recurse, 2102 const char *switch_url, 2103 svn_depth_t depth, 2104 svn_boolean_t send_copyfrom_args, 2105 svn_boolean_t ignore_ancestry) 2106{ 2107 SVN_ERR(writebuf_write_literal(conn, pool, "( switch ( ")); 2108 SVN_ERR(write_tuple_start_list(conn, pool)); 2109 SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); 2110 SVN_ERR(write_tuple_end_list(conn, pool)); 2111 SVN_ERR(write_tuple_cstring(conn, pool, target)); 2112 SVN_ERR(write_tuple_boolean(conn, pool, recurse)); 2113 SVN_ERR(write_tuple_cstring(conn, pool, switch_url)); 2114 SVN_ERR(write_tuple_depth(conn, pool, depth)); 2115 SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args)); 2116 SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry)); 2117 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2118 2119 return SVN_NO_ERROR; 2120} 2121 2122svn_error_t * 2123svn_ra_svn__write_cmd_status(svn_ra_svn_conn_t *conn, 2124 apr_pool_t *pool, 2125 const char *target, 2126 svn_boolean_t recurse, 2127 svn_revnum_t rev, 2128 svn_depth_t depth) 2129{ 2130 SVN_ERR(writebuf_write_literal(conn, pool, "( status ( ")); 2131 SVN_ERR(write_tuple_cstring(conn, pool, target)); 2132 SVN_ERR(write_tuple_boolean(conn, pool, recurse)); 2133 SVN_ERR(write_tuple_start_list(conn, pool)); 2134 SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); 2135 SVN_ERR(write_tuple_end_list(conn, pool)); 2136 SVN_ERR(write_tuple_depth(conn, pool, depth)); 2137 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2138 2139 return SVN_NO_ERROR; 2140} 2141 2142svn_error_t * 2143svn_ra_svn__write_cmd_diff(svn_ra_svn_conn_t *conn, 2144 apr_pool_t *pool, 2145 svn_revnum_t rev, 2146 const char *target, 2147 svn_boolean_t recurse, 2148 svn_boolean_t ignore_ancestry, 2149 const char *versus_url, 2150 svn_boolean_t text_deltas, 2151 svn_depth_t depth) 2152{ 2153 SVN_ERR(writebuf_write_literal(conn, pool, "( diff ( ")); 2154 SVN_ERR(write_tuple_start_list(conn, pool)); 2155 SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); 2156 SVN_ERR(write_tuple_end_list(conn, pool)); 2157 SVN_ERR(write_tuple_cstring(conn, pool, target)); 2158 SVN_ERR(write_tuple_boolean(conn, pool, recurse)); 2159 SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry)); 2160 SVN_ERR(write_tuple_cstring(conn, pool, versus_url)); 2161 SVN_ERR(write_tuple_boolean(conn, pool, text_deltas)); 2162 SVN_ERR(write_tuple_depth(conn, pool, depth)); 2163 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2164 2165 return SVN_NO_ERROR; 2166} 2167 2168svn_error_t * 2169svn_ra_svn__write_cmd_check_path(svn_ra_svn_conn_t *conn, 2170 apr_pool_t *pool, 2171 const char *path, 2172 svn_revnum_t rev) 2173{ 2174 SVN_ERR(writebuf_write_literal(conn, pool, "( check-path ( ")); 2175 SVN_ERR(write_tuple_cstring(conn, pool, path)); 2176 SVN_ERR(write_tuple_start_list(conn, pool)); 2177 SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); 2178 SVN_ERR(write_tuple_end_list(conn, pool)); 2179 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2180 2181 return SVN_NO_ERROR; 2182} 2183 2184svn_error_t * 2185svn_ra_svn__write_cmd_stat(svn_ra_svn_conn_t *conn, 2186 apr_pool_t *pool, 2187 const char *path, 2188 svn_revnum_t rev) 2189{ 2190 SVN_ERR(writebuf_write_literal(conn, pool, "( stat ( ")); 2191 SVN_ERR(write_tuple_cstring(conn, pool, path)); 2192 SVN_ERR(write_tuple_start_list(conn, pool)); 2193 SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); 2194 SVN_ERR(write_tuple_end_list(conn, pool)); 2195 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2196 2197 return SVN_NO_ERROR; 2198} 2199 2200svn_error_t * 2201svn_ra_svn__write_cmd_get_file_revs(svn_ra_svn_conn_t *conn, 2202 apr_pool_t *pool, 2203 const char *path, 2204 svn_revnum_t start, 2205 svn_revnum_t end, 2206 svn_boolean_t include_merged_revisions) 2207{ 2208 SVN_ERR(writebuf_write_literal(conn, pool, "( get-file-revs ( ")); 2209 SVN_ERR(write_tuple_cstring(conn, pool, path)); 2210 SVN_ERR(write_tuple_start_list(conn, pool)); 2211 SVN_ERR(write_tuple_revision_opt(conn, pool, start)); 2212 SVN_ERR(write_tuple_end_list(conn, pool)); 2213 SVN_ERR(write_tuple_start_list(conn, pool)); 2214 SVN_ERR(write_tuple_revision_opt(conn, pool, end)); 2215 SVN_ERR(write_tuple_end_list(conn, pool)); 2216 SVN_ERR(write_tuple_boolean(conn, pool, include_merged_revisions)); 2217 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2218 2219 return SVN_NO_ERROR; 2220} 2221 2222svn_error_t * 2223svn_ra_svn__write_cmd_lock(svn_ra_svn_conn_t *conn, 2224 apr_pool_t *pool, 2225 const char *path, 2226 const char *comment, 2227 svn_boolean_t steal_lock, 2228 svn_revnum_t revnum) 2229{ 2230 SVN_ERR(writebuf_write_literal(conn, pool, "( lock ( ")); 2231 SVN_ERR(write_tuple_cstring(conn, pool, path)); 2232 SVN_ERR(write_tuple_start_list(conn, pool)); 2233 SVN_ERR(write_tuple_cstring_opt(conn, pool, comment)); 2234 SVN_ERR(write_tuple_end_list(conn, pool)); 2235 SVN_ERR(write_tuple_boolean(conn, pool, steal_lock)); 2236 SVN_ERR(write_tuple_start_list(conn, pool)); 2237 SVN_ERR(write_tuple_revision_opt(conn, pool, revnum)); 2238 SVN_ERR(write_tuple_end_list(conn, pool)); 2239 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2240 2241 return SVN_NO_ERROR; 2242} 2243 2244svn_error_t * 2245svn_ra_svn__write_cmd_unlock(svn_ra_svn_conn_t *conn, 2246 apr_pool_t *pool, 2247 const char *path, 2248 const char *token, 2249 svn_boolean_t break_lock) 2250{ 2251 SVN_ERR(writebuf_write_literal(conn, pool, "( unlock ( ")); 2252 SVN_ERR(write_tuple_cstring(conn, pool, path)); 2253 SVN_ERR(write_tuple_start_list(conn, pool)); 2254 SVN_ERR(write_tuple_cstring_opt(conn, pool, token)); 2255 SVN_ERR(write_tuple_end_list(conn, pool)); 2256 SVN_ERR(write_tuple_boolean(conn, pool, break_lock)); 2257 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2258 2259 return SVN_NO_ERROR; 2260} 2261 2262svn_error_t * 2263svn_ra_svn__write_cmd_get_lock(svn_ra_svn_conn_t *conn, 2264 apr_pool_t *pool, 2265 const char *path) 2266{ 2267 SVN_ERR(writebuf_write_literal(conn, pool, "( get-lock ( ")); 2268 SVN_ERR(write_tuple_cstring(conn, pool, path)); 2269 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2270 2271 return SVN_NO_ERROR; 2272} 2273 2274svn_error_t * 2275svn_ra_svn__write_cmd_get_locks(svn_ra_svn_conn_t *conn, 2276 apr_pool_t *pool, 2277 const char *path, 2278 svn_depth_t depth) 2279{ 2280 SVN_ERR(writebuf_write_literal(conn, pool, "( get-locks ( ")); 2281 SVN_ERR(write_tuple_cstring(conn, pool, path)); 2282 SVN_ERR(write_tuple_start_list(conn, pool)); 2283 SVN_ERR(write_tuple_depth(conn, pool, depth)); 2284 SVN_ERR(write_tuple_end_list(conn, pool)); 2285 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2286 2287 return SVN_NO_ERROR; 2288} 2289 2290svn_error_t * 2291svn_ra_svn__write_cmd_replay(svn_ra_svn_conn_t *conn, 2292 apr_pool_t *pool, 2293 svn_revnum_t rev, 2294 svn_revnum_t low_water_mark, 2295 svn_boolean_t send_deltas) 2296{ 2297 SVN_ERR(writebuf_write_literal(conn, pool, "( replay ( ")); 2298 SVN_ERR(write_tuple_revision(conn, pool, rev)); 2299 SVN_ERR(write_tuple_revision(conn, pool, low_water_mark)); 2300 SVN_ERR(write_tuple_boolean(conn, pool, send_deltas)); 2301 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2302 2303 return SVN_NO_ERROR; 2304} 2305 2306svn_error_t * 2307svn_ra_svn__write_cmd_replay_range(svn_ra_svn_conn_t *conn, 2308 apr_pool_t *pool, 2309 svn_revnum_t start_revision, 2310 svn_revnum_t end_revision, 2311 svn_revnum_t low_water_mark, 2312 svn_boolean_t send_deltas) 2313{ 2314 SVN_ERR(writebuf_write_literal(conn, pool, "( replay-range ( ")); 2315 SVN_ERR(write_tuple_revision(conn, pool, start_revision)); 2316 SVN_ERR(write_tuple_revision(conn, pool, end_revision)); 2317 SVN_ERR(write_tuple_revision(conn, pool, low_water_mark)); 2318 SVN_ERR(write_tuple_boolean(conn, pool, send_deltas)); 2319 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2320 2321 return SVN_NO_ERROR; 2322} 2323 2324svn_error_t * 2325svn_ra_svn__write_cmd_get_deleted_rev(svn_ra_svn_conn_t *conn, 2326 apr_pool_t *pool, 2327 const char *path, 2328 svn_revnum_t peg_revision, 2329 svn_revnum_t end_revision) 2330{ 2331 SVN_ERR(writebuf_write_literal(conn, pool, "( get-deleted-rev ( ")); 2332 SVN_ERR(write_tuple_cstring(conn, pool, path)); 2333 SVN_ERR(write_tuple_revision(conn, pool, peg_revision)); 2334 SVN_ERR(write_tuple_revision(conn, pool, end_revision)); 2335 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2336 2337 return SVN_NO_ERROR; 2338} 2339 2340svn_error_t * 2341svn_ra_svn__write_cmd_get_iprops(svn_ra_svn_conn_t *conn, 2342 apr_pool_t *pool, 2343 const char *path, 2344 svn_revnum_t revision) 2345{ 2346 SVN_ERR(writebuf_write_literal(conn, pool, "( get-iprops ( ")); 2347 SVN_ERR(write_tuple_cstring(conn, pool, path)); 2348 SVN_ERR(write_tuple_start_list(conn, pool)); 2349 SVN_ERR(write_tuple_revision_opt(conn, pool, revision)); 2350 SVN_ERR(write_tuple_end_list(conn, pool)); 2351 SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2352 2353 return SVN_NO_ERROR; 2354} 2355 2356svn_error_t * 2357svn_ra_svn__write_cmd_finish_replay(svn_ra_svn_conn_t *conn, 2358 apr_pool_t *pool) 2359{ 2360 return writebuf_write_literal(conn, pool, "( finish-replay ( ) ) "); 2361} 2362 2363svn_error_t *svn_ra_svn__write_cmd_response(svn_ra_svn_conn_t *conn, 2364 apr_pool_t *pool, 2365 const char *fmt, ...) 2366{ 2367 va_list ap; 2368 svn_error_t *err; 2369 2370 SVN_ERR(writebuf_write_literal(conn, pool, "( success ")); 2371 va_start(ap, fmt); 2372 err = vwrite_tuple(conn, pool, fmt, &ap); 2373 va_end(ap); 2374 return err ? svn_error_trace(err) : svn_ra_svn__end_list(conn, pool); 2375} 2376 2377svn_error_t *svn_ra_svn__write_cmd_failure(svn_ra_svn_conn_t *conn, 2378 apr_pool_t *pool, 2379 const svn_error_t *err) 2380{ 2381 char buffer[128]; 2382 SVN_ERR(writebuf_write_literal(conn, pool, "( failure ( ")); 2383 for (; err; err = err->child) 2384 { 2385 const char *msg; 2386 2387#ifdef SVN_ERR__TRACING 2388 if (svn_error__is_tracing_link(err)) 2389 msg = err->message; 2390 else 2391#endif 2392 msg = svn_err_best_message(err, buffer, sizeof(buffer)); 2393 2394 /* The message string should have been optional, but we can't 2395 easily change that, so marshal nonexistent messages as "". */ 2396 SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "nccn", 2397 (apr_uint64_t) err->apr_err, 2398 msg ? msg : "", 2399 err->file ? err->file : "", 2400 (apr_uint64_t) err->line)); 2401 } 2402 return writebuf_write_literal(conn, pool, ") ) "); 2403} 2404 2405svn_error_t * 2406svn_ra_svn__write_data_log_changed_path(svn_ra_svn_conn_t *conn, 2407 apr_pool_t *pool, 2408 const char *path, 2409 char action, 2410 const char *copyfrom_path, 2411 svn_revnum_t copyfrom_rev, 2412 svn_node_kind_t node_kind, 2413 svn_boolean_t text_modified, 2414 svn_boolean_t props_modified) 2415{ 2416 SVN_ERR(write_tuple_start_list(conn, pool)); 2417 2418 SVN_ERR(write_tuple_cstring(conn, pool, path)); 2419 SVN_ERR(writebuf_writechar(conn, pool, action)); 2420 SVN_ERR(writebuf_writechar(conn, pool, ' ')); 2421 SVN_ERR(write_tuple_start_list(conn, pool)); 2422 SVN_ERR(write_tuple_cstring_opt(conn, pool, copyfrom_path)); 2423 SVN_ERR(write_tuple_revision_opt(conn, pool, copyfrom_rev)); 2424 SVN_ERR(write_tuple_end_list(conn, pool)); 2425 SVN_ERR(write_tuple_start_list(conn, pool)); 2426 SVN_ERR(write_tuple_cstring(conn, pool, svn_node_kind_to_word(node_kind))); 2427 SVN_ERR(write_tuple_boolean(conn, pool, text_modified)); 2428 SVN_ERR(write_tuple_boolean(conn, pool, props_modified)); 2429 2430 return writebuf_write_literal(conn, pool, ") ) "); 2431} 2432 2433svn_error_t * 2434svn_ra_svn__write_data_log_entry(svn_ra_svn_conn_t *conn, 2435 apr_pool_t *pool, 2436 svn_revnum_t revision, 2437 const svn_string_t *author, 2438 const svn_string_t *date, 2439 const svn_string_t *message, 2440 svn_boolean_t has_children, 2441 svn_boolean_t invalid_revnum, 2442 unsigned revprop_count) 2443{ 2444 SVN_ERR(write_tuple_revision(conn, pool, revision)); 2445 SVN_ERR(write_tuple_start_list(conn, pool)); 2446 SVN_ERR(write_tuple_string_opt(conn, pool, author)); 2447 SVN_ERR(write_tuple_end_list(conn, pool)); 2448 SVN_ERR(write_tuple_start_list(conn, pool)); 2449 SVN_ERR(write_tuple_string_opt(conn, pool, date)); 2450 SVN_ERR(write_tuple_end_list(conn, pool)); 2451 SVN_ERR(write_tuple_start_list(conn, pool)); 2452 SVN_ERR(write_tuple_string_opt(conn, pool, message)); 2453 SVN_ERR(write_tuple_end_list(conn, pool)); 2454 SVN_ERR(write_tuple_boolean(conn, pool, has_children)); 2455 SVN_ERR(write_tuple_boolean(conn, pool, invalid_revnum)); 2456 SVN_ERR(svn_ra_svn__write_number(conn, pool, revprop_count)); 2457 2458 return SVN_NO_ERROR; 2459} 2460 2461/* If condition COND is not met, return a "malformed network data" error. 2462 */ 2463#define CHECK_PROTOCOL_COND(cond)\ 2464 if (!(cond)) \ 2465 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, \ 2466 _("Malformed network data")); 2467 2468/* In *RESULT, return the SVN-style string at index IDX in tuple ITEMS. 2469 */ 2470static svn_error_t * 2471svn_ra_svn__read_string(const apr_array_header_t *items, 2472 int idx, 2473 svn_string_t **result) 2474{ 2475 svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); 2476 CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING); 2477 *result = elt->u.string; 2478 2479 return SVN_NO_ERROR; 2480} 2481 2482/* In *RESULT, return the C-style string at index IDX in tuple ITEMS. 2483 */ 2484static svn_error_t * 2485svn_ra_svn__read_cstring(const apr_array_header_t *items, 2486 int idx, 2487 const char **result) 2488{ 2489 svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); 2490 CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING); 2491 *result = elt->u.string->data; 2492 2493 return SVN_NO_ERROR; 2494} 2495 2496/* In *RESULT, return the word at index IDX in tuple ITEMS. 2497 */ 2498static svn_error_t * 2499svn_ra_svn__read_word(const apr_array_header_t *items, 2500 int idx, 2501 const char **result) 2502{ 2503 svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); 2504 CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD); 2505 *result = elt->u.word; 2506 2507 return SVN_NO_ERROR; 2508} 2509 2510/* In *RESULT, return the revision at index IDX in tuple ITEMS. 2511 */ 2512static svn_error_t * 2513svn_ra_svn__read_revision(const apr_array_header_t *items, 2514 int idx, 2515 svn_revnum_t *result) 2516{ 2517 svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); 2518 CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_NUMBER); 2519 *result = (svn_revnum_t)elt->u.number; 2520 2521 return SVN_NO_ERROR; 2522} 2523 2524/* In *RESULT, return the boolean at index IDX in tuple ITEMS. 2525 */ 2526static svn_error_t * 2527svn_ra_svn__read_boolean(const apr_array_header_t *items, 2528 int idx, 2529 apr_uint64_t *result) 2530{ 2531 svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); 2532 CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD); 2533 if (elt->u.word[0] == 't' && strcmp(elt->u.word, "true") == 0) 2534 *result = TRUE; 2535 else if (strcmp(elt->u.word, "false") == 0) 2536 *result = FALSE; 2537 else 2538 CHECK_PROTOCOL_COND(FALSE); 2539 2540 return SVN_NO_ERROR; 2541} 2542 2543/* In *RESULT, return the tuple at index IDX in tuple ITEMS. 2544 */ 2545static svn_error_t * 2546svn_ra_svn__read_list(const apr_array_header_t *items, 2547 int idx, 2548 const apr_array_header_t **result) 2549{ 2550 svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); 2551 CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_LIST); 2552 2553 *result = elt->u.list; 2554 return SVN_NO_ERROR; 2555} 2556 2557/* Verify the tuple ITEMS contains at least MIN and at most MAX elements. 2558 */ 2559static svn_error_t * 2560svn_ra_svn__read_check_array_size(const apr_array_header_t *items, 2561 int min, 2562 int max) 2563{ 2564 CHECK_PROTOCOL_COND(items->nelts >= min && items->nelts <= max); 2565 return SVN_NO_ERROR; 2566} 2567 2568svn_error_t * 2569svn_ra_svn__read_data_log_changed_entry(const apr_array_header_t *items, 2570 svn_string_t **cpath, 2571 const char **action, 2572 const char **copy_path, 2573 svn_revnum_t *copy_rev, 2574 const char **kind_str, 2575 apr_uint64_t *text_mods, 2576 apr_uint64_t *prop_mods) 2577{ 2578 const apr_array_header_t *sub_items; 2579 2580 /* initialize optional values */ 2581 *copy_path = NULL; 2582 *copy_rev = SVN_INVALID_REVNUM; 2583 *kind_str = NULL; 2584 *text_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER; 2585 *prop_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER; 2586 2587 /* top-level elements (mandatory) */ 2588 SVN_ERR(svn_ra_svn__read_check_array_size(items, 3, INT_MAX)); 2589 SVN_ERR(svn_ra_svn__read_string(items, 0, cpath)); 2590 SVN_ERR(svn_ra_svn__read_word(items, 1, action)); 2591 2592 /* first sub-structure (mandatory) */ 2593 SVN_ERR(svn_ra_svn__read_list(items, 2, &sub_items)); 2594 if (sub_items->nelts) 2595 { 2596 SVN_ERR(svn_ra_svn__read_check_array_size(sub_items, 2, 2)); 2597 SVN_ERR(svn_ra_svn__read_cstring(sub_items, 0, copy_path)); 2598 SVN_ERR(svn_ra_svn__read_revision(sub_items, 1, copy_rev)); 2599 } 2600 2601 /* second sub-structure (optional) */ 2602 if (items->nelts >= 4) 2603 { 2604 SVN_ERR(svn_ra_svn__read_list(items, 3, &sub_items)); 2605 switch (MIN(3, sub_items->nelts)) 2606 { 2607 case 3 : SVN_ERR(svn_ra_svn__read_boolean(sub_items, 2, prop_mods)); 2608 case 2 : SVN_ERR(svn_ra_svn__read_boolean(sub_items, 1, text_mods)); 2609 case 1 : SVN_ERR(svn_ra_svn__read_cstring(sub_items, 0, kind_str)); 2610 default: break; 2611 } 2612 } 2613 2614 return SVN_NO_ERROR; 2615} 2616