1251881Speter/* 2251881Speter * marshal.c : Marshalling routines for Subversion protocol 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter 25251881Speter 26251881Speter#include <assert.h> 27251881Speter#include <stdlib.h> 28251881Speter 29251881Speter#define APR_WANT_STRFUNC 30251881Speter#include <apr_want.h> 31251881Speter#include <apr_general.h> 32251881Speter#include <apr_lib.h> 33251881Speter#include <apr_strings.h> 34251881Speter 35251881Speter#include "svn_hash.h" 36251881Speter#include "svn_types.h" 37251881Speter#include "svn_string.h" 38251881Speter#include "svn_error.h" 39251881Speter#include "svn_pools.h" 40251881Speter#include "svn_ra_svn.h" 41251881Speter#include "svn_private_config.h" 42251881Speter#include "svn_ctype.h" 43299742Sdim#include "svn_sorts.h" 44251881Speter#include "svn_time.h" 45251881Speter 46251881Speter#include "ra_svn.h" 47251881Speter 48251881Speter#include "private/svn_string_private.h" 49251881Speter#include "private/svn_dep_compat.h" 50251881Speter#include "private/svn_error_private.h" 51299742Sdim#include "private/svn_subr_private.h" 52251881Speter 53251881Speter#define svn_iswhitespace(c) ((c) == ' ' || (c) == '\n') 54251881Speter 55251881Speter/* If we receive data that *claims* to be followed by a very long string, 56251881Speter * we should not trust that claim right away. But everything up to 1 MB 57251881Speter * should be too small to be instrumental for a DOS attack. */ 58251881Speter 59251881Speter#define SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD (0x100000) 60251881Speter 61299742Sdim/* We don't use "words" longer than this in our protocol. The longest word 62299742Sdim * we are currently using is only about 16 chars long but we leave room for 63299742Sdim * longer future capability and command names. 64299742Sdim */ 65299742Sdim#define MAX_WORD_LENGTH 31 66299742Sdim 67299742Sdim/* The generic parsers will use the following value to limit the recursion 68299742Sdim * depth to some reasonable value. The current protocol implementation 69299742Sdim * actually uses only maximum item nesting level of around 5. So, there is 70299742Sdim * plenty of headroom here. 71299742Sdim */ 72299742Sdim#define ITEM_NESTING_LIMIT 64 73299742Sdim 74251881Speter/* Return the APR socket timeout to be used for the connection depending 75251881Speter * on whether there is a blockage handler or zero copy has been activated. */ 76251881Speterstatic apr_interval_time_t 77251881Speterget_timeout(svn_ra_svn_conn_t *conn) 78251881Speter{ 79251881Speter return conn->block_handler ? 0 : -1; 80251881Speter} 81251881Speter 82251881Speter/* --- CONNECTION INITIALIZATION --- */ 83251881Speter 84299742Sdimsvn_ra_svn_conn_t *svn_ra_svn_create_conn4(apr_socket_t *sock, 85299742Sdim svn_stream_t *in_stream, 86299742Sdim svn_stream_t *out_stream, 87251881Speter int compression_level, 88251881Speter apr_size_t zero_copy_limit, 89251881Speter apr_size_t error_check_interval, 90299742Sdim apr_pool_t *result_pool) 91251881Speter{ 92251881Speter svn_ra_svn_conn_t *conn; 93299742Sdim void *mem = apr_palloc(result_pool, sizeof(*conn) + SVN_RA_SVN__PAGE_SIZE); 94251881Speter conn = (void*)APR_ALIGN((apr_uintptr_t)mem, SVN_RA_SVN__PAGE_SIZE); 95251881Speter 96299742Sdim assert((sock && !in_stream && !out_stream) 97299742Sdim || (!sock && in_stream && out_stream)); 98251881Speter#ifdef SVN_HAVE_SASL 99251881Speter conn->sock = sock; 100251881Speter conn->encrypted = FALSE; 101251881Speter#endif 102251881Speter conn->session = NULL; 103251881Speter conn->read_ptr = conn->read_buf; 104251881Speter conn->read_end = conn->read_buf; 105251881Speter conn->write_pos = 0; 106251881Speter conn->written_since_error_check = 0; 107251881Speter conn->error_check_interval = error_check_interval; 108251881Speter conn->may_check_for_error = error_check_interval == 0; 109251881Speter conn->block_handler = NULL; 110251881Speter conn->block_baton = NULL; 111299742Sdim conn->capabilities = apr_hash_make(result_pool); 112251881Speter conn->compression_level = compression_level; 113251881Speter conn->zero_copy_limit = zero_copy_limit; 114299742Sdim conn->pool = result_pool; 115251881Speter 116251881Speter if (sock != NULL) 117251881Speter { 118251881Speter apr_sockaddr_t *sa; 119299742Sdim conn->stream = svn_ra_svn__stream_from_sock(sock, result_pool); 120251881Speter if (!(apr_socket_addr_get(&sa, APR_REMOTE, sock) == APR_SUCCESS 121251881Speter && apr_sockaddr_ip_get(&conn->remote_ip, sa) == APR_SUCCESS)) 122251881Speter conn->remote_ip = NULL; 123251881Speter svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn)); 124251881Speter } 125251881Speter else 126251881Speter { 127299742Sdim conn->stream = svn_ra_svn__stream_from_streams(in_stream, out_stream, 128299742Sdim result_pool); 129251881Speter conn->remote_ip = NULL; 130251881Speter } 131251881Speter 132251881Speter return conn; 133251881Speter} 134251881Speter 135251881Spetersvn_error_t *svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn, 136251881Speter const apr_array_header_t *list) 137251881Speter{ 138251881Speter int i; 139251881Speter svn_ra_svn_item_t *item; 140251881Speter const char *word; 141251881Speter 142251881Speter for (i = 0; i < list->nelts; i++) 143251881Speter { 144251881Speter item = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t); 145251881Speter if (item->kind != SVN_RA_SVN_WORD) 146251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 147251881Speter _("Capability entry is not a word")); 148251881Speter word = apr_pstrdup(conn->pool, item->u.word); 149251881Speter svn_hash_sets(conn->capabilities, word, word); 150251881Speter } 151251881Speter return SVN_NO_ERROR; 152251881Speter} 153251881Speter 154299742Sdimapr_pool_t * 155299742Sdimsvn_ra_svn__get_pool(svn_ra_svn_conn_t *conn) 156299742Sdim{ 157299742Sdim return conn->pool; 158299742Sdim} 159299742Sdim 160251881Spetersvn_error_t * 161251881Spetersvn_ra_svn__set_shim_callbacks(svn_ra_svn_conn_t *conn, 162251881Speter svn_delta_shim_callbacks_t *shim_callbacks) 163251881Speter{ 164251881Speter conn->shim_callbacks = shim_callbacks; 165251881Speter return SVN_NO_ERROR; 166251881Speter} 167251881Speter 168251881Spetersvn_boolean_t svn_ra_svn_has_capability(svn_ra_svn_conn_t *conn, 169251881Speter const char *capability) 170251881Speter{ 171251881Speter return (svn_hash_gets(conn->capabilities, capability) != NULL); 172251881Speter} 173251881Speter 174251881Speterint 175251881Spetersvn_ra_svn_compression_level(svn_ra_svn_conn_t *conn) 176251881Speter{ 177251881Speter return conn->compression_level; 178251881Speter} 179251881Speter 180251881Speterapr_size_t 181251881Spetersvn_ra_svn_zero_copy_limit(svn_ra_svn_conn_t *conn) 182251881Speter{ 183251881Speter return conn->zero_copy_limit; 184251881Speter} 185251881Speter 186251881Speterconst char *svn_ra_svn_conn_remote_host(svn_ra_svn_conn_t *conn) 187251881Speter{ 188251881Speter return conn->remote_ip; 189251881Speter} 190251881Speter 191251881Spetervoid 192251881Spetersvn_ra_svn__set_block_handler(svn_ra_svn_conn_t *conn, 193251881Speter ra_svn_block_handler_t handler, 194251881Speter void *baton) 195251881Speter{ 196251881Speter conn->block_handler = handler; 197251881Speter conn->block_baton = baton; 198251881Speter svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn)); 199251881Speter} 200251881Speter 201299742Sdimsvn_error_t *svn_ra_svn__data_available(svn_ra_svn_conn_t *conn, 202299742Sdim svn_boolean_t *data_available) 203251881Speter{ 204299742Sdim return svn_ra_svn__stream_data_available(conn->stream, data_available); 205251881Speter} 206251881Speter 207251881Speter/* --- WRITE BUFFER MANAGEMENT --- */ 208251881Speter 209251881Speter/* Write data to socket or output file as appropriate. */ 210251881Speterstatic svn_error_t *writebuf_output(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 211251881Speter const char *data, apr_size_t len) 212251881Speter{ 213251881Speter const char *end = data + len; 214251881Speter apr_size_t count; 215251881Speter apr_pool_t *subpool = NULL; 216251881Speter svn_ra_svn__session_baton_t *session = conn->session; 217251881Speter 218251881Speter while (data < end) 219251881Speter { 220251881Speter count = end - data; 221251881Speter 222251881Speter if (session && session->callbacks && session->callbacks->cancel_func) 223251881Speter SVN_ERR((session->callbacks->cancel_func)(session->callbacks_baton)); 224251881Speter 225251881Speter SVN_ERR(svn_ra_svn__stream_write(conn->stream, data, &count)); 226251881Speter if (count == 0) 227251881Speter { 228251881Speter if (!subpool) 229251881Speter subpool = svn_pool_create(pool); 230251881Speter else 231251881Speter svn_pool_clear(subpool); 232251881Speter SVN_ERR(conn->block_handler(conn, subpool, conn->block_baton)); 233251881Speter } 234251881Speter data += count; 235251881Speter 236251881Speter if (session) 237251881Speter { 238251881Speter const svn_ra_callbacks2_t *cb = session->callbacks; 239251881Speter session->bytes_written += count; 240251881Speter 241251881Speter if (cb && cb->progress_func) 242251881Speter (cb->progress_func)(session->bytes_written + session->bytes_read, 243251881Speter -1, cb->progress_baton, subpool); 244251881Speter } 245251881Speter } 246251881Speter 247251881Speter conn->written_since_error_check += len; 248251881Speter conn->may_check_for_error 249251881Speter = conn->written_since_error_check >= conn->error_check_interval; 250251881Speter 251251881Speter if (subpool) 252251881Speter svn_pool_destroy(subpool); 253251881Speter return SVN_NO_ERROR; 254251881Speter} 255251881Speter 256251881Speter/* Write data from the write buffer out to the socket. */ 257251881Speterstatic svn_error_t *writebuf_flush(svn_ra_svn_conn_t *conn, apr_pool_t *pool) 258251881Speter{ 259251881Speter apr_size_t write_pos = conn->write_pos; 260251881Speter 261251881Speter /* Clear conn->write_pos first in case the block handler does a read. */ 262251881Speter conn->write_pos = 0; 263251881Speter SVN_ERR(writebuf_output(conn, pool, conn->write_buf, write_pos)); 264251881Speter return SVN_NO_ERROR; 265251881Speter} 266251881Speter 267251881Speterstatic svn_error_t *writebuf_write(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 268251881Speter const char *data, apr_size_t len) 269251881Speter{ 270251881Speter /* data >= 8k is sent immediately */ 271251881Speter if (len >= sizeof(conn->write_buf) / 2) 272251881Speter { 273251881Speter if (conn->write_pos > 0) 274251881Speter SVN_ERR(writebuf_flush(conn, pool)); 275251881Speter 276251881Speter return writebuf_output(conn, pool, data, len); 277251881Speter } 278251881Speter 279251881Speter /* ensure room for the data to add */ 280251881Speter if (conn->write_pos + len > sizeof(conn->write_buf)) 281251881Speter SVN_ERR(writebuf_flush(conn, pool)); 282251881Speter 283251881Speter /* buffer the new data block as well */ 284251881Speter memcpy(conn->write_buf + conn->write_pos, data, len); 285251881Speter conn->write_pos += len; 286251881Speter 287251881Speter return SVN_NO_ERROR; 288251881Speter} 289251881Speter 290299742Sdim/* Write STRING_LITERAL, which is a string literal argument. 291251881Speter 292299742Sdim Note: The purpose of the empty string "" in the macro definition is to 293299742Sdim assert that STRING_LITERAL is in fact a string literal. Otherwise, the 294299742Sdim string concatenation attempt should produce a compile-time error. */ 295299742Sdim#define writebuf_write_literal(conn, pool, string_literal) \ 296299742Sdim writebuf_write(conn, pool, string_literal, sizeof(string_literal "") - 1) 297299742Sdim 298251881Speterstatic APR_INLINE svn_error_t * 299251881Speterwritebuf_writechar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char data) 300251881Speter{ 301251881Speter if (conn->write_pos < sizeof(conn->write_buf)) 302251881Speter { 303251881Speter conn->write_buf[conn->write_pos] = data; 304251881Speter conn->write_pos++; 305251881Speter 306251881Speter return SVN_NO_ERROR; 307251881Speter } 308251881Speter else 309251881Speter { 310251881Speter char temp = data; 311251881Speter return writebuf_write(conn, pool, &temp, 1); 312251881Speter } 313251881Speter} 314251881Speter 315251881Speter/* --- READ BUFFER MANAGEMENT --- */ 316251881Speter 317251881Speter/* Read bytes into DATA until either the read buffer is empty or 318251881Speter * we reach END. */ 319251881Speterstatic char *readbuf_drain(svn_ra_svn_conn_t *conn, char *data, char *end) 320251881Speter{ 321251881Speter apr_ssize_t buflen, copylen; 322251881Speter 323251881Speter buflen = conn->read_end - conn->read_ptr; 324251881Speter copylen = (buflen < end - data) ? buflen : end - data; 325251881Speter memcpy(data, conn->read_ptr, copylen); 326251881Speter conn->read_ptr += copylen; 327251881Speter return data + copylen; 328251881Speter} 329251881Speter 330251881Speter/* Read data from socket or input file as appropriate. */ 331251881Speterstatic svn_error_t *readbuf_input(svn_ra_svn_conn_t *conn, char *data, 332251881Speter apr_size_t *len, apr_pool_t *pool) 333251881Speter{ 334251881Speter svn_ra_svn__session_baton_t *session = conn->session; 335251881Speter 336251881Speter if (session && session->callbacks && session->callbacks->cancel_func) 337251881Speter SVN_ERR((session->callbacks->cancel_func)(session->callbacks_baton)); 338251881Speter 339251881Speter SVN_ERR(svn_ra_svn__stream_read(conn->stream, data, len)); 340251881Speter if (*len == 0) 341251881Speter return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL); 342251881Speter 343251881Speter if (session) 344251881Speter { 345251881Speter const svn_ra_callbacks2_t *cb = session->callbacks; 346251881Speter session->bytes_read += *len; 347251881Speter 348251881Speter if (cb && cb->progress_func) 349251881Speter (cb->progress_func)(session->bytes_read + session->bytes_written, 350251881Speter -1, cb->progress_baton, pool); 351251881Speter } 352251881Speter 353251881Speter return SVN_NO_ERROR; 354251881Speter} 355251881Speter 356251881Speter/* Treat the next LEN input bytes from CONN as "read" */ 357251881Speterstatic svn_error_t *readbuf_skip(svn_ra_svn_conn_t *conn, apr_uint64_t len) 358251881Speter{ 359251881Speter do 360251881Speter { 361251881Speter apr_size_t buflen = conn->read_end - conn->read_ptr; 362251881Speter apr_size_t copylen = (buflen < len) ? buflen : (apr_size_t)len; 363251881Speter conn->read_ptr += copylen; 364251881Speter len -= copylen; 365251881Speter if (len == 0) 366251881Speter break; 367251881Speter 368251881Speter buflen = sizeof(conn->read_buf); 369251881Speter SVN_ERR(svn_ra_svn__stream_read(conn->stream, conn->read_buf, &buflen)); 370251881Speter if (buflen == 0) 371251881Speter return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL); 372251881Speter 373251881Speter conn->read_end = conn->read_buf + buflen; 374251881Speter conn->read_ptr = conn->read_buf; 375251881Speter } 376251881Speter while (len > 0); 377251881Speter 378251881Speter return SVN_NO_ERROR; 379251881Speter} 380251881Speter 381251881Speter/* Read data from the socket into the read buffer, which must be empty. */ 382251881Speterstatic svn_error_t *readbuf_fill(svn_ra_svn_conn_t *conn, apr_pool_t *pool) 383251881Speter{ 384251881Speter apr_size_t len; 385251881Speter 386251881Speter SVN_ERR_ASSERT(conn->read_ptr == conn->read_end); 387299742Sdim if (conn->write_pos) 388299742Sdim SVN_ERR(writebuf_flush(conn, pool)); 389299742Sdim 390251881Speter len = sizeof(conn->read_buf); 391251881Speter SVN_ERR(readbuf_input(conn, conn->read_buf, &len, pool)); 392251881Speter conn->read_ptr = conn->read_buf; 393251881Speter conn->read_end = conn->read_buf + len; 394251881Speter return SVN_NO_ERROR; 395251881Speter} 396251881Speter 397299742Sdim/* This is a hot function calling a cold function. GCC and others tend to 398299742Sdim * inline the cold sub-function instead of this hot one. Therefore, be 399299742Sdim * very insistent on lining this one. It is not a correctness issue, though. 400299742Sdim */ 401299742Sdimstatic SVN__FORCE_INLINE svn_error_t * 402251881Speterreadbuf_getchar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char *result) 403251881Speter{ 404251881Speter if (conn->read_ptr == conn->read_end) 405251881Speter SVN_ERR(readbuf_fill(conn, pool)); 406251881Speter *result = *conn->read_ptr++; 407251881Speter return SVN_NO_ERROR; 408251881Speter} 409251881Speter 410251881Speterstatic svn_error_t *readbuf_getchar_skip_whitespace(svn_ra_svn_conn_t *conn, 411251881Speter apr_pool_t *pool, 412251881Speter char *result) 413251881Speter{ 414251881Speter do 415251881Speter SVN_ERR(readbuf_getchar(conn, pool, result)); 416251881Speter while (svn_iswhitespace(*result)); 417251881Speter return SVN_NO_ERROR; 418251881Speter} 419251881Speter 420251881Speter/* Read the next LEN bytes from CONN and copy them to *DATA. */ 421251881Speterstatic svn_error_t *readbuf_read(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 422251881Speter char *data, apr_size_t len) 423251881Speter{ 424251881Speter char *end = data + len; 425251881Speter apr_size_t count; 426251881Speter 427251881Speter /* Copy in an appropriate amount of data from the buffer. */ 428251881Speter data = readbuf_drain(conn, data, end); 429251881Speter 430251881Speter /* Read large chunks directly into buffer. */ 431251881Speter while (end - data > (apr_ssize_t)sizeof(conn->read_buf)) 432251881Speter { 433251881Speter SVN_ERR(writebuf_flush(conn, pool)); 434251881Speter count = end - data; 435251881Speter SVN_ERR(readbuf_input(conn, data, &count, pool)); 436251881Speter data += count; 437251881Speter } 438251881Speter 439251881Speter while (end > data) 440251881Speter { 441251881Speter /* The remaining amount to read is small; fill the buffer and 442251881Speter * copy from that. */ 443251881Speter SVN_ERR(readbuf_fill(conn, pool)); 444251881Speter data = readbuf_drain(conn, data, end); 445251881Speter } 446251881Speter 447251881Speter return SVN_NO_ERROR; 448251881Speter} 449251881Speter 450251881Speterstatic svn_error_t *readbuf_skip_leading_garbage(svn_ra_svn_conn_t *conn, 451251881Speter apr_pool_t *pool) 452251881Speter{ 453251881Speter char buf[256]; /* Must be smaller than sizeof(conn->read_buf) - 1. */ 454251881Speter const char *p, *end; 455251881Speter apr_size_t len; 456251881Speter svn_boolean_t lparen = FALSE; 457251881Speter 458251881Speter SVN_ERR_ASSERT(conn->read_ptr == conn->read_end); 459251881Speter while (1) 460251881Speter { 461251881Speter /* Read some data directly from the connection input source. */ 462251881Speter len = sizeof(buf); 463251881Speter SVN_ERR(readbuf_input(conn, buf, &len, pool)); 464251881Speter end = buf + len; 465251881Speter 466251881Speter /* Scan the data for '(' WS with a very simple state machine. */ 467251881Speter for (p = buf; p < end; p++) 468251881Speter { 469251881Speter if (lparen && svn_iswhitespace(*p)) 470251881Speter break; 471251881Speter else 472251881Speter lparen = (*p == '('); 473251881Speter } 474251881Speter if (p < end) 475251881Speter break; 476251881Speter } 477251881Speter 478251881Speter /* p now points to the whitespace just after the left paren. Fake 479251881Speter * up the left paren and then copy what we have into the read 480251881Speter * buffer. */ 481251881Speter conn->read_buf[0] = '('; 482251881Speter memcpy(conn->read_buf + 1, p, end - p); 483251881Speter conn->read_ptr = conn->read_buf; 484251881Speter conn->read_end = conn->read_buf + 1 + (end - p); 485251881Speter return SVN_NO_ERROR; 486251881Speter} 487251881Speter 488251881Speter/* --- WRITING DATA ITEMS --- */ 489251881Speter 490251881Speterstatic svn_error_t *write_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 491251881Speter apr_uint64_t number, char follow) 492251881Speter{ 493251881Speter apr_size_t written; 494251881Speter 495251881Speter /* SVN_INT64_BUFFER_SIZE includes space for a terminating NUL that 496251881Speter * svn__ui64toa will always append. */ 497251881Speter if (conn->write_pos + SVN_INT64_BUFFER_SIZE >= sizeof(conn->write_buf)) 498251881Speter SVN_ERR(writebuf_flush(conn, pool)); 499251881Speter 500251881Speter written = svn__ui64toa(conn->write_buf + conn->write_pos, number); 501251881Speter conn->write_buf[conn->write_pos + written] = follow; 502251881Speter conn->write_pos += written + 1; 503251881Speter 504251881Speter return SVN_NO_ERROR; 505251881Speter} 506251881Speter 507251881Spetersvn_error_t * 508251881Spetersvn_ra_svn__write_number(svn_ra_svn_conn_t *conn, 509251881Speter apr_pool_t *pool, 510251881Speter apr_uint64_t number) 511251881Speter{ 512251881Speter return write_number(conn, pool, number, ' '); 513251881Speter} 514251881Speter 515299742Sdimstatic svn_error_t * 516299742Sdimsvn_ra_svn__write_ncstring(svn_ra_svn_conn_t *conn, 517299742Sdim apr_pool_t *pool, 518299742Sdim const char *s, 519299742Sdim apr_size_t len) 520251881Speter{ 521299742Sdim if (len < 10) 522251881Speter { 523299742Sdim SVN_ERR(writebuf_writechar(conn, pool, (char)(len + '0'))); 524251881Speter SVN_ERR(writebuf_writechar(conn, pool, ':')); 525251881Speter } 526251881Speter else 527299742Sdim SVN_ERR(write_number(conn, pool, len, ':')); 528251881Speter 529299742Sdim SVN_ERR(writebuf_write(conn, pool, s, len)); 530251881Speter SVN_ERR(writebuf_writechar(conn, pool, ' ')); 531299742Sdim 532251881Speter return SVN_NO_ERROR; 533251881Speter} 534251881Speter 535251881Spetersvn_error_t * 536299742Sdimsvn_ra_svn__write_string(svn_ra_svn_conn_t *conn, 537299742Sdim apr_pool_t *pool, 538299742Sdim const svn_string_t *str) 539299742Sdim{ 540299742Sdim SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, str->data, str->len)); 541299742Sdim return SVN_NO_ERROR; 542299742Sdim} 543299742Sdim 544299742Sdimsvn_error_t * 545251881Spetersvn_ra_svn__write_cstring(svn_ra_svn_conn_t *conn, 546251881Speter apr_pool_t *pool, 547251881Speter const char *s) 548251881Speter{ 549299742Sdim SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, s, strlen(s))); 550251881Speter return SVN_NO_ERROR; 551251881Speter} 552251881Speter 553251881Spetersvn_error_t * 554251881Spetersvn_ra_svn__write_word(svn_ra_svn_conn_t *conn, 555251881Speter apr_pool_t *pool, 556251881Speter const char *word) 557251881Speter{ 558299742Sdim SVN_ERR(writebuf_write(conn, pool, word, strlen(word))); 559251881Speter SVN_ERR(writebuf_writechar(conn, pool, ' ')); 560251881Speter 561251881Speter return SVN_NO_ERROR; 562251881Speter} 563251881Speter 564251881Spetersvn_error_t * 565299742Sdimsvn_ra_svn__write_boolean(svn_ra_svn_conn_t *conn, 566299742Sdim apr_pool_t *pool, 567299742Sdim svn_boolean_t value) 568299742Sdim{ 569299742Sdim if (value) 570299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "true ")); 571299742Sdim else 572299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "false ")); 573299742Sdim 574299742Sdim return SVN_NO_ERROR; 575299742Sdim} 576299742Sdim 577299742Sdimsvn_error_t * 578251881Spetersvn_ra_svn__write_proplist(svn_ra_svn_conn_t *conn, 579251881Speter apr_pool_t *pool, 580251881Speter apr_hash_t *props) 581251881Speter{ 582251881Speter apr_hash_index_t *hi; 583251881Speter const char *propname; 584251881Speter svn_string_t *propval; 585299742Sdim apr_size_t len; 586251881Speter 587299742Sdim /* One might use an iterpool here but that would only be used when the 588299742Sdim send buffer gets flushed and only by the CONN's progress callback. 589299742Sdim That should happen at most once for typical prop lists and even then 590299742Sdim use only a few bytes at best. 591299742Sdim */ 592251881Speter if (props) 593299742Sdim for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi)) 594299742Sdim { 595299742Sdim apr_hash_this(hi, (const void **)&propname, 596299742Sdim (apr_ssize_t *)&len, 597299742Sdim (void **)&propval); 598251881Speter 599299742Sdim SVN_ERR(svn_ra_svn__start_list(conn, pool)); 600299742Sdim SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, propname, len)); 601299742Sdim SVN_ERR(svn_ra_svn__write_string(conn, pool, propval)); 602299742Sdim SVN_ERR(svn_ra_svn__end_list(conn, pool)); 603299742Sdim } 604299742Sdim 605251881Speter return SVN_NO_ERROR; 606251881Speter} 607251881Speter 608251881Spetersvn_error_t * 609251881Spetersvn_ra_svn__start_list(svn_ra_svn_conn_t *conn, 610251881Speter apr_pool_t *pool) 611251881Speter{ 612251881Speter if (conn->write_pos + 2 <= sizeof(conn->write_buf)) 613251881Speter { 614251881Speter conn->write_buf[conn->write_pos] = '('; 615251881Speter conn->write_buf[conn->write_pos+1] = ' '; 616251881Speter conn->write_pos += 2; 617251881Speter return SVN_NO_ERROR; 618251881Speter } 619251881Speter 620251881Speter return writebuf_write(conn, pool, "( ", 2); 621251881Speter} 622251881Speter 623251881Spetersvn_error_t * 624251881Spetersvn_ra_svn__end_list(svn_ra_svn_conn_t *conn, 625251881Speter apr_pool_t *pool) 626251881Speter{ 627251881Speter if (conn->write_pos + 2 <= sizeof(conn->write_buf)) 628251881Speter { 629251881Speter conn->write_buf[conn->write_pos] = ')'; 630251881Speter conn->write_buf[conn->write_pos+1] = ' '; 631251881Speter conn->write_pos += 2; 632251881Speter return SVN_NO_ERROR; 633251881Speter } 634251881Speter 635251881Speter return writebuf_write(conn, pool, ") ", 2); 636251881Speter} 637251881Speter 638251881Spetersvn_error_t * 639251881Spetersvn_ra_svn__flush(svn_ra_svn_conn_t *conn, 640251881Speter apr_pool_t *pool) 641251881Speter{ 642251881Speter SVN_ERR(writebuf_flush(conn, pool)); 643251881Speter conn->may_check_for_error = TRUE; 644251881Speter 645251881Speter return SVN_NO_ERROR; 646251881Speter} 647251881Speter 648251881Speter/* --- WRITING TUPLES --- */ 649251881Speter 650251881Speterstatic svn_error_t * 651251881Spetervwrite_tuple_cstring(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap) 652251881Speter{ 653251881Speter const char *cstr = va_arg(*ap, const char *); 654251881Speter SVN_ERR_ASSERT(cstr); 655251881Speter return svn_ra_svn__write_cstring(conn, pool, cstr); 656251881Speter} 657251881Speter 658251881Speterstatic svn_error_t * 659251881Spetervwrite_tuple_cstring_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap) 660251881Speter{ 661251881Speter const char *cstr = va_arg(*ap, const char *); 662251881Speter return cstr ? svn_ra_svn__write_cstring(conn, pool, cstr) : SVN_NO_ERROR; 663251881Speter} 664251881Speter 665251881Speterstatic svn_error_t * 666251881Spetervwrite_tuple_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap) 667251881Speter{ 668251881Speter const svn_string_t *str = va_arg(*ap, const svn_string_t *); 669251881Speter SVN_ERR_ASSERT(str); 670251881Speter return svn_ra_svn__write_string(conn, pool, str); 671251881Speter} 672251881Speter 673251881Speterstatic svn_error_t * 674251881Spetervwrite_tuple_string_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap) 675251881Speter{ 676251881Speter const svn_string_t *str = va_arg(*ap, const svn_string_t *); 677251881Speter return str ? svn_ra_svn__write_string(conn, pool, str) : SVN_NO_ERROR; 678251881Speter} 679251881Speter 680251881Speterstatic svn_error_t * 681251881Spetervwrite_tuple_word(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap) 682251881Speter{ 683251881Speter const char *cstr = va_arg(*ap, const char *); 684251881Speter SVN_ERR_ASSERT(cstr); 685251881Speter return svn_ra_svn__write_word(conn, pool, cstr); 686251881Speter} 687251881Speter 688251881Speterstatic svn_error_t * 689251881Spetervwrite_tuple_word_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap) 690251881Speter{ 691251881Speter const char *cstr = va_arg(*ap, const char *); 692251881Speter return cstr ? svn_ra_svn__write_word(conn, pool, cstr) : SVN_NO_ERROR; 693251881Speter} 694251881Speter 695251881Speterstatic svn_error_t * 696251881Spetervwrite_tuple_revision(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap) 697251881Speter{ 698251881Speter svn_revnum_t rev = va_arg(*ap, svn_revnum_t); 699251881Speter SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev)); 700251881Speter return svn_ra_svn__write_number(conn, pool, rev); 701251881Speter} 702251881Speter 703251881Speterstatic svn_error_t * 704251881Spetervwrite_tuple_revision_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap) 705251881Speter{ 706251881Speter svn_revnum_t rev = va_arg(*ap, svn_revnum_t); 707251881Speter return SVN_IS_VALID_REVNUM(rev) 708251881Speter ? svn_ra_svn__write_number(conn, pool, rev) 709251881Speter : SVN_NO_ERROR; 710251881Speter} 711251881Speter 712251881Speterstatic svn_error_t * 713251881Spetervwrite_tuple_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap) 714251881Speter{ 715251881Speter return svn_ra_svn__write_number(conn, pool, va_arg(*ap, apr_uint64_t)); 716251881Speter} 717251881Speter 718251881Speterstatic svn_error_t * 719251881Spetervwrite_tuple_boolean(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap) 720251881Speter{ 721299742Sdim return svn_ra_svn__write_boolean(conn, pool, va_arg(*ap, svn_boolean_t)); 722251881Speter} 723251881Speter 724251881Speterstatic svn_error_t * 725251881Speterwrite_tuple_cstring(svn_ra_svn_conn_t *conn, 726251881Speter apr_pool_t *pool, 727251881Speter const char *cstr) 728251881Speter{ 729251881Speter SVN_ERR_ASSERT(cstr); 730251881Speter return svn_ra_svn__write_cstring(conn, pool, cstr); 731251881Speter} 732251881Speter 733251881Speterstatic svn_error_t * 734251881Speterwrite_tuple_cstring_opt(svn_ra_svn_conn_t *conn, 735251881Speter apr_pool_t *pool, 736251881Speter const char *cstr) 737251881Speter{ 738251881Speter return cstr ? svn_ra_svn__write_cstring(conn, pool, cstr) : SVN_NO_ERROR; 739251881Speter} 740251881Speter 741251881Speterstatic svn_error_t * 742251881Speterwrite_tuple_string(svn_ra_svn_conn_t *conn, 743251881Speter apr_pool_t *pool, 744251881Speter const svn_string_t *str) 745251881Speter{ 746251881Speter SVN_ERR_ASSERT(str); 747251881Speter return svn_ra_svn__write_string(conn, pool, str); 748251881Speter} 749251881Speter 750251881Speterstatic svn_error_t * 751251881Speterwrite_tuple_string_opt(svn_ra_svn_conn_t *conn, 752251881Speter apr_pool_t *pool, 753251881Speter const svn_string_t *str) 754251881Speter{ 755251881Speter return str ? svn_ra_svn__write_string(conn, pool, str) : SVN_NO_ERROR; 756251881Speter} 757251881Speter 758251881Speterstatic svn_error_t * 759251881Speterwrite_tuple_start_list(svn_ra_svn_conn_t *conn, 760251881Speter apr_pool_t *pool) 761251881Speter{ 762251881Speter return svn_ra_svn__start_list(conn, pool); 763251881Speter} 764251881Speter 765251881Speterstatic svn_error_t * 766251881Speterwrite_tuple_end_list(svn_ra_svn_conn_t *conn, 767251881Speter apr_pool_t *pool) 768251881Speter{ 769251881Speter return svn_ra_svn__end_list(conn, pool); 770251881Speter} 771251881Speter 772251881Speterstatic svn_error_t * 773251881Speterwrite_tuple_revision(svn_ra_svn_conn_t *conn, 774251881Speter apr_pool_t *pool, 775251881Speter svn_revnum_t rev) 776251881Speter{ 777251881Speter SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev)); 778251881Speter return svn_ra_svn__write_number(conn, pool, rev); 779251881Speter} 780251881Speter 781251881Speterstatic svn_error_t * 782251881Speterwrite_tuple_revision_opt(svn_ra_svn_conn_t *conn, 783251881Speter apr_pool_t *pool, 784251881Speter svn_revnum_t rev) 785251881Speter{ 786251881Speter return SVN_IS_VALID_REVNUM(rev) 787251881Speter ? svn_ra_svn__write_number(conn, pool, rev) 788251881Speter : SVN_NO_ERROR; 789251881Speter} 790251881Speter 791251881Speterstatic svn_error_t * 792251881Speterwrite_tuple_boolean(svn_ra_svn_conn_t *conn, 793251881Speter apr_pool_t *pool, 794251881Speter svn_boolean_t value) 795251881Speter{ 796299742Sdim return svn_ra_svn__write_boolean(conn, pool, value); 797251881Speter} 798251881Speter 799251881Speterstatic svn_error_t * 800251881Speterwrite_tuple_depth(svn_ra_svn_conn_t *conn, 801251881Speter apr_pool_t *pool, 802251881Speter svn_depth_t depth) 803251881Speter{ 804251881Speter return svn_ra_svn__write_word(conn, pool, svn_depth_to_word(depth)); 805251881Speter} 806251881Speter 807251881Speter 808251881Speterstatic svn_error_t * 809251881Speterwrite_cmd_add_node(svn_ra_svn_conn_t *conn, 810251881Speter apr_pool_t *pool, 811251881Speter const char *path, 812251881Speter const char *parent_token, 813251881Speter const char *token, 814251881Speter const char *copy_path, 815251881Speter svn_revnum_t copy_rev) 816251881Speter{ 817251881Speter SVN_ERR(write_tuple_cstring(conn, pool, path)); 818251881Speter SVN_ERR(write_tuple_cstring(conn, pool, parent_token)); 819251881Speter SVN_ERR(write_tuple_cstring(conn, pool, token)); 820251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 821251881Speter SVN_ERR(write_tuple_cstring_opt(conn, pool, copy_path)); 822251881Speter SVN_ERR(write_tuple_revision_opt(conn, pool, copy_rev)); 823251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 824251881Speter 825251881Speter return SVN_NO_ERROR; 826251881Speter} 827251881Speter 828251881Speterstatic svn_error_t * 829251881Speterwrite_cmd_open_node(svn_ra_svn_conn_t *conn, 830251881Speter apr_pool_t *pool, 831251881Speter const char *path, 832251881Speter const char *parent_token, 833251881Speter const char *token, 834251881Speter svn_revnum_t rev) 835251881Speter{ 836251881Speter SVN_ERR(write_tuple_cstring(conn, pool, path)); 837251881Speter SVN_ERR(write_tuple_cstring(conn, pool, parent_token)); 838251881Speter SVN_ERR(write_tuple_cstring(conn, pool, token)); 839251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 840251881Speter SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); 841251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 842251881Speter 843251881Speter return SVN_NO_ERROR; 844251881Speter} 845251881Speter 846251881Speterstatic svn_error_t * 847251881Speterwrite_cmd_change_node_prop(svn_ra_svn_conn_t *conn, 848251881Speter apr_pool_t *pool, 849251881Speter const char *token, 850251881Speter const char *name, 851251881Speter const svn_string_t *value) 852251881Speter{ 853251881Speter SVN_ERR(write_tuple_cstring(conn, pool, token)); 854251881Speter SVN_ERR(write_tuple_cstring(conn, pool, name)); 855251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 856251881Speter SVN_ERR(write_tuple_string_opt(conn, pool, value)); 857251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 858251881Speter 859251881Speter return SVN_NO_ERROR; 860251881Speter} 861251881Speter 862251881Speterstatic svn_error_t * 863251881Speterwrite_cmd_absent_node(svn_ra_svn_conn_t *conn, 864251881Speter apr_pool_t *pool, 865251881Speter const char *path, 866251881Speter const char *token) 867251881Speter{ 868251881Speter SVN_ERR(write_tuple_cstring(conn, pool, path)); 869251881Speter SVN_ERR(write_tuple_cstring(conn, pool, token)); 870251881Speter 871251881Speter return SVN_NO_ERROR; 872251881Speter} 873251881Speter 874251881Speter 875251881Speter 876251881Speter 877251881Speterstatic svn_error_t *vwrite_tuple(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 878251881Speter const char *fmt, va_list *ap) 879251881Speter{ 880251881Speter svn_boolean_t opt = FALSE; 881251881Speter 882251881Speter if (*fmt == '!') 883251881Speter fmt++; 884251881Speter else 885251881Speter SVN_ERR(svn_ra_svn__start_list(conn, pool)); 886251881Speter for (; *fmt; fmt++) 887251881Speter { 888251881Speter if (*fmt == 'c') 889251881Speter SVN_ERR(opt ? vwrite_tuple_cstring_opt(conn, pool, ap) 890251881Speter : vwrite_tuple_cstring(conn, pool, ap)); 891251881Speter else if (*fmt == 's') 892251881Speter SVN_ERR(opt ? vwrite_tuple_string_opt(conn, pool, ap) 893251881Speter : vwrite_tuple_string(conn, pool, ap)); 894251881Speter else if (*fmt == '(' && !opt) 895251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 896251881Speter else if (*fmt == ')') 897251881Speter { 898251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 899251881Speter opt = FALSE; 900251881Speter } 901251881Speter else if (*fmt == '?') 902251881Speter opt = TRUE; 903251881Speter else if (*fmt == 'w') 904251881Speter SVN_ERR(opt ? vwrite_tuple_word_opt(conn, pool, ap) 905251881Speter : vwrite_tuple_word(conn, pool, ap)); 906251881Speter else if (*fmt == 'r') 907251881Speter SVN_ERR(opt ? vwrite_tuple_revision_opt(conn, pool, ap) 908251881Speter : vwrite_tuple_revision(conn, pool, ap)); 909251881Speter else if (*fmt == 'n' && !opt) 910251881Speter SVN_ERR(vwrite_tuple_number(conn, pool, ap)); 911251881Speter else if (*fmt == 'b' && !opt) 912251881Speter SVN_ERR(vwrite_tuple_boolean(conn, pool, ap)); 913251881Speter else if (*fmt == '!' && !*(fmt + 1)) 914251881Speter return SVN_NO_ERROR; 915251881Speter else 916251881Speter SVN_ERR_MALFUNCTION(); 917251881Speter } 918251881Speter SVN_ERR(svn_ra_svn__end_list(conn, pool)); 919251881Speter return SVN_NO_ERROR; 920251881Speter} 921251881Speter 922251881Spetersvn_error_t * 923251881Spetersvn_ra_svn__write_tuple(svn_ra_svn_conn_t *conn, 924251881Speter apr_pool_t *pool, 925251881Speter const char *fmt, ...) 926251881Speter{ 927251881Speter svn_error_t *err; 928251881Speter va_list ap; 929251881Speter 930251881Speter va_start(ap, fmt); 931251881Speter err = vwrite_tuple(conn, pool, fmt, &ap); 932251881Speter va_end(ap); 933251881Speter return err; 934251881Speter} 935251881Speter 936251881Speter/* --- READING DATA ITEMS --- */ 937251881Speter 938251881Speter/* Read LEN bytes from CONN into already-allocated structure ITEM. 939251881Speter * Afterwards, *ITEM is of type 'SVN_RA_SVN_STRING', and its string 940251881Speter * data is allocated in POOL. */ 941251881Speterstatic svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 942251881Speter svn_ra_svn_item_t *item, apr_uint64_t len64) 943251881Speter{ 944251881Speter apr_size_t len = (apr_size_t)len64; 945251881Speter apr_size_t readbuf_len; 946251881Speter char *dest; 947299742Sdim apr_size_t buflen; 948251881Speter 949251881Speter /* We can't store strings longer than the maximum size of apr_size_t, 950251881Speter * so check for wrapping */ 951251881Speter if (len64 > APR_SIZE_MAX) 952251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 953251881Speter _("String length larger than maximum")); 954251881Speter 955299742Sdim buflen = conn->read_end - conn->read_ptr; 956299742Sdim /* Shorter strings can be copied directly from the read buffer. */ 957299742Sdim if (len <= buflen) 958251881Speter { 959299742Sdim item->kind = SVN_RA_SVN_STRING; 960299742Sdim item->u.string = svn_string_ncreate(conn->read_ptr, len, pool); 961299742Sdim conn->read_ptr += len; 962299742Sdim } 963299742Sdim else 964299742Sdim { 965299742Sdim /* Read the string in chunks. The chunk size is large enough to avoid 966299742Sdim * re-allocation in typical cases, and small enough to ensure we do 967299742Sdim * not pre-allocate an unreasonable amount of memory if (perhaps due 968299742Sdim * to network data corruption or a DOS attack), we receive a bogus 969299742Sdim * claim that a very long string is going to follow. In that case, we 970299742Sdim * start small and wait for all that data to actually show up. This 971299742Sdim * does not fully prevent DOS attacks but makes them harder (you have 972299742Sdim * to actually send gigabytes of data). */ 973299742Sdim svn_stringbuf_t *stringbuf = svn_stringbuf_create_empty(pool); 974251881Speter 975299742Sdim /* Read string data directly into the string structure. 976299742Sdim * Do it iteratively. */ 977299742Sdim do 978299742Sdim { 979299742Sdim /* Determine length of chunk to read and re-alloc the buffer. */ 980299742Sdim readbuf_len 981299742Sdim = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD 982299742Sdim ? len 983299742Sdim : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD; 984251881Speter 985299742Sdim svn_stringbuf_ensure(stringbuf, stringbuf->len + readbuf_len); 986299742Sdim dest = stringbuf->data + stringbuf->len; 987251881Speter 988299742Sdim /* read data & update length info */ 989299742Sdim SVN_ERR(readbuf_read(conn, pool, dest, readbuf_len)); 990251881Speter 991299742Sdim stringbuf->len += readbuf_len; 992299742Sdim len -= readbuf_len; 993299742Sdim } 994299742Sdim while (len); 995251881Speter 996299742Sdim /* zero-terminate the string */ 997299742Sdim stringbuf->data[stringbuf->len] = '\0'; 998251881Speter 999299742Sdim /* Return the string properly wrapped into an RA_SVN item. */ 1000299742Sdim item->kind = SVN_RA_SVN_STRING; 1001299742Sdim item->u.string = svn_stringbuf__morph_into_string(stringbuf); 1002299742Sdim } 1003251881Speter 1004251881Speter return SVN_NO_ERROR; 1005251881Speter} 1006251881Speter 1007251881Speter/* Given the first non-whitespace character FIRST_CHAR, read an item 1008251881Speter * into the already allocated structure ITEM. LEVEL should be set 1009299742Sdim * to 0 for the first call and is used to enforce a recursion limit 1010251881Speter * on the parser. */ 1011251881Speterstatic svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1012251881Speter svn_ra_svn_item_t *item, char first_char, 1013251881Speter int level) 1014251881Speter{ 1015251881Speter char c = first_char; 1016251881Speter apr_uint64_t val; 1017251881Speter svn_ra_svn_item_t *listitem; 1018251881Speter 1019299742Sdim if (++level >= ITEM_NESTING_LIMIT) 1020251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1021299742Sdim _("Items are nested too deeply")); 1022251881Speter 1023251881Speter 1024251881Speter /* Determine the item type and read it in. Make sure that c is the 1025251881Speter * first character at the end of the item so we can test to make 1026251881Speter * sure it's whitespace. */ 1027251881Speter if (svn_ctype_isdigit(c)) 1028251881Speter { 1029251881Speter /* It's a number or a string. Read the number part, either way. */ 1030251881Speter val = c - '0'; 1031251881Speter while (1) 1032251881Speter { 1033251881Speter apr_uint64_t prev_val = val; 1034251881Speter SVN_ERR(readbuf_getchar(conn, pool, &c)); 1035251881Speter if (!svn_ctype_isdigit(c)) 1036251881Speter break; 1037251881Speter val = val * 10 + (c - '0'); 1038251881Speter /* val wrapped past maximum value? */ 1039299742Sdim if ((prev_val >= (APR_UINT64_MAX / 10)) 1040299742Sdim && (val < APR_UINT64_MAX - 10)) 1041251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1042251881Speter _("Number is larger than maximum")); 1043251881Speter } 1044251881Speter if (c == ':') 1045251881Speter { 1046251881Speter /* It's a string. */ 1047251881Speter SVN_ERR(read_string(conn, pool, item, val)); 1048251881Speter SVN_ERR(readbuf_getchar(conn, pool, &c)); 1049251881Speter } 1050251881Speter else 1051251881Speter { 1052251881Speter /* It's a number. */ 1053251881Speter item->kind = SVN_RA_SVN_NUMBER; 1054251881Speter item->u.number = val; 1055251881Speter } 1056251881Speter } 1057251881Speter else if (svn_ctype_isalpha(c)) 1058251881Speter { 1059299742Sdim /* It's a word. Read it into a buffer of limited size. */ 1060299742Sdim char *buffer = apr_palloc(pool, MAX_WORD_LENGTH + 1); 1061299742Sdim char *end = buffer + MAX_WORD_LENGTH; 1062299742Sdim char *p = buffer + 1; 1063299742Sdim 1064299742Sdim buffer[0] = c; 1065251881Speter while (1) 1066251881Speter { 1067299742Sdim SVN_ERR(readbuf_getchar(conn, pool, p)); 1068299742Sdim if (!svn_ctype_isalnum(*p) && *p != '-') 1069251881Speter break; 1070299742Sdim 1071299742Sdim if (++p == end) 1072299742Sdim return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1073299742Sdim _("Word is too long")); 1074251881Speter } 1075299742Sdim 1076299742Sdim c = *p; 1077299742Sdim *p = '\0'; 1078299742Sdim 1079251881Speter item->kind = SVN_RA_SVN_WORD; 1080299742Sdim item->u.word = buffer; 1081251881Speter } 1082251881Speter else if (c == '(') 1083251881Speter { 1084251881Speter /* Read in the list items. */ 1085251881Speter item->kind = SVN_RA_SVN_LIST; 1086251881Speter item->u.list = apr_array_make(pool, 4, sizeof(svn_ra_svn_item_t)); 1087251881Speter while (1) 1088251881Speter { 1089251881Speter SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c)); 1090251881Speter if (c == ')') 1091251881Speter break; 1092251881Speter listitem = apr_array_push(item->u.list); 1093251881Speter SVN_ERR(read_item(conn, pool, listitem, c, level)); 1094251881Speter } 1095251881Speter SVN_ERR(readbuf_getchar(conn, pool, &c)); 1096251881Speter } 1097251881Speter 1098251881Speter if (!svn_iswhitespace(c)) 1099251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1100251881Speter _("Malformed network data")); 1101251881Speter return SVN_NO_ERROR; 1102251881Speter} 1103251881Speter 1104251881Speter/* Given the first non-whitespace character FIRST_CHAR, read the first 1105251881Speter * command (word) encountered in CONN into *ITEM. If ITEM is NULL, skip 1106251881Speter * to the end of the current list. Use POOL for allocations. */ 1107251881Speterstatic svn_error_t * 1108251881Speterread_command_only(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1109251881Speter const char **item, char first_char) 1110251881Speter{ 1111251881Speter char c = first_char; 1112251881Speter 1113251881Speter /* Determine the item type and read it in. Make sure that c is the 1114251881Speter * first character at the end of the item so we can test to make 1115251881Speter * sure it's whitespace. */ 1116251881Speter if (svn_ctype_isdigit(c)) 1117251881Speter { 1118251881Speter /* It's a number or a string. Read the number part, either way. */ 1119251881Speter apr_uint64_t val, prev_val=0; 1120251881Speter val = c - '0'; 1121251881Speter while (1) 1122251881Speter { 1123251881Speter prev_val = val; 1124251881Speter SVN_ERR(readbuf_getchar(conn, pool, &c)); 1125251881Speter if (!svn_ctype_isdigit(c)) 1126251881Speter break; 1127251881Speter val = val * 10 + (c - '0'); 1128251881Speter if (prev_val >= (APR_UINT64_MAX / 10)) /* > maximum value? */ 1129251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1130251881Speter _("Number is larger than maximum")); 1131251881Speter } 1132251881Speter if (c == ':') 1133251881Speter { 1134251881Speter /* It's a string. */ 1135251881Speter SVN_ERR(readbuf_skip(conn, val)); 1136251881Speter SVN_ERR(readbuf_getchar(conn, pool, &c)); 1137251881Speter } 1138251881Speter } 1139251881Speter else if (svn_ctype_isalpha(c)) 1140251881Speter { 1141251881Speter /* It's a word. */ 1142251881Speter if (item) 1143251881Speter { 1144251881Speter /* This is the word we want to read */ 1145251881Speter 1146251881Speter char *buf = apr_palloc(pool, 32); 1147251881Speter apr_size_t len = 1; 1148251881Speter buf[0] = c; 1149251881Speter 1150251881Speter while (1) 1151251881Speter { 1152251881Speter SVN_ERR(readbuf_getchar(conn, pool, &c)); 1153251881Speter if (!svn_ctype_isalnum(c) && c != '-') 1154251881Speter break; 1155251881Speter buf[len] = c; 1156251881Speter if (++len == 32) 1157251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1158251881Speter _("Word too long")); 1159251881Speter } 1160251881Speter buf[len] = 0; 1161251881Speter *item = buf; 1162251881Speter } 1163251881Speter else 1164251881Speter { 1165251881Speter /* we don't need the actual word, just skip it */ 1166251881Speter do 1167251881Speter { 1168251881Speter SVN_ERR(readbuf_getchar(conn, pool, &c)); 1169251881Speter } 1170251881Speter while (svn_ctype_isalnum(c) || c == '-'); 1171251881Speter } 1172251881Speter } 1173251881Speter else if (c == '(') 1174251881Speter { 1175251881Speter /* Read in the list items. */ 1176251881Speter while (1) 1177251881Speter { 1178251881Speter SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c)); 1179251881Speter if (c == ')') 1180251881Speter break; 1181251881Speter 1182251881Speter if (item && *item == NULL) 1183251881Speter SVN_ERR(read_command_only(conn, pool, item, c)); 1184251881Speter else 1185251881Speter SVN_ERR(read_command_only(conn, pool, NULL, c)); 1186251881Speter } 1187251881Speter SVN_ERR(readbuf_getchar(conn, pool, &c)); 1188251881Speter } 1189251881Speter 1190251881Speter return SVN_NO_ERROR; 1191251881Speter} 1192251881Speter 1193251881Spetersvn_error_t * 1194251881Spetersvn_ra_svn__read_item(svn_ra_svn_conn_t *conn, 1195251881Speter apr_pool_t *pool, 1196251881Speter svn_ra_svn_item_t **item) 1197251881Speter{ 1198251881Speter char c; 1199251881Speter 1200251881Speter /* Allocate space, read the first character, and then do the rest of 1201251881Speter * the work. This makes sense because of the way lists are read. */ 1202251881Speter *item = apr_palloc(pool, sizeof(**item)); 1203251881Speter SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c)); 1204251881Speter return read_item(conn, pool, *item, c, 0); 1205251881Speter} 1206251881Speter 1207299742Sdim/* Drain existing whitespace from the receive buffer of CONN until either 1208299742Sdim there is no data in the underlying receive socket anymore or we found 1209299742Sdim a non-whitespace char. Set *HAS_ITEM to TRUE in the latter case. 1210299742Sdim */ 1211299742Sdimstatic svn_error_t * 1212299742Sdimsvn_ra_svn__has_item(svn_boolean_t *has_item, 1213299742Sdim svn_ra_svn_conn_t *conn, 1214299742Sdim apr_pool_t *pool) 1215299742Sdim{ 1216299742Sdim do 1217299742Sdim { 1218299742Sdim if (conn->read_ptr == conn->read_end) 1219299742Sdim { 1220299742Sdim svn_boolean_t available; 1221299742Sdim if (conn->write_pos) 1222299742Sdim SVN_ERR(writebuf_flush(conn, pool)); 1223299742Sdim 1224299742Sdim SVN_ERR(svn_ra_svn__data_available(conn, &available)); 1225299742Sdim if (!available) 1226299742Sdim break; 1227299742Sdim 1228299742Sdim SVN_ERR(readbuf_fill(conn, pool)); 1229299742Sdim } 1230299742Sdim } 1231299742Sdim while (svn_iswhitespace(*conn->read_ptr) && ++conn->read_ptr); 1232299742Sdim 1233299742Sdim *has_item = conn->read_ptr != conn->read_end; 1234299742Sdim return SVN_NO_ERROR; 1235299742Sdim} 1236299742Sdim 1237251881Spetersvn_error_t * 1238251881Spetersvn_ra_svn__skip_leading_garbage(svn_ra_svn_conn_t *conn, 1239251881Speter apr_pool_t *pool) 1240251881Speter{ 1241251881Speter return readbuf_skip_leading_garbage(conn, pool); 1242251881Speter} 1243251881Speter 1244251881Speter/* --- READING AND PARSING TUPLES --- */ 1245251881Speter 1246251881Speter/* Parse a tuple of svn_ra_svn_item_t *'s. Advance *FMT to the end of the 1247251881Speter * tuple specification and advance AP by the corresponding arguments. */ 1248251881Speterstatic svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *pool, 1249251881Speter const char **fmt, va_list *ap) 1250251881Speter{ 1251251881Speter int count, nesting_level; 1252251881Speter svn_ra_svn_item_t *elt; 1253251881Speter 1254251881Speter for (count = 0; **fmt && count < items->nelts; (*fmt)++, count++) 1255251881Speter { 1256251881Speter /* '?' just means the tuple may stop; skip past it. */ 1257251881Speter if (**fmt == '?') 1258251881Speter (*fmt)++; 1259251881Speter elt = &APR_ARRAY_IDX(items, count, svn_ra_svn_item_t); 1260299742Sdim if (**fmt == '(' && elt->kind == SVN_RA_SVN_LIST) 1261299742Sdim { 1262299742Sdim (*fmt)++; 1263299742Sdim SVN_ERR(vparse_tuple(elt->u.list, pool, fmt, ap)); 1264299742Sdim } 1265299742Sdim else if (**fmt == 'c' && elt->kind == SVN_RA_SVN_STRING) 1266299742Sdim *va_arg(*ap, const char **) = elt->u.string->data; 1267251881Speter else if (**fmt == 's' && elt->kind == SVN_RA_SVN_STRING) 1268251881Speter *va_arg(*ap, svn_string_t **) = elt->u.string; 1269251881Speter else if (**fmt == 'w' && elt->kind == SVN_RA_SVN_WORD) 1270251881Speter *va_arg(*ap, const char **) = elt->u.word; 1271251881Speter else if (**fmt == 'b' && elt->kind == SVN_RA_SVN_WORD) 1272251881Speter { 1273251881Speter if (strcmp(elt->u.word, "true") == 0) 1274251881Speter *va_arg(*ap, svn_boolean_t *) = TRUE; 1275251881Speter else if (strcmp(elt->u.word, "false") == 0) 1276251881Speter *va_arg(*ap, svn_boolean_t *) = FALSE; 1277251881Speter else 1278251881Speter break; 1279251881Speter } 1280299742Sdim else if (**fmt == 'n' && elt->kind == SVN_RA_SVN_NUMBER) 1281299742Sdim *va_arg(*ap, apr_uint64_t *) = elt->u.number; 1282299742Sdim else if (**fmt == 'r' && elt->kind == SVN_RA_SVN_NUMBER) 1283299742Sdim *va_arg(*ap, svn_revnum_t *) = (svn_revnum_t) elt->u.number; 1284251881Speter else if (**fmt == 'B' && elt->kind == SVN_RA_SVN_WORD) 1285251881Speter { 1286251881Speter if (strcmp(elt->u.word, "true") == 0) 1287251881Speter *va_arg(*ap, apr_uint64_t *) = TRUE; 1288251881Speter else if (strcmp(elt->u.word, "false") == 0) 1289251881Speter *va_arg(*ap, apr_uint64_t *) = FALSE; 1290251881Speter else 1291251881Speter break; 1292251881Speter } 1293299742Sdim else if (**fmt == '3' && elt->kind == SVN_RA_SVN_WORD) 1294299742Sdim { 1295299742Sdim if (strcmp(elt->u.word, "true") == 0) 1296299742Sdim *va_arg(*ap, svn_tristate_t *) = svn_tristate_true; 1297299742Sdim else if (strcmp(elt->u.word, "false") == 0) 1298299742Sdim *va_arg(*ap, svn_tristate_t *) = svn_tristate_false; 1299299742Sdim else 1300299742Sdim break; 1301299742Sdim } 1302251881Speter else if (**fmt == 'l' && elt->kind == SVN_RA_SVN_LIST) 1303251881Speter *va_arg(*ap, apr_array_header_t **) = elt->u.list; 1304251881Speter else if (**fmt == ')') 1305251881Speter return SVN_NO_ERROR; 1306251881Speter else 1307251881Speter break; 1308251881Speter } 1309251881Speter if (**fmt == '?') 1310251881Speter { 1311251881Speter nesting_level = 0; 1312251881Speter for (; **fmt; (*fmt)++) 1313251881Speter { 1314251881Speter switch (**fmt) 1315251881Speter { 1316251881Speter case '?': 1317251881Speter break; 1318251881Speter case 'r': 1319251881Speter *va_arg(*ap, svn_revnum_t *) = SVN_INVALID_REVNUM; 1320251881Speter break; 1321251881Speter case 's': 1322251881Speter *va_arg(*ap, svn_string_t **) = NULL; 1323251881Speter break; 1324251881Speter case 'c': 1325251881Speter case 'w': 1326251881Speter *va_arg(*ap, const char **) = NULL; 1327251881Speter break; 1328251881Speter case 'l': 1329251881Speter *va_arg(*ap, apr_array_header_t **) = NULL; 1330251881Speter break; 1331251881Speter case 'B': 1332251881Speter case 'n': 1333251881Speter *va_arg(*ap, apr_uint64_t *) = SVN_RA_SVN_UNSPECIFIED_NUMBER; 1334251881Speter break; 1335299742Sdim case '3': 1336299742Sdim *va_arg(*ap, svn_tristate_t *) = svn_tristate_unknown; 1337299742Sdim break; 1338251881Speter case '(': 1339251881Speter nesting_level++; 1340251881Speter break; 1341251881Speter case ')': 1342251881Speter if (--nesting_level < 0) 1343251881Speter return SVN_NO_ERROR; 1344251881Speter break; 1345251881Speter default: 1346251881Speter SVN_ERR_MALFUNCTION(); 1347251881Speter } 1348251881Speter } 1349251881Speter } 1350251881Speter if (**fmt && **fmt != ')') 1351251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1352251881Speter _("Malformed network data")); 1353251881Speter return SVN_NO_ERROR; 1354251881Speter} 1355251881Speter 1356251881Spetersvn_error_t * 1357251881Spetersvn_ra_svn__parse_tuple(const apr_array_header_t *list, 1358251881Speter apr_pool_t *pool, 1359251881Speter const char *fmt, ...) 1360251881Speter{ 1361251881Speter svn_error_t *err; 1362251881Speter va_list ap; 1363251881Speter 1364251881Speter va_start(ap, fmt); 1365251881Speter err = vparse_tuple(list, pool, &fmt, &ap); 1366251881Speter va_end(ap); 1367251881Speter return err; 1368251881Speter} 1369251881Speter 1370251881Spetersvn_error_t * 1371251881Spetersvn_ra_svn__read_tuple(svn_ra_svn_conn_t *conn, 1372251881Speter apr_pool_t *pool, 1373251881Speter const char *fmt, ...) 1374251881Speter{ 1375251881Speter va_list ap; 1376251881Speter svn_ra_svn_item_t *item; 1377251881Speter svn_error_t *err; 1378251881Speter 1379251881Speter SVN_ERR(svn_ra_svn__read_item(conn, pool, &item)); 1380251881Speter if (item->kind != SVN_RA_SVN_LIST) 1381251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1382251881Speter _("Malformed network data")); 1383251881Speter va_start(ap, fmt); 1384251881Speter err = vparse_tuple(item->u.list, pool, &fmt, &ap); 1385251881Speter va_end(ap); 1386251881Speter return err; 1387251881Speter} 1388251881Speter 1389251881Spetersvn_error_t * 1390251881Spetersvn_ra_svn__read_command_only(svn_ra_svn_conn_t *conn, 1391251881Speter apr_pool_t *pool, 1392251881Speter const char **command) 1393251881Speter{ 1394251881Speter char c; 1395251881Speter SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c)); 1396251881Speter 1397251881Speter *command = NULL; 1398251881Speter return read_command_only(conn, pool, command, c); 1399251881Speter} 1400251881Speter 1401251881Speter 1402251881Spetersvn_error_t * 1403251881Spetersvn_ra_svn__parse_proplist(const apr_array_header_t *list, 1404251881Speter apr_pool_t *pool, 1405251881Speter apr_hash_t **props) 1406251881Speter{ 1407299742Sdim svn_string_t *name; 1408251881Speter svn_string_t *value; 1409251881Speter svn_ra_svn_item_t *elt; 1410251881Speter int i; 1411251881Speter 1412299742Sdim *props = svn_hash__make(pool); 1413251881Speter for (i = 0; i < list->nelts; i++) 1414251881Speter { 1415251881Speter elt = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t); 1416251881Speter if (elt->kind != SVN_RA_SVN_LIST) 1417251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1418251881Speter _("Proplist element not a list")); 1419299742Sdim SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, pool, "ss", 1420251881Speter &name, &value)); 1421299742Sdim apr_hash_set(*props, name->data, name->len, value); 1422251881Speter } 1423251881Speter 1424251881Speter return SVN_NO_ERROR; 1425251881Speter} 1426251881Speter 1427251881Speter 1428251881Speter/* --- READING AND WRITING COMMANDS AND RESPONSES --- */ 1429251881Speter 1430251881Spetersvn_error_t *svn_ra_svn__locate_real_error_child(svn_error_t *err) 1431251881Speter{ 1432251881Speter svn_error_t *this_link; 1433251881Speter 1434251881Speter SVN_ERR_ASSERT(err); 1435251881Speter 1436251881Speter for (this_link = err; 1437251881Speter this_link && (this_link->apr_err == SVN_ERR_RA_SVN_CMD_ERR); 1438251881Speter this_link = this_link->child) 1439251881Speter ; 1440251881Speter 1441251881Speter SVN_ERR_ASSERT(this_link); 1442251881Speter return this_link; 1443251881Speter} 1444251881Speter 1445251881Spetersvn_error_t *svn_ra_svn__handle_failure_status(const apr_array_header_t *params, 1446251881Speter apr_pool_t *pool) 1447251881Speter{ 1448251881Speter const char *message, *file; 1449251881Speter svn_error_t *err = NULL; 1450251881Speter svn_ra_svn_item_t *elt; 1451251881Speter int i; 1452251881Speter apr_uint64_t apr_err, line; 1453251881Speter apr_pool_t *subpool = svn_pool_create(pool); 1454251881Speter 1455251881Speter if (params->nelts == 0) 1456251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1457251881Speter _("Empty error list")); 1458251881Speter 1459251881Speter /* Rebuild the error list from the end, to avoid reversing the order. */ 1460251881Speter for (i = params->nelts - 1; i >= 0; i--) 1461251881Speter { 1462251881Speter svn_pool_clear(subpool); 1463251881Speter elt = &APR_ARRAY_IDX(params, i, svn_ra_svn_item_t); 1464251881Speter if (elt->kind != SVN_RA_SVN_LIST) 1465251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1466251881Speter _("Malformed error list")); 1467251881Speter SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, subpool, "nccn", 1468251881Speter &apr_err, &message, &file, &line)); 1469251881Speter /* The message field should have been optional, but we can't 1470251881Speter easily change that, so "" means a nonexistent message. */ 1471251881Speter if (!*message) 1472251881Speter message = NULL; 1473251881Speter 1474251881Speter /* Skip over links in the error chain that were intended only to 1475251881Speter exist on the server (to wrap real errors intended for the 1476251881Speter client) but accidentally got included in the server's actual 1477251881Speter response. */ 1478251881Speter if ((apr_status_t)apr_err != SVN_ERR_RA_SVN_CMD_ERR) 1479251881Speter { 1480251881Speter err = svn_error_create((apr_status_t)apr_err, err, message); 1481251881Speter err->file = apr_pstrdup(err->pool, file); 1482251881Speter err->line = (long)line; 1483251881Speter } 1484251881Speter } 1485251881Speter 1486251881Speter svn_pool_destroy(subpool); 1487251881Speter 1488251881Speter /* If we get here, then we failed to find a real error in the error 1489251881Speter chain that the server proported to be sending us. That's bad. */ 1490251881Speter if (! err) 1491251881Speter err = svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1492251881Speter _("Malformed error list")); 1493251881Speter 1494251881Speter return err; 1495251881Speter} 1496251881Speter 1497251881Spetersvn_error_t * 1498251881Spetersvn_ra_svn__read_cmd_response(svn_ra_svn_conn_t *conn, 1499251881Speter apr_pool_t *pool, 1500251881Speter const char *fmt, ...) 1501251881Speter{ 1502251881Speter va_list ap; 1503251881Speter const char *status; 1504251881Speter apr_array_header_t *params; 1505251881Speter svn_error_t *err; 1506251881Speter 1507251881Speter SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "wl", &status, ¶ms)); 1508251881Speter if (strcmp(status, "success") == 0) 1509251881Speter { 1510251881Speter va_start(ap, fmt); 1511251881Speter err = vparse_tuple(params, pool, &fmt, &ap); 1512251881Speter va_end(ap); 1513251881Speter return err; 1514251881Speter } 1515251881Speter else if (strcmp(status, "failure") == 0) 1516251881Speter { 1517299742Sdim return svn_error_trace(svn_ra_svn__handle_failure_status(params, pool)); 1518251881Speter } 1519251881Speter 1520251881Speter return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1521251881Speter _("Unknown status '%s' in command response"), 1522251881Speter status); 1523251881Speter} 1524251881Speter 1525251881Spetersvn_error_t * 1526299742Sdimsvn_ra_svn__has_command(svn_boolean_t *has_command, 1527299742Sdim svn_boolean_t *terminated, 1528299742Sdim svn_ra_svn_conn_t *conn, 1529299742Sdim apr_pool_t *pool) 1530299742Sdim{ 1531299742Sdim svn_error_t *err = svn_ra_svn__has_item(has_command, conn, pool); 1532299742Sdim if (err && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED) 1533299742Sdim { 1534299742Sdim *terminated = TRUE; 1535299742Sdim svn_error_clear(err); 1536299742Sdim return SVN_NO_ERROR; 1537299742Sdim } 1538299742Sdim 1539299742Sdim *terminated = FALSE; 1540299742Sdim return svn_error_trace(err); 1541299742Sdim} 1542299742Sdim 1543299742Sdimsvn_error_t * 1544299742Sdimsvn_ra_svn__handle_command(svn_boolean_t *terminate, 1545299742Sdim apr_hash_t *cmd_hash, 1546299742Sdim void *baton, 1547299742Sdim svn_ra_svn_conn_t *conn, 1548299742Sdim svn_boolean_t error_on_disconnect, 1549299742Sdim apr_pool_t *pool) 1550299742Sdim{ 1551299742Sdim const char *cmdname; 1552299742Sdim svn_error_t *err, *write_err; 1553299742Sdim apr_array_header_t *params; 1554299742Sdim const svn_ra_svn_cmd_entry_t *command; 1555299742Sdim 1556299742Sdim *terminate = FALSE; 1557299742Sdim err = svn_ra_svn__read_tuple(conn, pool, "wl", &cmdname, ¶ms); 1558299742Sdim if (err) 1559299742Sdim { 1560299742Sdim if (!error_on_disconnect 1561299742Sdim && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED) 1562299742Sdim { 1563299742Sdim svn_error_clear(err); 1564299742Sdim *terminate = TRUE; 1565299742Sdim return SVN_NO_ERROR; 1566299742Sdim } 1567299742Sdim return err; 1568299742Sdim } 1569299742Sdim 1570299742Sdim command = svn_hash_gets(cmd_hash, cmdname); 1571299742Sdim if (command) 1572299742Sdim { 1573299742Sdim err = (*command->handler)(conn, pool, params, baton); 1574299742Sdim *terminate = command->terminate; 1575299742Sdim } 1576299742Sdim else 1577299742Sdim { 1578299742Sdim err = svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL, 1579299742Sdim _("Unknown editor command '%s'"), cmdname); 1580299742Sdim err = svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, err, NULL); 1581299742Sdim } 1582299742Sdim 1583299742Sdim if (err && err->apr_err == SVN_ERR_RA_SVN_CMD_ERR) 1584299742Sdim { 1585299742Sdim write_err = svn_ra_svn__write_cmd_failure( 1586299742Sdim conn, pool, 1587299742Sdim svn_ra_svn__locate_real_error_child(err)); 1588299742Sdim svn_error_clear(err); 1589299742Sdim return write_err ? write_err : SVN_NO_ERROR; 1590299742Sdim } 1591299742Sdim 1592299742Sdim return err; 1593299742Sdim} 1594299742Sdim 1595299742Sdimsvn_error_t * 1596251881Spetersvn_ra_svn__handle_commands2(svn_ra_svn_conn_t *conn, 1597251881Speter apr_pool_t *pool, 1598251881Speter const svn_ra_svn_cmd_entry_t *commands, 1599251881Speter void *baton, 1600251881Speter svn_boolean_t error_on_disconnect) 1601251881Speter{ 1602251881Speter apr_pool_t *subpool = svn_pool_create(pool); 1603251881Speter apr_pool_t *iterpool = svn_pool_create(subpool); 1604251881Speter const svn_ra_svn_cmd_entry_t *command; 1605251881Speter apr_hash_t *cmd_hash = apr_hash_make(subpool); 1606251881Speter 1607251881Speter for (command = commands; command->cmdname; command++) 1608251881Speter svn_hash_sets(cmd_hash, command->cmdname, command); 1609251881Speter 1610251881Speter while (1) 1611251881Speter { 1612299742Sdim svn_boolean_t terminate; 1613299742Sdim svn_error_t *err; 1614251881Speter svn_pool_clear(iterpool); 1615299742Sdim 1616299742Sdim err = svn_ra_svn__handle_command(&terminate, cmd_hash, baton, conn, 1617299742Sdim error_on_disconnect, iterpool); 1618251881Speter if (err) 1619251881Speter { 1620299742Sdim svn_pool_destroy(subpool); 1621299742Sdim return svn_error_trace(err); 1622251881Speter } 1623299742Sdim if (terminate) 1624251881Speter break; 1625251881Speter } 1626251881Speter svn_pool_destroy(iterpool); 1627251881Speter svn_pool_destroy(subpool); 1628251881Speter return SVN_NO_ERROR; 1629251881Speter} 1630251881Speter 1631251881Spetersvn_error_t * 1632251881Spetersvn_ra_svn__write_cmd_target_rev(svn_ra_svn_conn_t *conn, 1633251881Speter apr_pool_t *pool, 1634251881Speter svn_revnum_t rev) 1635251881Speter{ 1636299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( target-rev ( ")); 1637251881Speter SVN_ERR(write_tuple_revision(conn, pool, rev)); 1638299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1639251881Speter 1640251881Speter return SVN_NO_ERROR; 1641251881Speter} 1642251881Speter 1643251881Spetersvn_error_t * 1644251881Spetersvn_ra_svn__write_cmd_open_root(svn_ra_svn_conn_t *conn, 1645251881Speter apr_pool_t *pool, 1646251881Speter svn_revnum_t rev, 1647251881Speter const char *token) 1648251881Speter{ 1649299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( open-root ( ")); 1650251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 1651251881Speter SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); 1652251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 1653251881Speter SVN_ERR(write_tuple_cstring(conn, pool, token)); 1654299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1655251881Speter 1656251881Speter return SVN_NO_ERROR; 1657251881Speter} 1658251881Speter 1659251881Spetersvn_error_t * 1660251881Spetersvn_ra_svn__write_cmd_delete_entry(svn_ra_svn_conn_t *conn, 1661251881Speter apr_pool_t *pool, 1662251881Speter const char *path, 1663251881Speter svn_revnum_t rev, 1664251881Speter const char *token) 1665251881Speter{ 1666299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( delete-entry ( ")); 1667251881Speter SVN_ERR(write_tuple_cstring(conn, pool, path)); 1668251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 1669251881Speter SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); 1670251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 1671251881Speter SVN_ERR(write_tuple_cstring(conn, pool, token)); 1672299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1673251881Speter 1674251881Speter return SVN_NO_ERROR; 1675251881Speter} 1676251881Speter 1677251881Spetersvn_error_t * 1678251881Spetersvn_ra_svn__write_cmd_add_dir(svn_ra_svn_conn_t *conn, 1679251881Speter apr_pool_t *pool, 1680251881Speter const char *path, 1681251881Speter const char *parent_token, 1682251881Speter const char *token, 1683251881Speter const char *copy_path, 1684251881Speter svn_revnum_t copy_rev) 1685251881Speter{ 1686299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( add-dir ( ")); 1687251881Speter SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token, 1688251881Speter copy_path, copy_rev)); 1689299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1690251881Speter 1691251881Speter return SVN_NO_ERROR; 1692251881Speter} 1693251881Speter 1694251881Spetersvn_error_t * 1695251881Spetersvn_ra_svn__write_cmd_open_dir(svn_ra_svn_conn_t *conn, 1696251881Speter apr_pool_t *pool, 1697251881Speter const char *path, 1698251881Speter const char *parent_token, 1699251881Speter const char *token, 1700251881Speter svn_revnum_t rev) 1701251881Speter{ 1702299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( open-dir ( ")); 1703251881Speter SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev)); 1704299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1705251881Speter 1706251881Speter return SVN_NO_ERROR; 1707251881Speter} 1708251881Speter 1709251881Spetersvn_error_t * 1710251881Spetersvn_ra_svn__write_cmd_change_dir_prop(svn_ra_svn_conn_t *conn, 1711251881Speter apr_pool_t *pool, 1712251881Speter const char *token, 1713251881Speter const char *name, 1714251881Speter const svn_string_t *value) 1715251881Speter{ 1716299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( change-dir-prop ( ")); 1717251881Speter SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value)); 1718299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1719251881Speter 1720251881Speter return SVN_NO_ERROR; 1721251881Speter} 1722251881Speter 1723251881Spetersvn_error_t * 1724251881Spetersvn_ra_svn__write_cmd_close_dir(svn_ra_svn_conn_t *conn, 1725251881Speter apr_pool_t *pool, 1726251881Speter const char *token) 1727251881Speter{ 1728299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( close-dir ( ")); 1729251881Speter SVN_ERR(write_tuple_cstring(conn, pool, token)); 1730299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1731251881Speter 1732251881Speter return SVN_NO_ERROR; 1733251881Speter} 1734251881Speter 1735251881Spetersvn_error_t * 1736251881Spetersvn_ra_svn__write_cmd_absent_dir(svn_ra_svn_conn_t *conn, 1737251881Speter apr_pool_t *pool, 1738251881Speter const char *path, 1739251881Speter const char *parent_token) 1740251881Speter{ 1741299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( absent-dir ( ")); 1742251881Speter SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token)); 1743299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1744251881Speter 1745251881Speter return SVN_NO_ERROR; 1746251881Speter} 1747251881Speter 1748251881Spetersvn_error_t * 1749251881Spetersvn_ra_svn__write_cmd_add_file(svn_ra_svn_conn_t *conn, 1750251881Speter apr_pool_t *pool, 1751251881Speter const char *path, 1752251881Speter const char *parent_token, 1753251881Speter const char *token, 1754251881Speter const char *copy_path, 1755251881Speter svn_revnum_t copy_rev) 1756251881Speter{ 1757299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( add-file ( ")); 1758251881Speter SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token, 1759251881Speter copy_path, copy_rev)); 1760299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1761251881Speter 1762251881Speter return SVN_NO_ERROR; 1763251881Speter} 1764251881Speter 1765251881Spetersvn_error_t * 1766251881Spetersvn_ra_svn__write_cmd_open_file(svn_ra_svn_conn_t *conn, 1767251881Speter apr_pool_t *pool, 1768251881Speter const char *path, 1769251881Speter const char *parent_token, 1770251881Speter const char *token, 1771251881Speter svn_revnum_t rev) 1772251881Speter{ 1773299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( open-file ( ")); 1774251881Speter SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev)); 1775299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1776251881Speter 1777251881Speter return SVN_NO_ERROR; 1778251881Speter} 1779251881Speter 1780251881Spetersvn_error_t * 1781251881Spetersvn_ra_svn__write_cmd_change_file_prop(svn_ra_svn_conn_t *conn, 1782251881Speter apr_pool_t *pool, 1783251881Speter const char *token, 1784251881Speter const char *name, 1785251881Speter const svn_string_t *value) 1786251881Speter{ 1787299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( change-file-prop ( ")); 1788251881Speter SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value)); 1789299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1790251881Speter 1791251881Speter return SVN_NO_ERROR; 1792251881Speter} 1793251881Speter 1794251881Spetersvn_error_t * 1795251881Spetersvn_ra_svn__write_cmd_close_file(svn_ra_svn_conn_t *conn, 1796251881Speter apr_pool_t *pool, 1797251881Speter const char *token, 1798251881Speter const char *text_checksum) 1799251881Speter{ 1800299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( close-file ( ")); 1801251881Speter SVN_ERR(write_tuple_cstring(conn, pool, token)); 1802251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 1803251881Speter SVN_ERR(write_tuple_cstring_opt(conn, pool, text_checksum)); 1804251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 1805299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1806251881Speter 1807251881Speter return SVN_NO_ERROR; 1808251881Speter} 1809251881Speter 1810251881Spetersvn_error_t * 1811251881Spetersvn_ra_svn__write_cmd_absent_file(svn_ra_svn_conn_t *conn, 1812251881Speter apr_pool_t *pool, 1813251881Speter const char *path, 1814251881Speter const char *parent_token) 1815251881Speter{ 1816299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( absent-file ( ")); 1817251881Speter SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token)); 1818299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1819251881Speter 1820251881Speter return SVN_NO_ERROR; 1821251881Speter} 1822251881Speter 1823251881Spetersvn_error_t * 1824251881Spetersvn_ra_svn__write_cmd_textdelta_chunk(svn_ra_svn_conn_t *conn, 1825251881Speter apr_pool_t *pool, 1826251881Speter const char *token, 1827251881Speter const svn_string_t *chunk) 1828251881Speter{ 1829299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-chunk ( ")); 1830251881Speter SVN_ERR(write_tuple_cstring(conn, pool, token)); 1831251881Speter SVN_ERR(write_tuple_string(conn, pool, chunk)); 1832299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1833251881Speter 1834251881Speter return SVN_NO_ERROR; 1835251881Speter} 1836251881Speter 1837251881Spetersvn_error_t * 1838251881Spetersvn_ra_svn__write_cmd_textdelta_end(svn_ra_svn_conn_t *conn, 1839251881Speter apr_pool_t *pool, 1840251881Speter const char *token) 1841251881Speter{ 1842299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-end ( ")); 1843251881Speter SVN_ERR(write_tuple_cstring(conn, pool, token)); 1844299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1845251881Speter 1846251881Speter return SVN_NO_ERROR; 1847251881Speter} 1848251881Speter 1849251881Spetersvn_error_t * 1850251881Spetersvn_ra_svn__write_cmd_apply_textdelta(svn_ra_svn_conn_t *conn, 1851251881Speter apr_pool_t *pool, 1852251881Speter const char *token, 1853251881Speter const char *base_checksum) 1854251881Speter{ 1855299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( apply-textdelta ( ")); 1856251881Speter SVN_ERR(write_tuple_cstring(conn, pool, token)); 1857251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 1858251881Speter SVN_ERR(write_tuple_cstring_opt(conn, pool, base_checksum)); 1859251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 1860299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1861251881Speter 1862251881Speter return SVN_NO_ERROR; 1863251881Speter} 1864251881Speter 1865251881Spetersvn_error_t * 1866251881Spetersvn_ra_svn__write_cmd_close_edit(svn_ra_svn_conn_t *conn, 1867251881Speter apr_pool_t *pool) 1868251881Speter{ 1869299742Sdim return writebuf_write_literal(conn, pool, "( close-edit ( ) ) "); 1870251881Speter} 1871251881Speter 1872251881Spetersvn_error_t * 1873251881Spetersvn_ra_svn__write_cmd_abort_edit(svn_ra_svn_conn_t *conn, 1874251881Speter apr_pool_t *pool) 1875251881Speter{ 1876299742Sdim return writebuf_write_literal(conn, pool, "( abort-edit ( ) ) "); 1877251881Speter} 1878251881Speter 1879251881Spetersvn_error_t * 1880251881Spetersvn_ra_svn__write_cmd_set_path(svn_ra_svn_conn_t *conn, 1881251881Speter apr_pool_t *pool, 1882251881Speter const char *path, 1883251881Speter svn_revnum_t rev, 1884251881Speter svn_boolean_t start_empty, 1885251881Speter const char *lock_token, 1886251881Speter svn_depth_t depth) 1887251881Speter{ 1888299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( set-path ( ")); 1889251881Speter SVN_ERR(write_tuple_cstring(conn, pool, path)); 1890251881Speter SVN_ERR(write_tuple_revision(conn, pool, rev)); 1891251881Speter SVN_ERR(write_tuple_boolean(conn, pool, start_empty)); 1892251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 1893251881Speter SVN_ERR(write_tuple_cstring_opt(conn, pool, lock_token)); 1894251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 1895251881Speter SVN_ERR(write_tuple_depth(conn, pool, depth)); 1896299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1897251881Speter 1898251881Speter return SVN_NO_ERROR; 1899251881Speter} 1900251881Speter 1901251881Spetersvn_error_t * 1902251881Spetersvn_ra_svn__write_cmd_delete_path(svn_ra_svn_conn_t *conn, 1903251881Speter apr_pool_t *pool, 1904251881Speter const char *path) 1905251881Speter{ 1906299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( delete-path ( ")); 1907251881Speter SVN_ERR(write_tuple_cstring(conn, pool, path)); 1908299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1909251881Speter 1910251881Speter return SVN_NO_ERROR; 1911251881Speter} 1912251881Speter 1913251881Spetersvn_error_t * 1914251881Spetersvn_ra_svn__write_cmd_link_path(svn_ra_svn_conn_t *conn, 1915251881Speter apr_pool_t *pool, 1916251881Speter const char *path, 1917251881Speter const char *url, 1918251881Speter svn_revnum_t rev, 1919251881Speter svn_boolean_t start_empty, 1920251881Speter const char *lock_token, 1921251881Speter svn_depth_t depth) 1922251881Speter{ 1923299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( link-path ( ")); 1924251881Speter SVN_ERR(write_tuple_cstring(conn, pool, path)); 1925251881Speter SVN_ERR(write_tuple_cstring(conn, pool, url)); 1926251881Speter SVN_ERR(write_tuple_revision(conn, pool, rev)); 1927251881Speter SVN_ERR(write_tuple_boolean(conn, pool, start_empty)); 1928251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 1929251881Speter SVN_ERR(write_tuple_cstring_opt(conn, pool,lock_token)); 1930251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 1931251881Speter SVN_ERR(write_tuple_depth(conn, pool, depth)); 1932299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1933251881Speter 1934251881Speter return SVN_NO_ERROR; 1935251881Speter} 1936251881Speter 1937251881Spetersvn_error_t * 1938251881Spetersvn_ra_svn__write_cmd_finish_report(svn_ra_svn_conn_t *conn, 1939251881Speter apr_pool_t *pool) 1940251881Speter{ 1941299742Sdim return writebuf_write_literal(conn, pool, "( finish-report ( ) ) "); 1942251881Speter} 1943251881Speter 1944251881Spetersvn_error_t * 1945251881Spetersvn_ra_svn__write_cmd_abort_report(svn_ra_svn_conn_t *conn, 1946251881Speter apr_pool_t *pool) 1947251881Speter{ 1948299742Sdim return writebuf_write_literal(conn, pool, "( abort-report ( ) ) "); 1949251881Speter} 1950251881Speter 1951251881Spetersvn_error_t * 1952251881Spetersvn_ra_svn__write_cmd_reparent(svn_ra_svn_conn_t *conn, 1953251881Speter apr_pool_t *pool, 1954251881Speter const char *url) 1955251881Speter{ 1956299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( reparent ( ")); 1957251881Speter SVN_ERR(write_tuple_cstring(conn, pool, url)); 1958299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1959251881Speter 1960251881Speter return SVN_NO_ERROR; 1961251881Speter} 1962251881Speter 1963251881Spetersvn_error_t * 1964251881Spetersvn_ra_svn__write_cmd_get_latest_rev(svn_ra_svn_conn_t *conn, 1965251881Speter apr_pool_t *pool) 1966251881Speter{ 1967299742Sdim return writebuf_write_literal(conn, pool, "( get-latest-rev ( ) ) "); 1968251881Speter} 1969251881Speter 1970251881Spetersvn_error_t * 1971251881Spetersvn_ra_svn__write_cmd_get_dated_rev(svn_ra_svn_conn_t *conn, 1972251881Speter apr_pool_t *pool, 1973251881Speter apr_time_t tm) 1974251881Speter{ 1975299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( get-dated-rev ( ")); 1976251881Speter SVN_ERR(write_tuple_cstring(conn, pool, svn_time_to_cstring(tm, pool))); 1977299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 1978251881Speter 1979251881Speter return SVN_NO_ERROR; 1980251881Speter} 1981251881Speter 1982251881Spetersvn_error_t * 1983251881Spetersvn_ra_svn__write_cmd_change_rev_prop2(svn_ra_svn_conn_t *conn, 1984251881Speter apr_pool_t *pool, 1985251881Speter svn_revnum_t rev, 1986251881Speter const char *name, 1987251881Speter const svn_string_t *value, 1988251881Speter svn_boolean_t dont_care, 1989251881Speter const svn_string_t *old_value) 1990251881Speter{ 1991299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop2 ( ")); 1992251881Speter SVN_ERR(write_tuple_revision(conn, pool, rev)); 1993251881Speter SVN_ERR(write_tuple_cstring(conn, pool, name)); 1994251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 1995251881Speter SVN_ERR(write_tuple_string_opt(conn, pool, value)); 1996251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 1997251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 1998251881Speter SVN_ERR(write_tuple_boolean(conn, pool, dont_care)); 1999251881Speter SVN_ERR(write_tuple_string_opt(conn, pool, old_value)); 2000251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 2001299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2002251881Speter 2003251881Speter return SVN_NO_ERROR; 2004251881Speter} 2005251881Speter 2006251881Spetersvn_error_t * 2007251881Spetersvn_ra_svn__write_cmd_change_rev_prop(svn_ra_svn_conn_t *conn, 2008251881Speter apr_pool_t *pool, 2009251881Speter svn_revnum_t rev, 2010251881Speter const char *name, 2011251881Speter const svn_string_t *value) 2012251881Speter{ 2013299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop ( ")); 2014251881Speter SVN_ERR(write_tuple_revision(conn, pool, rev)); 2015251881Speter SVN_ERR(write_tuple_cstring(conn, pool, name)); 2016251881Speter SVN_ERR(write_tuple_string_opt(conn, pool, value)); 2017299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2018251881Speter 2019251881Speter return SVN_NO_ERROR; 2020251881Speter} 2021251881Speter 2022251881Spetersvn_error_t * 2023251881Spetersvn_ra_svn__write_cmd_rev_proplist(svn_ra_svn_conn_t *conn, 2024251881Speter apr_pool_t *pool, 2025251881Speter svn_revnum_t rev) 2026251881Speter{ 2027299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( rev-proplist ( ")); 2028251881Speter SVN_ERR(write_tuple_revision(conn, pool, rev)); 2029299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2030251881Speter 2031251881Speter return SVN_NO_ERROR; 2032251881Speter} 2033251881Speter 2034251881Spetersvn_error_t * 2035251881Spetersvn_ra_svn__write_cmd_rev_prop(svn_ra_svn_conn_t *conn, 2036251881Speter apr_pool_t *pool, 2037251881Speter svn_revnum_t rev, 2038251881Speter const char *name) 2039251881Speter{ 2040299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( rev-prop ( ")); 2041251881Speter SVN_ERR(write_tuple_revision(conn, pool, rev)); 2042251881Speter SVN_ERR(write_tuple_cstring(conn, pool, name)); 2043299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2044251881Speter 2045251881Speter return SVN_NO_ERROR; 2046251881Speter} 2047251881Speter 2048251881Spetersvn_error_t * 2049251881Spetersvn_ra_svn__write_cmd_get_file(svn_ra_svn_conn_t *conn, 2050251881Speter apr_pool_t *pool, 2051251881Speter const char *path, 2052251881Speter svn_revnum_t rev, 2053251881Speter svn_boolean_t props, 2054251881Speter svn_boolean_t stream) 2055251881Speter{ 2056299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( get-file ( ")); 2057251881Speter SVN_ERR(write_tuple_cstring(conn, pool, path)); 2058251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 2059251881Speter SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); 2060251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 2061251881Speter SVN_ERR(write_tuple_boolean(conn, pool, props)); 2062251881Speter SVN_ERR(write_tuple_boolean(conn, pool, stream)); 2063251881Speter 2064299742Sdim /* Always send the, nominally optional, want-iprops as "false" to 2065299742Sdim workaround a bug in svnserve 1.8.0-1.8.8 that causes the server 2066299742Sdim to see "true" if it is omitted. */ 2067299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, " false ) ) ")); 2068299742Sdim 2069251881Speter return SVN_NO_ERROR; 2070251881Speter} 2071251881Speter 2072251881Spetersvn_error_t * 2073251881Spetersvn_ra_svn__write_cmd_update(svn_ra_svn_conn_t *conn, 2074251881Speter apr_pool_t *pool, 2075251881Speter svn_revnum_t rev, 2076251881Speter const char *target, 2077251881Speter svn_boolean_t recurse, 2078251881Speter svn_depth_t depth, 2079251881Speter svn_boolean_t send_copyfrom_args, 2080251881Speter svn_boolean_t ignore_ancestry) 2081251881Speter{ 2082299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( update ( ")); 2083251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 2084251881Speter SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); 2085251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 2086251881Speter SVN_ERR(write_tuple_cstring(conn, pool, target)); 2087251881Speter SVN_ERR(write_tuple_boolean(conn, pool, recurse)); 2088251881Speter SVN_ERR(write_tuple_depth(conn, pool, depth)); 2089251881Speter SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args)); 2090251881Speter SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry)); 2091299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2092251881Speter 2093251881Speter return SVN_NO_ERROR; 2094251881Speter} 2095251881Speter 2096251881Spetersvn_error_t * 2097251881Spetersvn_ra_svn__write_cmd_switch(svn_ra_svn_conn_t *conn, 2098251881Speter apr_pool_t *pool, 2099251881Speter svn_revnum_t rev, 2100251881Speter const char *target, 2101251881Speter svn_boolean_t recurse, 2102251881Speter const char *switch_url, 2103251881Speter svn_depth_t depth, 2104251881Speter svn_boolean_t send_copyfrom_args, 2105251881Speter svn_boolean_t ignore_ancestry) 2106251881Speter{ 2107299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( switch ( ")); 2108251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 2109251881Speter SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); 2110251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 2111251881Speter SVN_ERR(write_tuple_cstring(conn, pool, target)); 2112251881Speter SVN_ERR(write_tuple_boolean(conn, pool, recurse)); 2113251881Speter SVN_ERR(write_tuple_cstring(conn, pool, switch_url)); 2114251881Speter SVN_ERR(write_tuple_depth(conn, pool, depth)); 2115251881Speter SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args)); 2116251881Speter SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry)); 2117299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2118251881Speter 2119251881Speter return SVN_NO_ERROR; 2120251881Speter} 2121251881Speter 2122251881Spetersvn_error_t * 2123251881Spetersvn_ra_svn__write_cmd_status(svn_ra_svn_conn_t *conn, 2124251881Speter apr_pool_t *pool, 2125251881Speter const char *target, 2126251881Speter svn_boolean_t recurse, 2127251881Speter svn_revnum_t rev, 2128251881Speter svn_depth_t depth) 2129251881Speter{ 2130299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( status ( ")); 2131251881Speter SVN_ERR(write_tuple_cstring(conn, pool, target)); 2132251881Speter SVN_ERR(write_tuple_boolean(conn, pool, recurse)); 2133251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 2134251881Speter SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); 2135251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 2136251881Speter SVN_ERR(write_tuple_depth(conn, pool, depth)); 2137299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2138251881Speter 2139251881Speter return SVN_NO_ERROR; 2140251881Speter} 2141251881Speter 2142251881Spetersvn_error_t * 2143251881Spetersvn_ra_svn__write_cmd_diff(svn_ra_svn_conn_t *conn, 2144251881Speter apr_pool_t *pool, 2145251881Speter svn_revnum_t rev, 2146251881Speter const char *target, 2147251881Speter svn_boolean_t recurse, 2148251881Speter svn_boolean_t ignore_ancestry, 2149251881Speter const char *versus_url, 2150251881Speter svn_boolean_t text_deltas, 2151251881Speter svn_depth_t depth) 2152251881Speter{ 2153299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( diff ( ")); 2154251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 2155251881Speter SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); 2156251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 2157251881Speter SVN_ERR(write_tuple_cstring(conn, pool, target)); 2158251881Speter SVN_ERR(write_tuple_boolean(conn, pool, recurse)); 2159251881Speter SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry)); 2160251881Speter SVN_ERR(write_tuple_cstring(conn, pool, versus_url)); 2161251881Speter SVN_ERR(write_tuple_boolean(conn, pool, text_deltas)); 2162251881Speter SVN_ERR(write_tuple_depth(conn, pool, depth)); 2163299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2164251881Speter 2165251881Speter return SVN_NO_ERROR; 2166251881Speter} 2167251881Speter 2168251881Spetersvn_error_t * 2169251881Spetersvn_ra_svn__write_cmd_check_path(svn_ra_svn_conn_t *conn, 2170251881Speter apr_pool_t *pool, 2171251881Speter const char *path, 2172251881Speter svn_revnum_t rev) 2173251881Speter{ 2174299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( check-path ( ")); 2175251881Speter SVN_ERR(write_tuple_cstring(conn, pool, path)); 2176251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 2177251881Speter SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); 2178251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 2179299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2180251881Speter 2181251881Speter return SVN_NO_ERROR; 2182251881Speter} 2183251881Speter 2184251881Spetersvn_error_t * 2185251881Spetersvn_ra_svn__write_cmd_stat(svn_ra_svn_conn_t *conn, 2186251881Speter apr_pool_t *pool, 2187251881Speter const char *path, 2188251881Speter svn_revnum_t rev) 2189251881Speter{ 2190299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( stat ( ")); 2191251881Speter SVN_ERR(write_tuple_cstring(conn, pool, path)); 2192251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 2193251881Speter SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); 2194251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 2195299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2196251881Speter 2197251881Speter return SVN_NO_ERROR; 2198251881Speter} 2199251881Speter 2200251881Spetersvn_error_t * 2201251881Spetersvn_ra_svn__write_cmd_get_file_revs(svn_ra_svn_conn_t *conn, 2202251881Speter apr_pool_t *pool, 2203251881Speter const char *path, 2204251881Speter svn_revnum_t start, 2205251881Speter svn_revnum_t end, 2206251881Speter svn_boolean_t include_merged_revisions) 2207251881Speter{ 2208299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( get-file-revs ( ")); 2209251881Speter SVN_ERR(write_tuple_cstring(conn, pool, path)); 2210251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 2211251881Speter SVN_ERR(write_tuple_revision_opt(conn, pool, start)); 2212251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 2213251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 2214251881Speter SVN_ERR(write_tuple_revision_opt(conn, pool, end)); 2215251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 2216251881Speter SVN_ERR(write_tuple_boolean(conn, pool, include_merged_revisions)); 2217299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2218251881Speter 2219251881Speter return SVN_NO_ERROR; 2220251881Speter} 2221251881Speter 2222251881Spetersvn_error_t * 2223251881Spetersvn_ra_svn__write_cmd_lock(svn_ra_svn_conn_t *conn, 2224251881Speter apr_pool_t *pool, 2225251881Speter const char *path, 2226251881Speter const char *comment, 2227251881Speter svn_boolean_t steal_lock, 2228251881Speter svn_revnum_t revnum) 2229251881Speter{ 2230299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( lock ( ")); 2231251881Speter SVN_ERR(write_tuple_cstring(conn, pool, path)); 2232251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 2233251881Speter SVN_ERR(write_tuple_cstring_opt(conn, pool, comment)); 2234251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 2235251881Speter SVN_ERR(write_tuple_boolean(conn, pool, steal_lock)); 2236251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 2237251881Speter SVN_ERR(write_tuple_revision_opt(conn, pool, revnum)); 2238251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 2239299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2240251881Speter 2241251881Speter return SVN_NO_ERROR; 2242251881Speter} 2243251881Speter 2244251881Spetersvn_error_t * 2245251881Spetersvn_ra_svn__write_cmd_unlock(svn_ra_svn_conn_t *conn, 2246251881Speter apr_pool_t *pool, 2247251881Speter const char *path, 2248251881Speter const char *token, 2249251881Speter svn_boolean_t break_lock) 2250251881Speter{ 2251299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( unlock ( ")); 2252251881Speter SVN_ERR(write_tuple_cstring(conn, pool, path)); 2253251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 2254251881Speter SVN_ERR(write_tuple_cstring_opt(conn, pool, token)); 2255251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 2256251881Speter SVN_ERR(write_tuple_boolean(conn, pool, break_lock)); 2257299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2258251881Speter 2259251881Speter return SVN_NO_ERROR; 2260251881Speter} 2261251881Speter 2262251881Spetersvn_error_t * 2263251881Spetersvn_ra_svn__write_cmd_get_lock(svn_ra_svn_conn_t *conn, 2264251881Speter apr_pool_t *pool, 2265251881Speter const char *path) 2266251881Speter{ 2267299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( get-lock ( ")); 2268251881Speter SVN_ERR(write_tuple_cstring(conn, pool, path)); 2269299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2270251881Speter 2271251881Speter return SVN_NO_ERROR; 2272251881Speter} 2273251881Speter 2274251881Spetersvn_error_t * 2275251881Spetersvn_ra_svn__write_cmd_get_locks(svn_ra_svn_conn_t *conn, 2276251881Speter apr_pool_t *pool, 2277251881Speter const char *path, 2278251881Speter svn_depth_t depth) 2279251881Speter{ 2280299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( get-locks ( ")); 2281251881Speter SVN_ERR(write_tuple_cstring(conn, pool, path)); 2282251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 2283251881Speter SVN_ERR(write_tuple_depth(conn, pool, depth)); 2284251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 2285299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2286251881Speter 2287251881Speter return SVN_NO_ERROR; 2288251881Speter} 2289251881Speter 2290251881Spetersvn_error_t * 2291251881Spetersvn_ra_svn__write_cmd_replay(svn_ra_svn_conn_t *conn, 2292251881Speter apr_pool_t *pool, 2293251881Speter svn_revnum_t rev, 2294251881Speter svn_revnum_t low_water_mark, 2295251881Speter svn_boolean_t send_deltas) 2296251881Speter{ 2297299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( replay ( ")); 2298251881Speter SVN_ERR(write_tuple_revision(conn, pool, rev)); 2299251881Speter SVN_ERR(write_tuple_revision(conn, pool, low_water_mark)); 2300251881Speter SVN_ERR(write_tuple_boolean(conn, pool, send_deltas)); 2301299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2302251881Speter 2303251881Speter return SVN_NO_ERROR; 2304251881Speter} 2305251881Speter 2306251881Spetersvn_error_t * 2307251881Spetersvn_ra_svn__write_cmd_replay_range(svn_ra_svn_conn_t *conn, 2308251881Speter apr_pool_t *pool, 2309251881Speter svn_revnum_t start_revision, 2310251881Speter svn_revnum_t end_revision, 2311251881Speter svn_revnum_t low_water_mark, 2312251881Speter svn_boolean_t send_deltas) 2313251881Speter{ 2314299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( replay-range ( ")); 2315251881Speter SVN_ERR(write_tuple_revision(conn, pool, start_revision)); 2316251881Speter SVN_ERR(write_tuple_revision(conn, pool, end_revision)); 2317251881Speter SVN_ERR(write_tuple_revision(conn, pool, low_water_mark)); 2318251881Speter SVN_ERR(write_tuple_boolean(conn, pool, send_deltas)); 2319299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2320251881Speter 2321251881Speter return SVN_NO_ERROR; 2322251881Speter} 2323251881Speter 2324251881Spetersvn_error_t * 2325251881Spetersvn_ra_svn__write_cmd_get_deleted_rev(svn_ra_svn_conn_t *conn, 2326251881Speter apr_pool_t *pool, 2327251881Speter const char *path, 2328251881Speter svn_revnum_t peg_revision, 2329251881Speter svn_revnum_t end_revision) 2330251881Speter{ 2331299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( get-deleted-rev ( ")); 2332251881Speter SVN_ERR(write_tuple_cstring(conn, pool, path)); 2333251881Speter SVN_ERR(write_tuple_revision(conn, pool, peg_revision)); 2334251881Speter SVN_ERR(write_tuple_revision(conn, pool, end_revision)); 2335299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2336251881Speter 2337251881Speter return SVN_NO_ERROR; 2338251881Speter} 2339251881Speter 2340251881Spetersvn_error_t * 2341251881Spetersvn_ra_svn__write_cmd_get_iprops(svn_ra_svn_conn_t *conn, 2342251881Speter apr_pool_t *pool, 2343251881Speter const char *path, 2344251881Speter svn_revnum_t revision) 2345251881Speter{ 2346299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( get-iprops ( ")); 2347251881Speter SVN_ERR(write_tuple_cstring(conn, pool, path)); 2348251881Speter SVN_ERR(write_tuple_start_list(conn, pool)); 2349251881Speter SVN_ERR(write_tuple_revision_opt(conn, pool, revision)); 2350251881Speter SVN_ERR(write_tuple_end_list(conn, pool)); 2351299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); 2352251881Speter 2353251881Speter return SVN_NO_ERROR; 2354251881Speter} 2355251881Speter 2356251881Spetersvn_error_t * 2357251881Spetersvn_ra_svn__write_cmd_finish_replay(svn_ra_svn_conn_t *conn, 2358251881Speter apr_pool_t *pool) 2359251881Speter{ 2360299742Sdim return writebuf_write_literal(conn, pool, "( finish-replay ( ) ) "); 2361251881Speter} 2362251881Speter 2363251881Spetersvn_error_t *svn_ra_svn__write_cmd_response(svn_ra_svn_conn_t *conn, 2364251881Speter apr_pool_t *pool, 2365251881Speter const char *fmt, ...) 2366251881Speter{ 2367251881Speter va_list ap; 2368251881Speter svn_error_t *err; 2369251881Speter 2370299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( success ")); 2371251881Speter va_start(ap, fmt); 2372251881Speter err = vwrite_tuple(conn, pool, fmt, &ap); 2373251881Speter va_end(ap); 2374251881Speter return err ? svn_error_trace(err) : svn_ra_svn__end_list(conn, pool); 2375251881Speter} 2376251881Speter 2377251881Spetersvn_error_t *svn_ra_svn__write_cmd_failure(svn_ra_svn_conn_t *conn, 2378299742Sdim apr_pool_t *pool, 2379299742Sdim const svn_error_t *err) 2380251881Speter{ 2381251881Speter char buffer[128]; 2382299742Sdim SVN_ERR(writebuf_write_literal(conn, pool, "( failure ( ")); 2383251881Speter for (; err; err = err->child) 2384251881Speter { 2385251881Speter const char *msg; 2386251881Speter 2387251881Speter#ifdef SVN_ERR__TRACING 2388251881Speter if (svn_error__is_tracing_link(err)) 2389251881Speter msg = err->message; 2390251881Speter else 2391251881Speter#endif 2392251881Speter msg = svn_err_best_message(err, buffer, sizeof(buffer)); 2393251881Speter 2394251881Speter /* The message string should have been optional, but we can't 2395251881Speter easily change that, so marshal nonexistent messages as "". */ 2396251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "nccn", 2397251881Speter (apr_uint64_t) err->apr_err, 2398251881Speter msg ? msg : "", 2399251881Speter err->file ? err->file : "", 2400251881Speter (apr_uint64_t) err->line)); 2401251881Speter } 2402299742Sdim return writebuf_write_literal(conn, pool, ") ) "); 2403251881Speter} 2404299742Sdim 2405299742Sdimsvn_error_t * 2406299742Sdimsvn_ra_svn__write_data_log_changed_path(svn_ra_svn_conn_t *conn, 2407299742Sdim apr_pool_t *pool, 2408299742Sdim const char *path, 2409299742Sdim char action, 2410299742Sdim const char *copyfrom_path, 2411299742Sdim svn_revnum_t copyfrom_rev, 2412299742Sdim svn_node_kind_t node_kind, 2413299742Sdim svn_boolean_t text_modified, 2414299742Sdim svn_boolean_t props_modified) 2415299742Sdim{ 2416299742Sdim SVN_ERR(write_tuple_start_list(conn, pool)); 2417299742Sdim 2418299742Sdim SVN_ERR(write_tuple_cstring(conn, pool, path)); 2419299742Sdim SVN_ERR(writebuf_writechar(conn, pool, action)); 2420299742Sdim SVN_ERR(writebuf_writechar(conn, pool, ' ')); 2421299742Sdim SVN_ERR(write_tuple_start_list(conn, pool)); 2422299742Sdim SVN_ERR(write_tuple_cstring_opt(conn, pool, copyfrom_path)); 2423299742Sdim SVN_ERR(write_tuple_revision_opt(conn, pool, copyfrom_rev)); 2424299742Sdim SVN_ERR(write_tuple_end_list(conn, pool)); 2425299742Sdim SVN_ERR(write_tuple_start_list(conn, pool)); 2426299742Sdim SVN_ERR(write_tuple_cstring(conn, pool, svn_node_kind_to_word(node_kind))); 2427299742Sdim SVN_ERR(write_tuple_boolean(conn, pool, text_modified)); 2428299742Sdim SVN_ERR(write_tuple_boolean(conn, pool, props_modified)); 2429299742Sdim 2430299742Sdim return writebuf_write_literal(conn, pool, ") ) "); 2431299742Sdim} 2432299742Sdim 2433299742Sdimsvn_error_t * 2434299742Sdimsvn_ra_svn__write_data_log_entry(svn_ra_svn_conn_t *conn, 2435299742Sdim apr_pool_t *pool, 2436299742Sdim svn_revnum_t revision, 2437299742Sdim const svn_string_t *author, 2438299742Sdim const svn_string_t *date, 2439299742Sdim const svn_string_t *message, 2440299742Sdim svn_boolean_t has_children, 2441299742Sdim svn_boolean_t invalid_revnum, 2442299742Sdim unsigned revprop_count) 2443299742Sdim{ 2444299742Sdim SVN_ERR(write_tuple_revision(conn, pool, revision)); 2445299742Sdim SVN_ERR(write_tuple_start_list(conn, pool)); 2446299742Sdim SVN_ERR(write_tuple_string_opt(conn, pool, author)); 2447299742Sdim SVN_ERR(write_tuple_end_list(conn, pool)); 2448299742Sdim SVN_ERR(write_tuple_start_list(conn, pool)); 2449299742Sdim SVN_ERR(write_tuple_string_opt(conn, pool, date)); 2450299742Sdim SVN_ERR(write_tuple_end_list(conn, pool)); 2451299742Sdim SVN_ERR(write_tuple_start_list(conn, pool)); 2452299742Sdim SVN_ERR(write_tuple_string_opt(conn, pool, message)); 2453299742Sdim SVN_ERR(write_tuple_end_list(conn, pool)); 2454299742Sdim SVN_ERR(write_tuple_boolean(conn, pool, has_children)); 2455299742Sdim SVN_ERR(write_tuple_boolean(conn, pool, invalid_revnum)); 2456299742Sdim SVN_ERR(svn_ra_svn__write_number(conn, pool, revprop_count)); 2457299742Sdim 2458299742Sdim return SVN_NO_ERROR; 2459299742Sdim} 2460299742Sdim 2461299742Sdim/* If condition COND is not met, return a "malformed network data" error. 2462299742Sdim */ 2463299742Sdim#define CHECK_PROTOCOL_COND(cond)\ 2464299742Sdim if (!(cond)) \ 2465299742Sdim return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, \ 2466299742Sdim _("Malformed network data")); 2467299742Sdim 2468299742Sdim/* In *RESULT, return the SVN-style string at index IDX in tuple ITEMS. 2469299742Sdim */ 2470299742Sdimstatic svn_error_t * 2471299742Sdimsvn_ra_svn__read_string(const apr_array_header_t *items, 2472299742Sdim int idx, 2473299742Sdim svn_string_t **result) 2474299742Sdim{ 2475299742Sdim svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); 2476299742Sdim CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING); 2477299742Sdim *result = elt->u.string; 2478299742Sdim 2479299742Sdim return SVN_NO_ERROR; 2480299742Sdim} 2481299742Sdim 2482299742Sdim/* In *RESULT, return the C-style string at index IDX in tuple ITEMS. 2483299742Sdim */ 2484299742Sdimstatic svn_error_t * 2485299742Sdimsvn_ra_svn__read_cstring(const apr_array_header_t *items, 2486299742Sdim int idx, 2487299742Sdim const char **result) 2488299742Sdim{ 2489299742Sdim svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); 2490299742Sdim CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING); 2491299742Sdim *result = elt->u.string->data; 2492299742Sdim 2493299742Sdim return SVN_NO_ERROR; 2494299742Sdim} 2495299742Sdim 2496299742Sdim/* In *RESULT, return the word at index IDX in tuple ITEMS. 2497299742Sdim */ 2498299742Sdimstatic svn_error_t * 2499299742Sdimsvn_ra_svn__read_word(const apr_array_header_t *items, 2500299742Sdim int idx, 2501299742Sdim const char **result) 2502299742Sdim{ 2503299742Sdim svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); 2504299742Sdim CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD); 2505299742Sdim *result = elt->u.word; 2506299742Sdim 2507299742Sdim return SVN_NO_ERROR; 2508299742Sdim} 2509299742Sdim 2510299742Sdim/* In *RESULT, return the revision at index IDX in tuple ITEMS. 2511299742Sdim */ 2512299742Sdimstatic svn_error_t * 2513299742Sdimsvn_ra_svn__read_revision(const apr_array_header_t *items, 2514299742Sdim int idx, 2515299742Sdim svn_revnum_t *result) 2516299742Sdim{ 2517299742Sdim svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); 2518299742Sdim CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_NUMBER); 2519299742Sdim *result = (svn_revnum_t)elt->u.number; 2520299742Sdim 2521299742Sdim return SVN_NO_ERROR; 2522299742Sdim} 2523299742Sdim 2524299742Sdim/* In *RESULT, return the boolean at index IDX in tuple ITEMS. 2525299742Sdim */ 2526299742Sdimstatic svn_error_t * 2527299742Sdimsvn_ra_svn__read_boolean(const apr_array_header_t *items, 2528299742Sdim int idx, 2529299742Sdim apr_uint64_t *result) 2530299742Sdim{ 2531299742Sdim svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); 2532299742Sdim CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD); 2533299742Sdim if (elt->u.word[0] == 't' && strcmp(elt->u.word, "true") == 0) 2534299742Sdim *result = TRUE; 2535299742Sdim else if (strcmp(elt->u.word, "false") == 0) 2536299742Sdim *result = FALSE; 2537299742Sdim else 2538299742Sdim CHECK_PROTOCOL_COND(FALSE); 2539299742Sdim 2540299742Sdim return SVN_NO_ERROR; 2541299742Sdim} 2542299742Sdim 2543299742Sdim/* In *RESULT, return the tuple at index IDX in tuple ITEMS. 2544299742Sdim */ 2545299742Sdimstatic svn_error_t * 2546299742Sdimsvn_ra_svn__read_list(const apr_array_header_t *items, 2547299742Sdim int idx, 2548299742Sdim const apr_array_header_t **result) 2549299742Sdim{ 2550299742Sdim svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); 2551299742Sdim CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_LIST); 2552299742Sdim 2553299742Sdim *result = elt->u.list; 2554299742Sdim return SVN_NO_ERROR; 2555299742Sdim} 2556299742Sdim 2557299742Sdim/* Verify the tuple ITEMS contains at least MIN and at most MAX elements. 2558299742Sdim */ 2559299742Sdimstatic svn_error_t * 2560299742Sdimsvn_ra_svn__read_check_array_size(const apr_array_header_t *items, 2561299742Sdim int min, 2562299742Sdim int max) 2563299742Sdim{ 2564299742Sdim CHECK_PROTOCOL_COND(items->nelts >= min && items->nelts <= max); 2565299742Sdim return SVN_NO_ERROR; 2566299742Sdim} 2567299742Sdim 2568299742Sdimsvn_error_t * 2569299742Sdimsvn_ra_svn__read_data_log_changed_entry(const apr_array_header_t *items, 2570299742Sdim svn_string_t **cpath, 2571299742Sdim const char **action, 2572299742Sdim const char **copy_path, 2573299742Sdim svn_revnum_t *copy_rev, 2574299742Sdim const char **kind_str, 2575299742Sdim apr_uint64_t *text_mods, 2576299742Sdim apr_uint64_t *prop_mods) 2577299742Sdim{ 2578299742Sdim const apr_array_header_t *sub_items; 2579299742Sdim 2580299742Sdim /* initialize optional values */ 2581299742Sdim *copy_path = NULL; 2582299742Sdim *copy_rev = SVN_INVALID_REVNUM; 2583299742Sdim *kind_str = NULL; 2584299742Sdim *text_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER; 2585299742Sdim *prop_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER; 2586299742Sdim 2587299742Sdim /* top-level elements (mandatory) */ 2588299742Sdim SVN_ERR(svn_ra_svn__read_check_array_size(items, 3, INT_MAX)); 2589299742Sdim SVN_ERR(svn_ra_svn__read_string(items, 0, cpath)); 2590299742Sdim SVN_ERR(svn_ra_svn__read_word(items, 1, action)); 2591299742Sdim 2592299742Sdim /* first sub-structure (mandatory) */ 2593299742Sdim SVN_ERR(svn_ra_svn__read_list(items, 2, &sub_items)); 2594299742Sdim if (sub_items->nelts) 2595299742Sdim { 2596299742Sdim SVN_ERR(svn_ra_svn__read_check_array_size(sub_items, 2, 2)); 2597299742Sdim SVN_ERR(svn_ra_svn__read_cstring(sub_items, 0, copy_path)); 2598299742Sdim SVN_ERR(svn_ra_svn__read_revision(sub_items, 1, copy_rev)); 2599299742Sdim } 2600299742Sdim 2601299742Sdim /* second sub-structure (optional) */ 2602299742Sdim if (items->nelts >= 4) 2603299742Sdim { 2604299742Sdim SVN_ERR(svn_ra_svn__read_list(items, 3, &sub_items)); 2605299742Sdim switch (MIN(3, sub_items->nelts)) 2606299742Sdim { 2607299742Sdim case 3 : SVN_ERR(svn_ra_svn__read_boolean(sub_items, 2, prop_mods)); 2608299742Sdim case 2 : SVN_ERR(svn_ra_svn__read_boolean(sub_items, 1, text_mods)); 2609299742Sdim case 1 : SVN_ERR(svn_ra_svn__read_cstring(sub_items, 0, kind_str)); 2610299742Sdim default: break; 2611299742Sdim } 2612299742Sdim } 2613299742Sdim 2614299742Sdim return SVN_NO_ERROR; 2615299742Sdim} 2616