#define APR_WANT_STRFUNC
#include <apr_want.h>
#include <apr_pools.h>
#include <apr_tables.h>
#include <apr_strings.h>
#include <apr_xml.h>
#include <ne_socket.h>
#include "svn_error.h"
#include "svn_pools.h"
#include "svn_path.h"
#include "svn_xml.h"
#include "../libsvn_ra/ra_loader.h"
#include "ra_dav.h"
struct log_baton
{
apr_pool_t *subpool;
svn_revnum_t revision;
const char *author;
const char *date;
const char *msg;
apr_hash_t *changed_paths;
svn_log_changed_path_t *this_path_item;
svn_log_message_receiver_t receiver;
void *receiver_baton;
int limit;
int count;
svn_boolean_t limit_compat_bailout;
svn_error_t *err;
};
static void
reset_log_item(struct log_baton *lb)
{
lb->revision = SVN_INVALID_REVNUM;
lb->author = NULL;
lb->date = NULL;
lb->msg = NULL;
lb->changed_paths = NULL;
svn_pool_clear(lb->subpool);
}
static int
log_validate(void *userdata, svn_ra_dav__xml_elmid parent,
svn_ra_dav__xml_elmid child)
{
return SVN_RA_DAV__XML_VALID;
}
static int
log_start_element(void *userdata,
const svn_ra_dav__xml_elm_t *elm,
const char **atts)
{
struct log_baton *lb = userdata;
const char *copyfrom_path, *copyfrom_revstr;
svn_revnum_t copyfrom_rev;
switch (elm->id)
{
case ELEM_added_path:
case ELEM_replaced_path:
case ELEM_deleted_path:
case ELEM_modified_path:
lb->this_path_item = apr_pcalloc(lb->subpool,
sizeof(*(lb->this_path_item)));
lb->this_path_item->copyfrom_rev = SVN_INVALID_REVNUM;
if ((elm->id == ELEM_added_path) || (elm->id == ELEM_replaced_path))
{
lb->this_path_item->action
= (elm->id == ELEM_added_path) ? 'A' : 'R';
copyfrom_path = svn_xml_get_attr_value("copyfrom-path", atts);
copyfrom_revstr = svn_xml_get_attr_value("copyfrom-rev", atts);
if (copyfrom_path && copyfrom_revstr
&& (SVN_IS_VALID_REVNUM
(copyfrom_rev = SVN_STR_TO_REV(copyfrom_revstr))))
{
lb->this_path_item->copyfrom_path = apr_pstrdup(lb->subpool,
copyfrom_path);
lb->this_path_item->copyfrom_rev = copyfrom_rev;
}
}
else if (elm->id == ELEM_deleted_path)
{
lb->this_path_item->action = 'D';
}
else
{
lb->this_path_item->action = 'M';
}
break;
default:
lb->this_path_item = NULL;
break;
}
return SVN_RA_DAV__XML_VALID;
}
static int
log_end_element(void *userdata,
const svn_ra_dav__xml_elm_t *elm,
const char *cdata)
{
struct log_baton *lb = userdata;
switch (elm->id)
{
case ELEM_version_name:
lb->revision = SVN_STR_TO_REV(cdata);
break;
case ELEM_creator_displayname:
lb->author = apr_pstrdup(lb->subpool, cdata);
break;
case ELEM_log_date:
lb->date = apr_pstrdup(lb->subpool, cdata);
break;
case ELEM_added_path:
case ELEM_replaced_path:
case ELEM_deleted_path:
case ELEM_modified_path:
{
char *path = apr_pstrdup(lb->subpool, cdata);
if (! lb->changed_paths)
lb->changed_paths = apr_hash_make(lb->subpool);
apr_hash_set(lb->changed_paths, path, APR_HASH_KEY_STRING,
lb->this_path_item);
break;
}
case ELEM_comment:
lb->msg = apr_pstrdup(lb->subpool, cdata);
break;
case ELEM_log_item:
{
svn_error_t *err;
if (lb->limit && (++lb->count > lb->limit))
{
lb->err = SVN_NO_ERROR;
lb->limit_compat_bailout = TRUE;
return SVN_RA_DAV__XML_INVALID;
}
err = (*(lb->receiver))(lb->receiver_baton,
lb->changed_paths,
lb->revision,
lb->author,
lb->date,
lb->msg,
lb->subpool);
reset_log_item(lb);
if (err)
{
if (lb->err == NULL)
lb->err = err;
else
svn_error_clear(err);
return SVN_RA_DAV__XML_INVALID;
}
}
break;
case ELEM_log_report:
{
}
break;
}
return SVN_RA_DAV__XML_VALID;
}
svn_error_t * svn_ra_dav__get_log(svn_ra_session_t *session,
const apr_array_header_t *paths,
svn_revnum_t start,
svn_revnum_t end,
int limit,
svn_boolean_t discover_changed_paths,
svn_boolean_t strict_node_history,
svn_log_message_receiver_t receiver,
void *receiver_baton,
apr_pool_t *pool)
{
int i;
svn_ra_dav__session_t *ras = session->priv;
svn_stringbuf_t *request_body = svn_stringbuf_create("", pool);
struct log_baton lb;
svn_string_t bc_url, bc_relative;
const char *final_bc_url;
svn_revnum_t use_rev;
svn_error_t *err;
static const char log_request_head[]
= "<S:log-report xmlns:S=\"" SVN_XML_NAMESPACE "\">" DEBUG_CR;
static const char log_request_tail[] = "</S:log-report>" DEBUG_CR;
static const svn_ra_dav__xml_elm_t log_report_elements[] =
{
{ SVN_XML_NAMESPACE, "log-report", ELEM_log_report, 0 },
{ SVN_XML_NAMESPACE, "log-item", ELEM_log_item, 0 },
{ SVN_XML_NAMESPACE, "date", ELEM_log_date, SVN_RA_DAV__XML_CDATA },
{ SVN_XML_NAMESPACE, "added-path", ELEM_added_path,
SVN_RA_DAV__XML_CDATA },
{ SVN_XML_NAMESPACE, "deleted-path", ELEM_deleted_path,
SVN_RA_DAV__XML_CDATA },
{ SVN_XML_NAMESPACE, "modified-path", ELEM_modified_path,
SVN_RA_DAV__XML_CDATA },
{ SVN_XML_NAMESPACE, "replaced-path", ELEM_replaced_path,
SVN_RA_DAV__XML_CDATA },
{ "DAV:", "version-name", ELEM_version_name, SVN_RA_DAV__XML_CDATA },
{ "DAV:", "creator-displayname", ELEM_creator_displayname,
SVN_RA_DAV__XML_CDATA },
{ "DAV:", "comment", ELEM_comment, SVN_RA_DAV__XML_CDATA },
{ NULL }
};
svn_stringbuf_appendcstr(request_body, log_request_head);
svn_stringbuf_appendcstr(request_body,
apr_psprintf(pool,
"<S:start-revision>%ld"
"</S:start-revision>", start));
svn_stringbuf_appendcstr(request_body,
apr_psprintf(pool,
"<S:end-revision>%ld"
"</S:end-revision>", end));
if (limit)
{
svn_stringbuf_appendcstr(request_body,
apr_psprintf(pool,
"<S:limit>%d</S:limit>", limit));
}
if (discover_changed_paths)
{
svn_stringbuf_appendcstr(request_body,
apr_psprintf(pool,
"<S:discover-changed-paths/>"));
}
if (strict_node_history)
{
svn_stringbuf_appendcstr(request_body,
apr_psprintf(pool,
"<S:strict-node-history/>"));
}
if (paths)
{
for (i = 0; i < paths->nelts; i++)
{
const char *this_path =
apr_xml_quote_string(pool,
((const char **)paths->elts)[i],
0);
svn_stringbuf_appendcstr(request_body, "<S:path>");
svn_stringbuf_appendcstr(request_body, this_path);
svn_stringbuf_appendcstr(request_body, "</S:path>");
}
}
svn_stringbuf_appendcstr(request_body, log_request_tail);
lb.receiver = receiver;
lb.receiver_baton = receiver_baton;
lb.subpool = svn_pool_create(pool);
lb.err = NULL;
lb.limit = limit;
lb.count = 0;
lb.limit_compat_bailout = FALSE;
reset_log_item(&lb);
use_rev = (start > end) ? start : end;
SVN_ERR(svn_ra_dav__get_baseline_info(NULL, &bc_url, &bc_relative, NULL,
ras->sess, ras->url->data, use_rev,
pool));
final_bc_url = svn_path_url_add_component(bc_url.data, bc_relative.data,
pool);
err = svn_ra_dav__parsed_request_compat(ras->sess,
"REPORT",
final_bc_url,
request_body->data,
0,
NULL,
log_report_elements,
log_validate,
log_start_element,
log_end_element,
&lb,
NULL,
NULL,
FALSE,
pool);
if (lb.err)
{
if (err)
svn_error_clear(err);
return lb.err;
}
svn_pool_destroy(lb.subpool);
if (err && lb.limit_compat_bailout)
return SVN_NO_ERROR;
return err;
}