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 * ====================================================================
24 #define APR_WANT_STRFUNC
26 #include <apr_general.h>
28 #include <apr_strings.h>
29 #include <apr_network_io.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"
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
,
50 svn_ra_svn_conn_t
*conn
= apr_palloc(pool
, sizeof(*conn
));
52 assert((sock
&& !in_file
&& !out_file
) || (!sock
&& in_file
&& out_file
));
55 conn
->encrypted
= FALSE
;
58 conn
->read_ptr
= conn
->read_buf
;
59 conn
->read_end
= conn
->read_buf
;
61 conn
->block_handler
= NULL
;
62 conn
->block_baton
= NULL
;
63 conn
->capabilities
= apr_hash_make(pool
);
67 conn
->stream
= svn_ra_svn__stream_from_sock(sock
, pool
);
69 conn
->stream
= svn_ra_svn__stream_from_files(in_file
, out_file
, pool
);
74 svn_error_t
*svn_ra_svn_set_capabilities(svn_ra_svn_conn_t
*conn
,
75 apr_array_header_t
*list
)
78 svn_ra_svn_item_t
*item
;
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
);
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
);
101 svn_ra_svn__set_block_handler(svn_ra_svn_conn_t
*conn
,
102 ra_svn_block_handler_t handler
,
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
,
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
,
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
;
140 apr_pool_t
*subpool
= NULL
;
141 svn_ra_svn__session_baton_t
*session
= conn
->session
;
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
));
156 subpool
= svn_pool_create(pool
);
158 svn_pool_clear(subpool
);
159 SVN_ERR(conn
->block_handler(conn
, subpool
, conn
->block_baton
));
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
);
175 svn_pool_destroy(subpool
);
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. */
186 SVN_ERR(writebuf_output(conn
, pool
, conn
->write_buf
, write_pos
));
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
));
205 writebuf_push(conn
, data
, end
);
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
, ...)
219 str
= apr_pvsprintf(pool
, fmt
, 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
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
));
252 return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED
, NULL
,
253 _("Connection closed unexpectedly"));
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
);
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
)
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
;
282 static svn_error_t
*readbuf_getchar(svn_ra_svn_conn_t
*conn
, apr_pool_t
*pool
,
285 if (conn
->read_ptr
== conn
->read_end
)
286 SVN_ERR(readbuf_fill(conn
, pool
));
287 *result
= *conn
->read_ptr
++;
291 static svn_error_t
*readbuf_getchar_skip_whitespace(svn_ra_svn_conn_t
*conn
,
296 SVN_ERR(readbuf_getchar(conn
, pool
, result
));
297 while (svn_iswhitespace(*result
));
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
;
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
));
315 SVN_ERR(readbuf_input(conn
, data
, &count
, pool
));
321 /* The remaining amount to read is small; fill the buffer and
323 SVN_ERR(readbuf_fill(conn
, pool
));
324 data
= readbuf_drain(conn
, data
, end
);
330 static svn_error_t
*readbuf_skip_leading_garbage(svn_ra_svn_conn_t
*conn
,
333 char buf
[256]; /* Must be smaller than sizeof(conn->read_buf) - 1. */
336 svn_boolean_t lparen
= FALSE
;
338 assert(conn
->read_ptr
== conn
->read_end
);
341 /* Read some data directly from the connection input source. */
343 SVN_ERR(readbuf_input(conn
, buf
, &len
, pool
));
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
))
352 lparen
= (*p
== '(');
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
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
);
368 /* --- WRITING DATA ITEMS --- */
370 svn_error_t
*svn_ra_svn_write_number(svn_ra_svn_conn_t
*conn
, apr_pool_t
*pool
,
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));
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
,
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
;
404 const char *propname
;
405 svn_string_t
*propval
;
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
);
416 SVN_ERR(svn_ra_svn_write_tuple(conn
, iterpool
, "cs",
419 svn_pool_destroy(iterpool
);
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
;
448 const svn_string_t
*str
;
453 SVN_ERR(svn_ra_svn_start_list(conn
, pool
));
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
*);
470 SVN_ERR(svn_ra_svn_write_string(conn
, pool
, str
));
472 else if (*fmt
== 'c')
474 cstr
= va_arg(ap
, const char *);
477 SVN_ERR(svn_ra_svn_write_cstring(conn
, pool
, cstr
));
479 else if (*fmt
== 'w')
481 cstr
= va_arg(ap
, const char *);
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
== '?')
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
));
500 else if (*fmt
== '!' && !*(fmt
+ 1))
505 SVN_ERR(svn_ra_svn_end_list(conn
, pool
));
509 svn_error_t
*svn_ra_svn_write_tuple(svn_ra_svn_conn_t
*conn
, apr_pool_t
*pool
,
510 const char *fmt
, ...)
516 err
= vwrite_tuple(conn
, pool
, fmt
, ap
);
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
)
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"));
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
547 svn_stringbuf_appendbytes(stringbuf
, readbuf
, 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
;
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
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
,
568 apr_uint64_t val
, prev_val
=0;
569 svn_stringbuf_t
*str
;
570 svn_ra_svn_item_t
*listitem
;
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. */
582 /* It's a number or a string. Read the number part, either way. */
587 SVN_ERR(readbuf_getchar(conn
, pool
, &c
));
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"));
598 SVN_ERR(read_string(conn
, pool
, item
, val
));
599 SVN_ERR(readbuf_getchar(conn
, pool
, &c
));
604 item
->kind
= SVN_RA_SVN_NUMBER
;
605 item
->u
.number
= val
;
608 else if (apr_isalpha(c
))
611 str
= svn_stringbuf_ncreate(&c
, 1, pool
);
614 SVN_ERR(readbuf_getchar(conn
, pool
, &c
));
615 if (!apr_isalnum(c
) && c
!= '-')
617 svn_stringbuf_appendbytes(str
, &c
, 1);
619 item
->kind
= SVN_RA_SVN_WORD
;
620 item
->u
.word
= str
->data
;
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
));
629 SVN_ERR(readbuf_getchar_skip_whitespace(conn
, pool
, &c
));
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"));
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
)
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
,
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. */
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
;
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
;
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
)
711 SVN_ERR(vparse_tuple(elt
->u
.list
, pool
, fmt
, ap
));
713 else if (**fmt
== ')')
721 for (; **fmt
; (*fmt
)++)
728 *va_arg(*ap
, svn_revnum_t
*) = SVN_INVALID_REVNUM
;
731 *va_arg(*ap
, svn_string_t
**) = NULL
;
735 *va_arg(*ap
, const char **) = NULL
;
738 *va_arg(*ap
, apr_array_header_t
**) = NULL
;
742 *va_arg(*ap
, apr_uint64_t
*) = SVN_RA_SVN_UNSPECIFIED_NUMBER
;
748 if (--nesting_level
< 0)
756 if (**fmt
&& **fmt
!= ')')
757 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA
, NULL
,
758 _("Malformed network data"));
762 svn_error_t
*svn_ra_svn_parse_tuple(apr_array_header_t
*list
,
764 const char *fmt
, ...)
770 err
= vparse_tuple(list
, pool
, &fmt
, &ap
);
775 svn_error_t
*svn_ra_svn_read_tuple(svn_ra_svn_conn_t
*conn
, apr_pool_t
*pool
,
776 const char *fmt
, ...)
779 svn_ra_svn_item_t
*item
;
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"));
787 err
= vparse_tuple(item
->u
.list
, pool
, &fmt
, &ap
);
792 svn_error_t
*svn_ra_svn_parse_proplist(apr_array_header_t
*list
,
798 svn_ra_svn_item_t
*elt
;
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
);
816 /* --- READING AND WRITING COMMANDS AND RESPONSES --- */
818 svn_error_t
*svn_ra_svn__handle_failure_status(apr_array_header_t
*params
,
821 const char *message
, *file
;
822 svn_error_t
*err
= NULL
;
823 svn_ra_svn_item_t
*elt
;
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. */
846 err
= svn_error_create(apr_err
, err
, message
);
847 err
->file
= apr_pstrdup(err
->pool
, file
);
851 svn_pool_destroy(subpool
);
855 svn_error_t
*svn_ra_svn_read_cmd_response(svn_ra_svn_conn_t
*conn
,
857 const char *fmt
, ...)
861 apr_array_header_t
*params
;
864 SVN_ERR(svn_ra_svn_read_tuple(conn
, pool
, "wl", &status
, ¶ms
));
865 if (strcmp(status
, "success") == 0)
868 err
= vparse_tuple(params
, pool
, &fmt
, &ap
);
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"),
882 svn_error_t
*svn_ra_svn_handle_commands(svn_ra_svn_conn_t
*conn
,
884 const svn_ra_svn_cmd_entry_t
*commands
,
887 apr_pool_t
*subpool
= svn_pool_create(pool
);
890 svn_error_t
*err
, *write_err
;
891 apr_array_header_t
*params
;
895 svn_pool_clear(subpool
);
896 SVN_ERR(svn_ra_svn_read_tuple(conn
, subpool
, "wl", &cmdname
, ¶ms
));
897 for (i
= 0; commands
[i
].cmdname
; i
++)
899 if (strcmp(cmdname
, commands
[i
].cmdname
) == 0)
902 if (commands
[i
].cmdname
)
903 err
= (*commands
[i
].handler
)(conn
, subpool
, params
, baton
);
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
);
921 if (commands
[i
].terminate
)
924 svn_pool_destroy(subpool
);
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
, ...)
934 SVN_ERR(svn_ra_svn_start_list(conn
, pool
));
935 SVN_ERR(svn_ra_svn_write_word(conn
, pool
, cmdname
));
937 err
= vwrite_tuple(conn
, pool
, fmt
, ap
);
941 SVN_ERR(svn_ra_svn_end_list(conn
, pool
));
945 svn_error_t
*svn_ra_svn_write_cmd_response(svn_ra_svn_conn_t
*conn
,
947 const char *fmt
, ...)
952 SVN_ERR(svn_ra_svn_start_list(conn
, pool
));
953 SVN_ERR(svn_ra_svn_write_word(conn
, pool
, "success"));
955 err
= vwrite_tuple(conn
, pool
, fmt
, ap
);
959 SVN_ERR(svn_ra_svn_end_list(conn
, pool
));
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
));