In the command-line client, forbid
[svn.git] / subversion / libsvn_ra_svn / marshal.c
blob8f554f564c6765418f1bc21684be0ba5fdba5fd9
1 /*
2 * marshal.c : Marshalling routines for Subversion protocol
4 * ====================================================================
5 * Copyright (c) 2000-2007 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
21 #include <assert.h>
22 #include <stdlib.h>
24 #define APR_WANT_STRFUNC
25 #include <apr_want.h>
26 #include <apr_general.h>
27 #include <apr_lib.h>
28 #include <apr_strings.h>
29 #include <apr_network_io.h>
30 #include <apr_poll.h>
32 #include "svn_types.h"
33 #include "svn_string.h"
34 #include "svn_error.h"
35 #include "svn_pools.h"
36 #include "svn_ra_svn.h"
37 #include "svn_private_config.h"
39 #include "ra_svn.h"
41 #define svn_iswhitespace(c) ((c) == ' ' || (c) == '\n')
43 /* --- CONNECTION INITIALIZATION --- */
45 svn_ra_svn_conn_t *svn_ra_svn_create_conn(apr_socket_t *sock,
46 apr_file_t *in_file,
47 apr_file_t *out_file,
48 apr_pool_t *pool)
50 svn_ra_svn_conn_t *conn = apr_palloc(pool, sizeof(*conn));
52 assert((sock && !in_file && !out_file) || (!sock && in_file && out_file));
53 #ifdef SVN_HAVE_SASL
54 conn->sock = sock;
55 conn->encrypted = FALSE;
56 #endif
57 conn->session = NULL;
58 conn->read_ptr = conn->read_buf;
59 conn->read_end = conn->read_buf;
60 conn->write_pos = 0;
61 conn->block_handler = NULL;
62 conn->block_baton = NULL;
63 conn->capabilities = apr_hash_make(pool);
64 conn->pool = pool;
66 if (sock != NULL)
67 conn->stream = svn_ra_svn__stream_from_sock(sock, pool);
68 else
69 conn->stream = svn_ra_svn__stream_from_files(in_file, out_file, pool);
71 return conn;
74 svn_error_t *svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn,
75 apr_array_header_t *list)
77 int i;
78 svn_ra_svn_item_t *item;
79 const char *word;
81 for (i = 0; i < list->nelts; i++)
83 item = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
84 if (item->kind != SVN_RA_SVN_WORD)
85 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
86 _("Capability entry is not a word"));
87 word = apr_pstrdup(conn->pool, item->u.word);
88 apr_hash_set(conn->capabilities, word, APR_HASH_KEY_STRING, word);
90 return SVN_NO_ERROR;
93 svn_boolean_t svn_ra_svn_has_capability(svn_ra_svn_conn_t *conn,
94 const char *capability)
96 return (apr_hash_get(conn->capabilities, capability,
97 APR_HASH_KEY_STRING) != NULL);
100 void
101 svn_ra_svn__set_block_handler(svn_ra_svn_conn_t *conn,
102 ra_svn_block_handler_t handler,
103 void *baton)
105 apr_interval_time_t interval = (handler) ? 0 : -1;
107 conn->block_handler = handler;
108 conn->block_baton = baton;
109 svn_ra_svn__stream_timeout(conn->stream, interval);
112 svn_boolean_t svn_ra_svn__input_waiting(svn_ra_svn_conn_t *conn,
113 apr_pool_t *pool)
115 return svn_ra_svn__stream_pending(conn->stream);
118 /* --- WRITE BUFFER MANAGEMENT --- */
120 /* Write bytes into the write buffer until either the write buffer is
121 * full or we reach END. */
122 static const char *writebuf_push(svn_ra_svn_conn_t *conn, const char *data,
123 const char *end)
125 apr_ssize_t buflen, copylen;
127 buflen = sizeof(conn->write_buf) - conn->write_pos;
128 copylen = (buflen < end - data) ? buflen : end - data;
129 memcpy(conn->write_buf + conn->write_pos, data, copylen);
130 conn->write_pos += copylen;
131 return data + copylen;
134 /* Write data to socket or output file as appropriate. */
135 static svn_error_t *writebuf_output(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
136 const char *data, apr_size_t len)
138 const char *end = data + len;
139 apr_size_t count;
140 apr_pool_t *subpool = NULL;
141 svn_ra_svn__session_baton_t *session = conn->session;
143 while (data < end)
145 count = end - data;
147 if (session && session->callbacks &&
148 session->callbacks->cancel_func)
149 SVN_ERR((session->callbacks->cancel_func)
150 (session->callbacks_baton));
152 SVN_ERR(svn_ra_svn__stream_write(conn->stream, data, &count));
153 if (count == 0)
155 if (!subpool)
156 subpool = svn_pool_create(pool);
157 else
158 svn_pool_clear(subpool);
159 SVN_ERR(conn->block_handler(conn, subpool, conn->block_baton));
161 data += count;
163 if (session)
165 const svn_ra_callbacks2_t *cb = session->callbacks;
166 session->bytes_written += count;
168 if (cb && cb->progress_func)
169 (cb->progress_func)(session->bytes_written + session->bytes_read,
170 -1, cb->progress_baton, subpool);
174 if (subpool)
175 svn_pool_destroy(subpool);
176 return SVN_NO_ERROR;
179 /* Write data from the write buffer out to the socket. */
180 static svn_error_t *writebuf_flush(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
182 int write_pos = conn->write_pos;
184 /* Clear conn->write_pos first in case the block handler does a read. */
185 conn->write_pos = 0;
186 SVN_ERR(writebuf_output(conn, pool, conn->write_buf, write_pos));
187 return SVN_NO_ERROR;
190 static svn_error_t *writebuf_write(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
191 const char *data, apr_size_t len)
193 const char *end = data + len;
195 if (conn->write_pos > 0 && conn->write_pos + len > sizeof(conn->write_buf))
197 /* Fill and then empty the write buffer. */
198 data = writebuf_push(conn, data, end);
199 SVN_ERR(writebuf_flush(conn, pool));
202 if (end - data > (apr_ssize_t)sizeof(conn->write_buf))
203 SVN_ERR(writebuf_output(conn, pool, data, end - data));
204 else
205 writebuf_push(conn, data, end);
206 return SVN_NO_ERROR;
209 static svn_error_t *writebuf_printf(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
210 const char *fmt, ...)
211 __attribute__ ((format(printf, 3, 4)));
212 static svn_error_t *writebuf_printf(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
213 const char *fmt, ...)
215 va_list ap;
216 char *str;
218 va_start(ap, fmt);
219 str = apr_pvsprintf(pool, fmt, ap);
220 va_end(ap);
221 return writebuf_write(conn, pool, str, strlen(str));
224 /* --- READ BUFFER MANAGEMENT --- */
226 /* Read bytes into DATA until either the read buffer is empty or
227 * we reach END. */
228 static char *readbuf_drain(svn_ra_svn_conn_t *conn, char *data, char *end)
230 apr_ssize_t buflen, copylen;
232 buflen = conn->read_end - conn->read_ptr;
233 copylen = (buflen < end - data) ? buflen : end - data;
234 memcpy(data, conn->read_ptr, copylen);
235 conn->read_ptr += copylen;
236 return data + copylen;
239 /* Read data from socket or input file as appropriate. */
240 static svn_error_t *readbuf_input(svn_ra_svn_conn_t *conn, char *data,
241 apr_size_t *len, apr_pool_t *pool)
243 svn_ra_svn__session_baton_t *session = conn->session;
245 if (session && session->callbacks &&
246 session->callbacks->cancel_func)
247 SVN_ERR((session->callbacks->cancel_func)
248 (session->callbacks_baton));
250 SVN_ERR(svn_ra_svn__stream_read(conn->stream, data, len));
251 if (*len == 0)
252 return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL,
253 _("Connection closed unexpectedly"));
255 if (session)
257 const svn_ra_callbacks2_t *cb = session->callbacks;
258 session->bytes_read += *len;
260 if (cb && cb->progress_func)
261 (cb->progress_func)(session->bytes_read + session->bytes_written,
262 -1, cb->progress_baton, pool);
265 return SVN_NO_ERROR;
268 /* Read data from the socket into the read buffer, which must be empty. */
269 static svn_error_t *readbuf_fill(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
271 apr_size_t len;
273 assert(conn->read_ptr == conn->read_end);
274 SVN_ERR(writebuf_flush(conn, pool));
275 len = sizeof(conn->read_buf);
276 SVN_ERR(readbuf_input(conn, conn->read_buf, &len, pool));
277 conn->read_ptr = conn->read_buf;
278 conn->read_end = conn->read_buf + len;
279 return SVN_NO_ERROR;
282 static svn_error_t *readbuf_getchar(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
283 char *result)
285 if (conn->read_ptr == conn->read_end)
286 SVN_ERR(readbuf_fill(conn, pool));
287 *result = *conn->read_ptr++;
288 return SVN_NO_ERROR;
291 static svn_error_t *readbuf_getchar_skip_whitespace(svn_ra_svn_conn_t *conn,
292 apr_pool_t *pool,
293 char *result)
296 SVN_ERR(readbuf_getchar(conn, pool, result));
297 while (svn_iswhitespace(*result));
298 return SVN_NO_ERROR;
301 static svn_error_t *readbuf_read(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
302 char *data, apr_size_t len)
304 char *end = data + len;
305 apr_size_t count;
307 /* Copy in an appropriate amount of data from the buffer. */
308 data = readbuf_drain(conn, data, end);
310 /* Read large chunks directly into buffer. */
311 while (end - data > (apr_ssize_t)sizeof(conn->read_buf))
313 SVN_ERR(writebuf_flush(conn, pool));
314 count = end - data;
315 SVN_ERR(readbuf_input(conn, data, &count, pool));
316 data += count;
319 while (end > data)
321 /* The remaining amount to read is small; fill the buffer and
322 * copy from that. */
323 SVN_ERR(readbuf_fill(conn, pool));
324 data = readbuf_drain(conn, data, end);
327 return SVN_NO_ERROR;
330 static svn_error_t *readbuf_skip_leading_garbage(svn_ra_svn_conn_t *conn,
331 apr_pool_t *pool)
333 char buf[256]; /* Must be smaller than sizeof(conn->read_buf) - 1. */
334 const char *p, *end;
335 apr_size_t len;
336 svn_boolean_t lparen = FALSE;
338 assert(conn->read_ptr == conn->read_end);
339 while (1)
341 /* Read some data directly from the connection input source. */
342 len = sizeof(buf);
343 SVN_ERR(readbuf_input(conn, buf, &len, pool));
344 end = buf + len;
346 /* Scan the data for '(' WS with a very simple state machine. */
347 for (p = buf; p < end; p++)
349 if (lparen && svn_iswhitespace(*p))
350 break;
351 else
352 lparen = (*p == '(');
354 if (p < end)
355 break;
358 /* p now points to the whitespace just after the left paren. Fake
359 * up the left paren and then copy what we have into the read
360 * buffer. */
361 conn->read_buf[0] = '(';
362 memcpy(conn->read_buf + 1, p, end - p);
363 conn->read_ptr = conn->read_buf;
364 conn->read_end = conn->read_buf + 1 + (end - p);
365 return SVN_NO_ERROR;
368 /* --- WRITING DATA ITEMS --- */
370 svn_error_t *svn_ra_svn_write_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
371 apr_uint64_t number)
373 return writebuf_printf(conn, pool, "%" APR_UINT64_T_FMT " ", number);
376 svn_error_t *svn_ra_svn_write_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
377 const svn_string_t *str)
379 SVN_ERR(writebuf_printf(conn, pool, "%" APR_SIZE_T_FMT ":", str->len));
380 SVN_ERR(writebuf_write(conn, pool, str->data, str->len));
381 SVN_ERR(writebuf_write(conn, pool, " ", 1));
382 return SVN_NO_ERROR;
385 svn_error_t *svn_ra_svn_write_cstring(svn_ra_svn_conn_t *conn,
386 apr_pool_t *pool, const char *s)
388 return writebuf_printf(conn, pool, "%" APR_SIZE_T_FMT ":%s ", strlen(s), s);
391 svn_error_t *svn_ra_svn_write_word(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
392 const char *word)
394 return writebuf_printf(conn, pool, "%s ", word);
397 svn_error_t *svn_ra_svn_write_proplist(svn_ra_svn_conn_t *conn,
398 apr_pool_t *pool, apr_hash_t *props)
400 apr_pool_t *iterpool;
401 apr_hash_index_t *hi;
402 const void *key;
403 void *val;
404 const char *propname;
405 svn_string_t *propval;
407 if (props)
409 iterpool = svn_pool_create(pool);
410 for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
412 svn_pool_clear(iterpool);
413 apr_hash_this(hi, &key, NULL, &val);
414 propname = key;
415 propval = val;
416 SVN_ERR(svn_ra_svn_write_tuple(conn, iterpool, "cs",
417 propname, propval));
419 svn_pool_destroy(iterpool);
422 return SVN_NO_ERROR;
425 svn_error_t *svn_ra_svn_start_list(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
427 return writebuf_write(conn, pool, "( ", 2);
430 svn_error_t *svn_ra_svn_end_list(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
432 return writebuf_write(conn, pool, ") ", 2);
435 svn_error_t *svn_ra_svn_flush(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
437 return writebuf_flush(conn, pool);
440 /* --- WRITING TUPLES --- */
442 static svn_error_t *vwrite_tuple(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
443 const char *fmt, va_list ap)
445 svn_boolean_t opt = FALSE;
446 svn_revnum_t rev;
447 const char *cstr;
448 const svn_string_t *str;
450 if (*fmt == '!')
451 fmt++;
452 else
453 SVN_ERR(svn_ra_svn_start_list(conn, pool));
454 for (; *fmt; fmt++)
456 if (*fmt == 'n' && !opt)
457 SVN_ERR(svn_ra_svn_write_number(conn, pool, va_arg(ap, apr_uint64_t)));
458 else if (*fmt == 'r')
460 rev = va_arg(ap, svn_revnum_t);
461 assert(opt || SVN_IS_VALID_REVNUM(rev));
462 if (SVN_IS_VALID_REVNUM(rev))
463 SVN_ERR(svn_ra_svn_write_number(conn, pool, rev));
465 else if (*fmt == 's')
467 str = va_arg(ap, const svn_string_t *);
468 assert(opt || str);
469 if (str)
470 SVN_ERR(svn_ra_svn_write_string(conn, pool, str));
472 else if (*fmt == 'c')
474 cstr = va_arg(ap, const char *);
475 assert(opt || cstr);
476 if (cstr)
477 SVN_ERR(svn_ra_svn_write_cstring(conn, pool, cstr));
479 else if (*fmt == 'w')
481 cstr = va_arg(ap, const char *);
482 assert(opt || cstr);
483 if (cstr)
484 SVN_ERR(svn_ra_svn_write_word(conn, pool, cstr));
486 else if (*fmt == 'b' && !opt)
488 cstr = va_arg(ap, svn_boolean_t) ? "true" : "false";
489 SVN_ERR(svn_ra_svn_write_word(conn, pool, cstr));
491 else if (*fmt == '?')
492 opt = TRUE;
493 else if (*fmt == '(' && !opt)
494 SVN_ERR(svn_ra_svn_start_list(conn, pool));
495 else if (*fmt == ')')
497 SVN_ERR(svn_ra_svn_end_list(conn, pool));
498 opt = FALSE;
500 else if (*fmt == '!' && !*(fmt + 1))
501 return SVN_NO_ERROR;
502 else
503 abort();
505 SVN_ERR(svn_ra_svn_end_list(conn, pool));
506 return SVN_NO_ERROR;
509 svn_error_t *svn_ra_svn_write_tuple(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
510 const char *fmt, ...)
512 svn_error_t *err;
513 va_list ap;
515 va_start(ap, fmt);
516 err = vwrite_tuple(conn, pool, fmt, ap);
517 va_end(ap);
518 return err;
521 /* --- READING DATA ITEMS --- */
523 /* Read LEN bytes from CONN into already-allocated structure ITEM.
524 * Afterwards, *ITEM is of type 'SVN_RA_SVN_STRING', and its string
525 * data is allocated in POOL. */
526 static svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
527 svn_ra_svn_item_t *item, apr_uint64_t len)
529 char readbuf[4096];
530 apr_size_t readbuf_len;
531 svn_stringbuf_t *stringbuf = svn_stringbuf_create("", pool);
533 /* We can't store strings longer than the maximum size of apr_size_t,
534 * so check for wrapping */
535 if (((apr_size_t) len) < len)
536 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
537 _("String length larger than maximum"));
539 while (len)
541 readbuf_len = len > sizeof(readbuf) ? sizeof(readbuf) : len;
543 SVN_ERR(readbuf_read(conn, pool, readbuf, readbuf_len));
544 /* Read into a stringbuf_t to so we don't allow the sender to allocate
545 * an arbitrary amount of memory without actually sending us that much
546 * data */
547 svn_stringbuf_appendbytes(stringbuf, readbuf, readbuf_len);
548 len -= readbuf_len;
551 item->kind = SVN_RA_SVN_STRING;
552 item->u.string = apr_palloc(pool, sizeof(*item->u.string));
553 item->u.string->data = stringbuf->data;
554 item->u.string->len = stringbuf->len;
556 return SVN_NO_ERROR;
559 /* Given the first non-whitespace character FIRST_CHAR, read an item
560 * into the already allocated structure ITEM. LEVEL should be set
561 * to 0 for the first call and is used to enforce a recurssion limit
562 * on the parser. */
563 static svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
564 svn_ra_svn_item_t *item, char first_char,
565 int level)
567 char c = first_char;
568 apr_uint64_t val, prev_val=0;
569 svn_stringbuf_t *str;
570 svn_ra_svn_item_t *listitem;
572 if (++level >= 64)
573 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
574 _("Too many nested items"));
577 /* Determine the item type and read it in. Make sure that c is the
578 * first character at the end of the item so we can test to make
579 * sure it's whitespace. */
580 if (apr_isdigit(c))
582 /* It's a number or a string. Read the number part, either way. */
583 val = c - '0';
584 while (1)
586 prev_val = val;
587 SVN_ERR(readbuf_getchar(conn, pool, &c));
588 if (!apr_isdigit(c))
589 break;
590 val = val * 10 + (c - '0');
591 if ((val / 10) != prev_val) /* val wrapped past maximum value */
592 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
593 _("Number is larger than maximum"));
595 if (c == ':')
597 /* It's a string. */
598 SVN_ERR(read_string(conn, pool, item, val));
599 SVN_ERR(readbuf_getchar(conn, pool, &c));
601 else
603 /* It's a number. */
604 item->kind = SVN_RA_SVN_NUMBER;
605 item->u.number = val;
608 else if (apr_isalpha(c))
610 /* It's a word. */
611 str = svn_stringbuf_ncreate(&c, 1, pool);
612 while (1)
614 SVN_ERR(readbuf_getchar(conn, pool, &c));
615 if (!apr_isalnum(c) && c != '-')
616 break;
617 svn_stringbuf_appendbytes(str, &c, 1);
619 item->kind = SVN_RA_SVN_WORD;
620 item->u.word = str->data;
622 else if (c == '(')
624 /* Read in the list items. */
625 item->kind = SVN_RA_SVN_LIST;
626 item->u.list = apr_array_make(pool, 0, sizeof(svn_ra_svn_item_t));
627 while (1)
629 SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
630 if (c == ')')
631 break;
632 listitem = apr_array_push(item->u.list);
633 SVN_ERR(read_item(conn, pool, listitem, c, level));
635 SVN_ERR(readbuf_getchar(conn, pool, &c));
638 if (!svn_iswhitespace(c))
639 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
640 _("Malformed network data"));
641 return SVN_NO_ERROR;
644 svn_error_t *svn_ra_svn_read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
645 svn_ra_svn_item_t **item)
647 char c;
649 /* Allocate space, read the first character, and then do the rest of
650 * the work. This makes sense because of the way lists are read. */
651 *item = apr_palloc(pool, sizeof(**item));
652 SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
653 return read_item(conn, pool, *item, c, 0);
656 svn_error_t *svn_ra_svn_skip_leading_garbage(svn_ra_svn_conn_t *conn,
657 apr_pool_t *pool)
659 return readbuf_skip_leading_garbage(conn, pool);
662 /* --- READING AND PARSING TUPLES --- */
664 /* Parse a tuple of svn_ra_svn_item_t *'s. Advance *FMT to the end of the
665 * tuple specification and advance AP by the corresponding arguments. */
666 static svn_error_t *vparse_tuple(apr_array_header_t *items, apr_pool_t *pool,
667 const char **fmt, va_list *ap)
669 int count, nesting_level;
670 svn_ra_svn_item_t *elt;
672 for (count = 0; **fmt && count < items->nelts; (*fmt)++, count++)
674 /* '?' just means the tuple may stop; skip past it. */
675 if (**fmt == '?')
676 (*fmt)++;
677 elt = &APR_ARRAY_IDX(items, count, svn_ra_svn_item_t);
678 if (**fmt == 'n' && elt->kind == SVN_RA_SVN_NUMBER)
679 *va_arg(*ap, apr_uint64_t *) = elt->u.number;
680 else if (**fmt == 'r' && elt->kind == SVN_RA_SVN_NUMBER)
681 *va_arg(*ap, svn_revnum_t *) = (svn_revnum_t) elt->u.number;
682 else if (**fmt == 's' && elt->kind == SVN_RA_SVN_STRING)
683 *va_arg(*ap, svn_string_t **) = elt->u.string;
684 else if (**fmt == 'c' && elt->kind == SVN_RA_SVN_STRING)
685 *va_arg(*ap, const char **) = elt->u.string->data;
686 else if (**fmt == 'w' && elt->kind == SVN_RA_SVN_WORD)
687 *va_arg(*ap, const char **) = elt->u.word;
688 else if (**fmt == 'b' && elt->kind == SVN_RA_SVN_WORD)
690 if (strcmp(elt->u.word, "true") == 0)
691 *va_arg(*ap, svn_boolean_t *) = TRUE;
692 else if (strcmp(elt->u.word, "false") == 0)
693 *va_arg(*ap, svn_boolean_t *) = FALSE;
694 else
695 break;
697 else if (**fmt == 'B' && elt->kind == SVN_RA_SVN_WORD)
699 if (strcmp(elt->u.word, "true") == 0)
700 *va_arg(*ap, apr_uint64_t *) = TRUE;
701 else if (strcmp(elt->u.word, "false") == 0)
702 *va_arg(*ap, apr_uint64_t *) = FALSE;
703 else
704 break;
706 else if (**fmt == 'l' && elt->kind == SVN_RA_SVN_LIST)
707 *va_arg(*ap, apr_array_header_t **) = elt->u.list;
708 else if (**fmt == '(' && elt->kind == SVN_RA_SVN_LIST)
710 (*fmt)++;
711 SVN_ERR(vparse_tuple(elt->u.list, pool, fmt, ap));
713 else if (**fmt == ')')
714 return SVN_NO_ERROR;
715 else
716 break;
718 if (**fmt == '?')
720 nesting_level = 0;
721 for (; **fmt; (*fmt)++)
723 switch (**fmt)
725 case '?':
726 break;
727 case 'r':
728 *va_arg(*ap, svn_revnum_t *) = SVN_INVALID_REVNUM;
729 break;
730 case 's':
731 *va_arg(*ap, svn_string_t **) = NULL;
732 break;
733 case 'c':
734 case 'w':
735 *va_arg(*ap, const char **) = NULL;
736 break;
737 case 'l':
738 *va_arg(*ap, apr_array_header_t **) = NULL;
739 break;
740 case 'B':
741 case 'n':
742 *va_arg(*ap, apr_uint64_t *) = SVN_RA_SVN_UNSPECIFIED_NUMBER;
743 break;
744 case '(':
745 nesting_level++;
746 break;
747 case ')':
748 if (--nesting_level < 0)
749 return SVN_NO_ERROR;
750 break;
751 default:
752 abort();
756 if (**fmt && **fmt != ')')
757 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
758 _("Malformed network data"));
759 return SVN_NO_ERROR;
762 svn_error_t *svn_ra_svn_parse_tuple(apr_array_header_t *list,
763 apr_pool_t *pool,
764 const char *fmt, ...)
766 svn_error_t *err;
767 va_list ap;
769 va_start(ap, fmt);
770 err = vparse_tuple(list, pool, &fmt, &ap);
771 va_end(ap);
772 return err;
775 svn_error_t *svn_ra_svn_read_tuple(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
776 const char *fmt, ...)
778 va_list ap;
779 svn_ra_svn_item_t *item;
780 svn_error_t *err;
782 SVN_ERR(svn_ra_svn_read_item(conn, pool, &item));
783 if (item->kind != SVN_RA_SVN_LIST)
784 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
785 _("Malformed network data"));
786 va_start(ap, fmt);
787 err = vparse_tuple(item->u.list, pool, &fmt, &ap);
788 va_end(ap);
789 return err;
792 svn_error_t *svn_ra_svn_parse_proplist(apr_array_header_t *list,
793 apr_pool_t *pool,
794 apr_hash_t **props)
796 char *name;
797 svn_string_t *value;
798 svn_ra_svn_item_t *elt;
799 int i;
801 *props = apr_hash_make(pool);
802 for (i = 0; i < list->nelts; i++)
804 elt = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
805 if (elt->kind != SVN_RA_SVN_LIST)
806 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
807 _("Proplist element not a list"));
808 SVN_ERR(svn_ra_svn_parse_tuple(elt->u.list, pool, "cs", &name, &value));
809 apr_hash_set(*props, name, APR_HASH_KEY_STRING, value);
812 return SVN_NO_ERROR;
816 /* --- READING AND WRITING COMMANDS AND RESPONSES --- */
818 svn_error_t *svn_ra_svn__handle_failure_status(apr_array_header_t *params,
819 apr_pool_t *pool)
821 const char *message, *file;
822 svn_error_t *err = NULL;
823 svn_ra_svn_item_t *elt;
824 int i;
825 apr_uint64_t apr_err, line;
826 apr_pool_t *subpool = svn_pool_create(pool);
828 if (params->nelts == 0)
829 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
830 _("Empty error list"));
832 /* Rebuild the error list from the end, to avoid reversing the order. */
833 for (i = params->nelts - 1; i >= 0; i--)
835 svn_pool_clear(subpool);
836 elt = &APR_ARRAY_IDX(params, i, svn_ra_svn_item_t);
837 if (elt->kind != SVN_RA_SVN_LIST)
838 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
839 _("Malformed error list"));
840 SVN_ERR(svn_ra_svn_parse_tuple(elt->u.list, subpool, "nccn", &apr_err,
841 &message, &file, &line));
842 /* The message field should have been optional, but we can't
843 easily change that, so "" means a nonexistent message. */
844 if (!*message)
845 message = NULL;
846 err = svn_error_create(apr_err, err, message);
847 err->file = apr_pstrdup(err->pool, file);
848 err->line = line;
851 svn_pool_destroy(subpool);
852 return err;
855 svn_error_t *svn_ra_svn_read_cmd_response(svn_ra_svn_conn_t *conn,
856 apr_pool_t *pool,
857 const char *fmt, ...)
859 va_list ap;
860 const char *status;
861 apr_array_header_t *params;
862 svn_error_t *err;
864 SVN_ERR(svn_ra_svn_read_tuple(conn, pool, "wl", &status, &params));
865 if (strcmp(status, "success") == 0)
867 va_start(ap, fmt);
868 err = vparse_tuple(params, pool, &fmt, &ap);
869 va_end(ap);
870 return err;
872 else if (strcmp(status, "failure") == 0)
874 return svn_ra_svn__handle_failure_status(params, pool);
877 return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
878 _("Unknown status '%s' in command response"),
879 status);
882 svn_error_t *svn_ra_svn_handle_commands(svn_ra_svn_conn_t *conn,
883 apr_pool_t *pool,
884 const svn_ra_svn_cmd_entry_t *commands,
885 void *baton)
887 apr_pool_t *subpool = svn_pool_create(pool);
888 const char *cmdname;
889 int i;
890 svn_error_t *err, *write_err;
891 apr_array_header_t *params;
893 while (1)
895 svn_pool_clear(subpool);
896 SVN_ERR(svn_ra_svn_read_tuple(conn, subpool, "wl", &cmdname, &params));
897 for (i = 0; commands[i].cmdname; i++)
899 if (strcmp(cmdname, commands[i].cmdname) == 0)
900 break;
902 if (commands[i].cmdname)
903 err = (*commands[i].handler)(conn, subpool, params, baton);
904 else
906 err = svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL,
907 _("Unknown command '%s'"), cmdname);
908 err = svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, err, NULL);
911 if (err && err->apr_err == SVN_ERR_RA_SVN_CMD_ERR)
913 write_err = svn_ra_svn_write_cmd_failure(conn, subpool, err->child);
914 svn_error_clear(err);
915 if (write_err)
916 return write_err;
918 else if (err)
919 return err;
921 if (commands[i].terminate)
922 break;
924 svn_pool_destroy(subpool);
925 return SVN_NO_ERROR;
928 svn_error_t *svn_ra_svn_write_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
929 const char *cmdname, const char *fmt, ...)
931 va_list ap;
932 svn_error_t *err;
934 SVN_ERR(svn_ra_svn_start_list(conn, pool));
935 SVN_ERR(svn_ra_svn_write_word(conn, pool, cmdname));
936 va_start(ap, fmt);
937 err = vwrite_tuple(conn, pool, fmt, ap);
938 va_end(ap);
939 if (err)
940 return err;
941 SVN_ERR(svn_ra_svn_end_list(conn, pool));
942 return SVN_NO_ERROR;
945 svn_error_t *svn_ra_svn_write_cmd_response(svn_ra_svn_conn_t *conn,
946 apr_pool_t *pool,
947 const char *fmt, ...)
949 va_list ap;
950 svn_error_t *err;
952 SVN_ERR(svn_ra_svn_start_list(conn, pool));
953 SVN_ERR(svn_ra_svn_write_word(conn, pool, "success"));
954 va_start(ap, fmt);
955 err = vwrite_tuple(conn, pool, fmt, ap);
956 va_end(ap);
957 if (err)
958 return err;
959 SVN_ERR(svn_ra_svn_end_list(conn, pool));
960 return SVN_NO_ERROR;
963 svn_error_t *svn_ra_svn_write_cmd_failure(svn_ra_svn_conn_t *conn,
964 apr_pool_t *pool, svn_error_t *err)
966 SVN_ERR(svn_ra_svn_start_list(conn, pool));
967 SVN_ERR(svn_ra_svn_write_word(conn, pool, "failure"));
968 SVN_ERR(svn_ra_svn_start_list(conn, pool));
969 for (; err; err = err->child)
971 /* The message string should have been optional, but we can't
972 easily change that, so marshal nonexistent messages as "". */
973 SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "nccn",
974 (apr_uint64_t) err->apr_err,
975 err->message ? err->message : "",
976 err->file, (apr_uint64_t) err->line));
978 SVN_ERR(svn_ra_svn_end_list(conn, pool));
979 SVN_ERR(svn_ra_svn_end_list(conn, pool));
980 return SVN_NO_ERROR;