1251881Speter/* 2251881Speter * blame.c : entry point for blame RA functions for ra_serf 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#include <apr_uri.h> 25251881Speter#include <serf.h> 26251881Speter 27251881Speter#include "svn_hash.h" 28251881Speter#include "svn_pools.h" 29251881Speter#include "svn_ra.h" 30251881Speter#include "svn_dav.h" 31251881Speter#include "svn_xml.h" 32251881Speter#include "svn_config.h" 33251881Speter#include "svn_delta.h" 34251881Speter#include "svn_path.h" 35251881Speter#include "svn_base64.h" 36251881Speter#include "svn_props.h" 37251881Speter 38251881Speter#include "svn_private_config.h" 39251881Speter 40251881Speter#include "private/svn_string_private.h" 41251881Speter 42251881Speter#include "ra_serf.h" 43251881Speter#include "../libsvn_ra/ra_loader.h" 44251881Speter 45251881Speter 46251881Speter/* 47251881Speter * This enum represents the current state of our XML parsing for a REPORT. 48251881Speter */ 49251881Spetertypedef enum blame_state_e { 50251881Speter INITIAL = 0, 51251881Speter FILE_REVS_REPORT, 52251881Speter FILE_REV, 53251881Speter REV_PROP, 54251881Speter SET_PROP, 55251881Speter REMOVE_PROP, 56251881Speter MERGED_REVISION, 57251881Speter TXDELTA 58251881Speter} blame_state_e; 59251881Speter 60251881Speter 61251881Spetertypedef struct blame_context_t { 62251881Speter /* pool passed to get_file_revs */ 63251881Speter apr_pool_t *pool; 64251881Speter 65251881Speter /* parameters set by our caller */ 66251881Speter const char *path; 67251881Speter svn_revnum_t start; 68251881Speter svn_revnum_t end; 69251881Speter svn_boolean_t include_merged_revisions; 70251881Speter 71251881Speter /* blame handler and baton */ 72251881Speter svn_file_rev_handler_t file_rev; 73251881Speter void *file_rev_baton; 74251881Speter 75251881Speter /* As we parse each FILE_REV, we collect data in these variables: 76251881Speter property changes and new content. STREAM is valid when we're 77251881Speter in the TXDELTA state, processing the incoming cdata. */ 78251881Speter apr_hash_t *rev_props; 79251881Speter apr_array_header_t *prop_diffs; 80251881Speter apr_pool_t *state_pool; /* put property stuff in here */ 81251881Speter 82251881Speter svn_stream_t *stream; 83251881Speter 84251881Speter} blame_context_t; 85251881Speter 86251881Speter 87251881Speter#define D_ "DAV:" 88251881Speter#define S_ SVN_XML_NAMESPACE 89251881Speterstatic const svn_ra_serf__xml_transition_t blame_ttable[] = { 90251881Speter { INITIAL, S_, "file-revs-report", FILE_REVS_REPORT, 91251881Speter FALSE, { NULL }, FALSE }, 92251881Speter 93251881Speter { FILE_REVS_REPORT, S_, "file-rev", FILE_REV, 94251881Speter FALSE, { "path", "rev", NULL }, TRUE }, 95251881Speter 96251881Speter { FILE_REV, S_, "rev-prop", REV_PROP, 97251881Speter TRUE, { "name", "?encoding", NULL }, TRUE }, 98251881Speter 99251881Speter { FILE_REV, S_, "set-prop", SET_PROP, 100251881Speter TRUE, { "name", "?encoding", NULL }, TRUE }, 101251881Speter 102251881Speter { FILE_REV, S_, "remove-prop", REMOVE_PROP, 103251881Speter FALSE, { "name", NULL }, TRUE }, 104251881Speter 105251881Speter { FILE_REV, S_, "merged-revision", MERGED_REVISION, 106251881Speter FALSE, { NULL }, TRUE }, 107251881Speter 108251881Speter { FILE_REV, S_, "txdelta", TXDELTA, 109251881Speter FALSE, { NULL }, TRUE }, 110251881Speter 111251881Speter { 0 } 112251881Speter}; 113251881Speter 114251881Speter 115251881Speter/* Conforms to svn_ra_serf__xml_opened_t */ 116251881Speterstatic svn_error_t * 117251881Speterblame_opened(svn_ra_serf__xml_estate_t *xes, 118251881Speter void *baton, 119251881Speter int entered_state, 120251881Speter const svn_ra_serf__dav_props_t *tag, 121251881Speter apr_pool_t *scratch_pool) 122251881Speter{ 123251881Speter blame_context_t *blame_ctx = baton; 124251881Speter 125251881Speter if (entered_state == FILE_REV) 126251881Speter { 127251881Speter apr_pool_t *state_pool = svn_ra_serf__xml_state_pool(xes); 128251881Speter 129251881Speter /* Child elements will store properties in these structures. */ 130251881Speter blame_ctx->rev_props = apr_hash_make(state_pool); 131251881Speter blame_ctx->prop_diffs = apr_array_make(state_pool, 132251881Speter 5, sizeof(svn_prop_t)); 133251881Speter blame_ctx->state_pool = state_pool; 134251881Speter 135251881Speter /* Clear this, so we can detect the absence of a TXDELTA. */ 136251881Speter blame_ctx->stream = NULL; 137251881Speter } 138251881Speter else if (entered_state == TXDELTA) 139251881Speter { 140251881Speter apr_pool_t *state_pool = svn_ra_serf__xml_state_pool(xes); 141251881Speter apr_hash_t *gathered = svn_ra_serf__xml_gather_since(xes, FILE_REV); 142251881Speter const char *path; 143251881Speter const char *rev; 144251881Speter const char *merged_revision; 145251881Speter svn_txdelta_window_handler_t txdelta; 146251881Speter void *txdelta_baton; 147251881Speter 148251881Speter path = svn_hash_gets(gathered, "path"); 149251881Speter rev = svn_hash_gets(gathered, "rev"); 150251881Speter merged_revision = svn_hash_gets(gathered, "merged-revision"); 151251881Speter 152251881Speter SVN_ERR(blame_ctx->file_rev(blame_ctx->file_rev_baton, 153251881Speter path, SVN_STR_TO_REV(rev), 154251881Speter blame_ctx->rev_props, 155251881Speter merged_revision != NULL, 156251881Speter &txdelta, &txdelta_baton, 157251881Speter blame_ctx->prop_diffs, 158251881Speter state_pool)); 159251881Speter 160251881Speter blame_ctx->stream = svn_base64_decode(svn_txdelta_parse_svndiff( 161251881Speter txdelta, txdelta_baton, 162251881Speter TRUE /* error_on_early_close */, 163251881Speter state_pool), 164251881Speter state_pool); 165251881Speter } 166251881Speter 167251881Speter return SVN_NO_ERROR; 168251881Speter} 169251881Speter 170251881Speter 171251881Speter/* Conforms to svn_ra_serf__xml_closed_t */ 172251881Speterstatic svn_error_t * 173251881Speterblame_closed(svn_ra_serf__xml_estate_t *xes, 174251881Speter void *baton, 175251881Speter int leaving_state, 176251881Speter const svn_string_t *cdata, 177251881Speter apr_hash_t *attrs, 178251881Speter apr_pool_t *scratch_pool) 179251881Speter{ 180251881Speter blame_context_t *blame_ctx = baton; 181251881Speter 182251881Speter if (leaving_state == FILE_REV) 183251881Speter { 184251881Speter /* Note that we test STREAM, but any pointer is currently invalid. 185251881Speter It was closed when left the TXDELTA state. */ 186251881Speter if (blame_ctx->stream == NULL) 187251881Speter { 188251881Speter const char *path; 189251881Speter const char *rev; 190251881Speter 191251881Speter path = svn_hash_gets(attrs, "path"); 192251881Speter rev = svn_hash_gets(attrs, "rev"); 193251881Speter 194251881Speter /* Send a "no content" notification. */ 195251881Speter SVN_ERR(blame_ctx->file_rev(blame_ctx->file_rev_baton, 196251881Speter path, SVN_STR_TO_REV(rev), 197251881Speter blame_ctx->rev_props, 198251881Speter FALSE /* result_of_merge */, 199251881Speter NULL, NULL, /* txdelta / baton */ 200251881Speter blame_ctx->prop_diffs, 201251881Speter scratch_pool)); 202251881Speter } 203251881Speter } 204251881Speter else if (leaving_state == MERGED_REVISION) 205251881Speter { 206251881Speter svn_ra_serf__xml_note(xes, FILE_REV, "merged-revision", "*"); 207251881Speter } 208251881Speter else if (leaving_state == TXDELTA) 209251881Speter { 210251881Speter SVN_ERR(svn_stream_close(blame_ctx->stream)); 211251881Speter } 212251881Speter else 213251881Speter { 214251881Speter const char *name; 215251881Speter const svn_string_t *value; 216251881Speter 217251881Speter SVN_ERR_ASSERT(leaving_state == REV_PROP 218251881Speter || leaving_state == SET_PROP 219251881Speter || leaving_state == REMOVE_PROP); 220251881Speter 221251881Speter name = apr_pstrdup(blame_ctx->state_pool, 222251881Speter svn_hash_gets(attrs, "name")); 223251881Speter 224251881Speter if (leaving_state == REMOVE_PROP) 225251881Speter { 226251881Speter value = NULL; 227251881Speter } 228251881Speter else 229251881Speter { 230251881Speter const char *encoding = svn_hash_gets(attrs, "encoding"); 231251881Speter 232251881Speter if (encoding && strcmp(encoding, "base64") == 0) 233251881Speter value = svn_base64_decode_string(cdata, blame_ctx->state_pool); 234251881Speter else 235251881Speter value = svn_string_dup(cdata, blame_ctx->state_pool); 236251881Speter } 237251881Speter 238251881Speter if (leaving_state == REV_PROP) 239251881Speter { 240251881Speter svn_hash_sets(blame_ctx->rev_props, name, value); 241251881Speter } 242251881Speter else 243251881Speter { 244251881Speter svn_prop_t *prop = apr_array_push(blame_ctx->prop_diffs); 245251881Speter 246251881Speter prop->name = name; 247251881Speter prop->value = value; 248251881Speter } 249251881Speter } 250251881Speter 251251881Speter return SVN_NO_ERROR; 252251881Speter} 253251881Speter 254251881Speter 255251881Speter/* Conforms to svn_ra_serf__xml_cdata_t */ 256251881Speterstatic svn_error_t * 257251881Speterblame_cdata(svn_ra_serf__xml_estate_t *xes, 258251881Speter void *baton, 259251881Speter int current_state, 260251881Speter const char *data, 261251881Speter apr_size_t len, 262251881Speter apr_pool_t *scratch_pool) 263251881Speter{ 264251881Speter blame_context_t *blame_ctx = baton; 265251881Speter 266251881Speter if (current_state == TXDELTA) 267251881Speter { 268251881Speter SVN_ERR(svn_stream_write(blame_ctx->stream, data, &len)); 269251881Speter /* Ignore the returned LEN value. */ 270251881Speter } 271251881Speter 272251881Speter return SVN_NO_ERROR; 273251881Speter} 274251881Speter 275251881Speter 276251881Speter/* Implements svn_ra_serf__request_body_delegate_t */ 277251881Speterstatic svn_error_t * 278251881Spetercreate_file_revs_body(serf_bucket_t **body_bkt, 279251881Speter void *baton, 280251881Speter serf_bucket_alloc_t *alloc, 281251881Speter apr_pool_t *pool) 282251881Speter{ 283251881Speter serf_bucket_t *buckets; 284251881Speter blame_context_t *blame_ctx = baton; 285251881Speter 286251881Speter buckets = serf_bucket_aggregate_create(alloc); 287251881Speter 288251881Speter svn_ra_serf__add_open_tag_buckets(buckets, alloc, 289251881Speter "S:file-revs-report", 290251881Speter "xmlns:S", SVN_XML_NAMESPACE, 291251881Speter NULL); 292251881Speter 293251881Speter svn_ra_serf__add_tag_buckets(buckets, 294251881Speter "S:start-revision", apr_ltoa(pool, blame_ctx->start), 295251881Speter alloc); 296251881Speter 297251881Speter svn_ra_serf__add_tag_buckets(buckets, 298251881Speter "S:end-revision", apr_ltoa(pool, blame_ctx->end), 299251881Speter alloc); 300251881Speter 301251881Speter if (blame_ctx->include_merged_revisions) 302251881Speter { 303251881Speter svn_ra_serf__add_tag_buckets(buckets, 304251881Speter "S:include-merged-revisions", NULL, 305251881Speter alloc); 306251881Speter } 307251881Speter 308251881Speter svn_ra_serf__add_tag_buckets(buckets, 309251881Speter "S:path", blame_ctx->path, 310251881Speter alloc); 311251881Speter 312251881Speter svn_ra_serf__add_close_tag_buckets(buckets, alloc, 313251881Speter "S:file-revs-report"); 314251881Speter 315251881Speter *body_bkt = buckets; 316251881Speter return SVN_NO_ERROR; 317251881Speter} 318251881Speter 319251881Spetersvn_error_t * 320251881Spetersvn_ra_serf__get_file_revs(svn_ra_session_t *ra_session, 321251881Speter const char *path, 322251881Speter svn_revnum_t start, 323251881Speter svn_revnum_t end, 324251881Speter svn_boolean_t include_merged_revisions, 325251881Speter svn_file_rev_handler_t rev_handler, 326251881Speter void *rev_handler_baton, 327251881Speter apr_pool_t *pool) 328251881Speter{ 329251881Speter blame_context_t *blame_ctx; 330251881Speter svn_ra_serf__session_t *session = ra_session->priv; 331251881Speter svn_ra_serf__handler_t *handler; 332251881Speter svn_ra_serf__xml_context_t *xmlctx; 333251881Speter const char *req_url; 334251881Speter svn_error_t *err; 335251881Speter 336251881Speter blame_ctx = apr_pcalloc(pool, sizeof(*blame_ctx)); 337251881Speter blame_ctx->pool = pool; 338251881Speter blame_ctx->path = path; 339251881Speter blame_ctx->file_rev = rev_handler; 340251881Speter blame_ctx->file_rev_baton = rev_handler_baton; 341251881Speter blame_ctx->start = start; 342251881Speter blame_ctx->end = end; 343251881Speter blame_ctx->include_merged_revisions = include_merged_revisions; 344251881Speter 345251881Speter SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */, 346251881Speter session, NULL /* conn */, 347251881Speter NULL /* url */, end, 348251881Speter pool, pool)); 349251881Speter 350251881Speter xmlctx = svn_ra_serf__xml_context_create(blame_ttable, 351251881Speter blame_opened, 352251881Speter blame_closed, 353251881Speter blame_cdata, 354251881Speter blame_ctx, 355251881Speter pool); 356251881Speter handler = svn_ra_serf__create_expat_handler(xmlctx, pool); 357251881Speter 358251881Speter handler->method = "REPORT"; 359251881Speter handler->path = req_url; 360251881Speter handler->body_type = "text/xml"; 361251881Speter handler->body_delegate = create_file_revs_body; 362251881Speter handler->body_delegate_baton = blame_ctx; 363251881Speter handler->conn = session->conns[0]; 364251881Speter handler->session = session; 365251881Speter 366251881Speter err = svn_ra_serf__context_run_one(handler, pool); 367251881Speter 368251881Speter err = svn_error_compose_create( 369253734Speter svn_ra_serf__error_on_status(handler->sline, 370251881Speter handler->path, 371251881Speter handler->location), 372251881Speter err); 373251881Speter 374251881Speter return svn_error_trace(err); 375251881Speter} 376