2 * mergeinfo.c : routines for requesting and parsing mergeinfo reports
4 * ====================================================================
5 * Copyright (c) 2006 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 <apr_pools.h>
22 #include <apr_tables.h>
23 #include <apr_strings.h>
26 #include <ne_socket.h>
28 #include "svn_error.h"
29 #include "svn_pools.h"
32 #include "svn_mergeinfo.h"
33 #include "private/svn_dav_protocol.h"
34 #include "../libsvn_ra/ra_loader.h"
38 /* Baton for accumulating mergeinfo. RESULT stores the final
39 mergeinfo hash result we are going to hand back to the caller of
40 get_mergeinfo. curr_path and curr_info contain the value of the
41 CDATA from the mergeinfo items as we get them from the server. */
43 struct mergeinfo_baton
46 const char *curr_path
;
47 svn_stringbuf_t
*curr_info
;
52 static const svn_ra_neon__xml_elm_t mergeinfo_report_elements
[] =
54 { SVN_XML_NAMESPACE
, SVN_DAV__MERGEINFO_REPORT
, ELEM_mergeinfo_report
, 0 },
55 { SVN_XML_NAMESPACE
, SVN_DAV__MERGEINFO_ITEM
, ELEM_mergeinfo_item
, 0 },
56 { SVN_XML_NAMESPACE
, SVN_DAV__MERGEINFO_PATH
, ELEM_mergeinfo_path
,
57 SVN_RA_NEON__XML_CDATA
},
58 { SVN_XML_NAMESPACE
, SVN_DAV__MERGEINFO_INFO
, ELEM_mergeinfo_info
,
59 SVN_RA_NEON__XML_CDATA
},
64 start_element(int *elem
, void *baton
, int parent_state
, const char *nspace
,
65 const char *elt_name
, const char **atts
)
67 struct mergeinfo_baton
*mb
= baton
;
69 const svn_ra_neon__xml_elm_t
*elm
70 = svn_ra_neon__lookup_xml_elem(mergeinfo_report_elements
, nspace
,
74 *elem
= NE_XML_DECLINE
;
78 if (parent_state
== ELEM_root
)
80 /* If we're at the root of the tree, the element has to be the editor
82 if (elm
->id
!= ELEM_mergeinfo_report
)
83 return UNEXPECTED_ELEMENT(nspace
, elt_name
);
86 if (elm
->id
== ELEM_mergeinfo_item
)
88 svn_stringbuf_setempty(mb
->curr_info
);
99 end_element(void *baton
, int state
, const char *nspace
, const char *elt_name
)
101 struct mergeinfo_baton
*mb
= baton
;
103 const svn_ra_neon__xml_elm_t
*elm
104 = svn_ra_neon__lookup_xml_elem(mergeinfo_report_elements
, nspace
,
107 return UNEXPECTED_ELEMENT(nspace
, elt_name
);
109 if (elm
->id
== ELEM_mergeinfo_item
)
111 if (mb
->curr_info
&& mb
->curr_path
)
113 apr_hash_t
*path_mergeinfo
;
115 mb
->err
= svn_mergeinfo_parse(&path_mergeinfo
, mb
->curr_info
->data
,
119 apr_hash_set(mb
->result
, mb
->curr_path
, APR_HASH_KEY_STRING
,
128 cdata_handler(void *baton
, int state
, const char *cdata
, size_t len
)
130 struct mergeinfo_baton
*mb
= baton
;
131 apr_size_t nlen
= len
;
135 case ELEM_mergeinfo_path
:
136 mb
->curr_path
= apr_pstrndup(mb
->pool
, cdata
, nlen
);
139 case ELEM_mergeinfo_info
:
141 svn_stringbuf_appendbytes(mb
->curr_info
, cdata
, nlen
);
152 /* Request a mergeinfo-report from the URL attached to SESSION,
153 and fill in the MERGEINFO hash with the results. */
155 svn_ra_neon__get_mergeinfo(svn_ra_session_t
*session
,
156 apr_hash_t
**mergeinfo
,
157 const apr_array_header_t
*paths
,
158 svn_revnum_t revision
,
159 svn_mergeinfo_inheritance_t inherit
,
163 svn_ra_neon__session_t
*ras
= session
->priv
;
164 svn_stringbuf_t
*request_body
= svn_stringbuf_create("", pool
);
165 struct mergeinfo_baton mb
;
166 svn_string_t bc_url
, bc_relative
;
167 const char *final_bc_url
;
169 static const char minfo_report_head
[] =
170 "<S:" SVN_DAV__MERGEINFO_REPORT
" xmlns:S=\"" SVN_XML_NAMESPACE
"\">"
173 static const char minfo_report_tail
[] =
174 "</S:" SVN_DAV__MERGEINFO_REPORT
">" DEBUG_CR
;
176 /* Construct the request body. */
177 svn_stringbuf_appendcstr(request_body
, minfo_report_head
);
178 svn_stringbuf_appendcstr(request_body
,
181 "</S:revision>", revision
));
182 svn_stringbuf_appendcstr(request_body
,
186 svn_inheritance_to_word(inherit
)));
189 for (i
= 0; i
< paths
->nelts
; i
++)
191 const char *this_path
=
192 apr_xml_quote_string(pool
,
193 ((const char **)paths
->elts
)[i
],
195 svn_stringbuf_appendcstr(request_body
, "<S:path>");
196 svn_stringbuf_appendcstr(request_body
, this_path
);
197 svn_stringbuf_appendcstr(request_body
, "</S:path>");
201 svn_stringbuf_appendcstr(request_body
, minfo_report_tail
);
205 mb
.curr_info
= svn_stringbuf_create("", pool
);
206 mb
.result
= apr_hash_make(pool
);
207 mb
.err
= SVN_NO_ERROR
;
209 /* ras's URL may not exist in HEAD, and thus it's not safe to send
210 it as the main argument to the REPORT request; it might cause
211 dav_get_resource() to choke on the server. So instead, we pass a
212 baseline-collection URL, which we get from END. */
213 SVN_ERR(svn_ra_neon__get_baseline_info(NULL
, &bc_url
, &bc_relative
, NULL
,
214 ras
, ras
->url
->data
, revision
,
216 final_bc_url
= svn_path_url_add_component(bc_url
.data
, bc_relative
.data
,
219 SVN_ERR(svn_ra_neon__parsed_request(ras
,
233 if (mb
.err
== SVN_NO_ERROR
)
234 *mergeinfo
= mb
.result
;