1333347Speter/* 2333347Speter * list.c : entry point for the list RA function in ra_serf 3333347Speter * 4333347Speter * ==================================================================== 5333347Speter * Licensed to the Apache Software Foundation (ASF) under one 6333347Speter * or more contributor license agreements. See the NOTICE file 7333347Speter * distributed with this work for additional information 8333347Speter * regarding copyright ownership. The ASF licenses this file 9333347Speter * to you under the Apache License, Version 2.0 (the 10333347Speter * "License"); you may not use this file except in compliance 11333347Speter * with the License. You may obtain a copy of the License at 12333347Speter * 13333347Speter * http://www.apache.org/licenses/LICENSE-2.0 14333347Speter * 15333347Speter * Unless required by applicable law or agreed to in writing, 16333347Speter * software distributed under the License is distributed on an 17333347Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18333347Speter * KIND, either express or implied. See the License for the 19333347Speter * specific language governing permissions and limitations 20333347Speter * under the License. 21333347Speter * ==================================================================== 22333347Speter */ 23333347Speter 24333347Speter#include <serf.h> 25333347Speter 26333347Speter#include "svn_hash.h" 27333347Speter#include "svn_base64.h" 28333347Speter#include "svn_xml.h" 29333347Speter#include "svn_time.h" 30333347Speter 31333347Speter#include "svn_private_config.h" 32333347Speter 33333347Speter#include "ra_serf.h" 34333347Speter#include "../libsvn_ra/ra_loader.h" 35333347Speter 36333347Speter 37333347Speter 38333347Speter/* 39333347Speter * This enum represents the current state of our XML parsing for a REPORT. 40333347Speter */ 41333347Speterenum list_state_e { 42333347Speter INITIAL = XML_STATE_INITIAL, 43333347Speter REPORT, 44333347Speter ITEM, 45333347Speter AUTHOR 46333347Speter}; 47333347Speter 48333347Spetertypedef struct list_context_t { 49333347Speter apr_pool_t *pool; 50333347Speter 51333347Speter /* parameters set by our caller */ 52333347Speter const char *path; 53333347Speter svn_revnum_t revision; 54333347Speter const apr_array_header_t *patterns; 55333347Speter svn_depth_t depth; 56333347Speter apr_uint32_t dirent_fields; 57333347Speter apr_array_header_t *props; 58333347Speter 59333347Speter /* Buffer the author info for the current item. 60333347Speter * We use the AUTHOR pointer to differentiate between 0-length author 61333347Speter * strings and missing / NULL authors. */ 62333347Speter const char *author; 63333347Speter svn_stringbuf_t *author_buf; 64333347Speter 65333347Speter /* log receiver function and baton */ 66333347Speter svn_ra_dirent_receiver_t receiver; 67333347Speter void *receiver_baton; 68333347Speter} list_context_t; 69333347Speter 70333347Speter#define D_ "DAV:" 71333347Speter#define S_ SVN_XML_NAMESPACE 72333347Speterstatic const svn_ra_serf__xml_transition_t log_ttable[] = { 73333347Speter { INITIAL, S_, "list-report", REPORT, 74333347Speter FALSE, { NULL }, FALSE }, 75333347Speter 76333347Speter { REPORT, S_, "item", ITEM, 77333347Speter TRUE, { "node-kind", "?size", "?has-props", "?created-rev", 78333347Speter "?date", NULL }, TRUE }, 79333347Speter 80333347Speter { ITEM, D_, "creator-displayname", AUTHOR, 81333347Speter TRUE, { "?encoding", NULL }, TRUE }, 82333347Speter 83333347Speter { 0 } 84333347Speter}; 85333347Speter 86333347Speter/* Conforms to svn_ra_serf__xml_closed_t */ 87333347Speterstatic svn_error_t * 88333347Speteritem_closed(svn_ra_serf__xml_estate_t *xes, 89333347Speter void *baton, 90333347Speter int leaving_state, 91333347Speter const svn_string_t *cdata, 92333347Speter apr_hash_t *attrs, 93333347Speter apr_pool_t *scratch_pool) 94333347Speter{ 95333347Speter list_context_t *list_ctx = baton; 96333347Speter 97333347Speter if (leaving_state == AUTHOR) 98333347Speter { 99333347Speter /* For compatibility with liveprops, current servers will not use 100333347Speter * base64-encoding for "binary" user names bu simply drop the 101333347Speter * offending control chars. 102333347Speter * 103333347Speter * We might want to switch to revprop-style encoding, though, 104333347Speter * and this is the code to do that. */ 105333347Speter const char *encoding = svn_hash_gets(attrs, "encoding"); 106333347Speter if (encoding) 107333347Speter { 108333347Speter /* Check for a known encoding type. This is easy -- there's 109333347Speter only one. */ 110333347Speter if (strcmp(encoding, "base64") != 0) 111333347Speter { 112333347Speter return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, 113333347Speter _("Unsupported encoding '%s'"), 114333347Speter encoding); 115333347Speter } 116333347Speter 117333347Speter cdata = svn_base64_decode_string(cdata, scratch_pool); 118333347Speter } 119333347Speter 120333347Speter /* Remember until the next ITEM closing tag. */ 121333347Speter svn_stringbuf_set(list_ctx->author_buf, cdata->data); 122333347Speter list_ctx->author = list_ctx->author_buf->data; 123333347Speter } 124333347Speter else if (leaving_state == ITEM) 125333347Speter { 126333347Speter const char *dirent_path = cdata->data; 127333347Speter const char *kind_word, *date, *crev, *size; 128333347Speter svn_dirent_t dirent = { 0 }; 129333347Speter 130333347Speter kind_word = svn_hash_gets(attrs, "node-kind"); 131333347Speter size = svn_hash_gets(attrs, "size"); 132333347Speter 133333347Speter dirent.has_props = svn_hash__get_bool(attrs, "has-props", FALSE); 134333347Speter crev = svn_hash_gets(attrs, "created-rev"); 135333347Speter date = svn_hash_gets(attrs, "date"); 136333347Speter 137333347Speter /* Convert data. */ 138333347Speter dirent.kind = svn_node_kind_from_word(kind_word); 139333347Speter 140333347Speter if (size) 141333347Speter SVN_ERR(svn_cstring_atoi64(&dirent.size, size)); 142333347Speter else 143333347Speter dirent.size = SVN_INVALID_FILESIZE; 144333347Speter 145333347Speter if (crev) 146333347Speter SVN_ERR(svn_revnum_parse(&dirent.created_rev, crev, NULL)); 147333347Speter else 148333347Speter dirent.created_rev = SVN_INVALID_REVNUM; 149333347Speter 150333347Speter if (date) 151333347Speter SVN_ERR(svn_time_from_cstring(&dirent.time, date, scratch_pool)); 152333347Speter 153333347Speter if (list_ctx->author) 154333347Speter dirent.last_author = list_ctx->author; 155333347Speter 156333347Speter /* Invoke RECEIVER */ 157333347Speter SVN_ERR(list_ctx->receiver(dirent_path, &dirent, 158333347Speter list_ctx->receiver_baton, scratch_pool)); 159333347Speter 160333347Speter /* Reset buffered info. */ 161333347Speter list_ctx->author = NULL; 162333347Speter } 163333347Speter 164333347Speter return SVN_NO_ERROR; 165333347Speter} 166333347Speter 167333347Speter/* Implements svn_ra_serf__request_body_delegate_t */ 168333347Speterstatic svn_error_t * 169333347Spetercreate_list_body(serf_bucket_t **body_bkt, 170333347Speter void *baton, 171333347Speter serf_bucket_alloc_t *alloc, 172333347Speter apr_pool_t *pool /* request pool */, 173333347Speter apr_pool_t *scratch_pool) 174333347Speter{ 175333347Speter serf_bucket_t *buckets; 176333347Speter list_context_t *list_ctx = baton; 177333347Speter int i; 178333347Speter 179333347Speter buckets = serf_bucket_aggregate_create(alloc); 180333347Speter 181333347Speter svn_ra_serf__add_open_tag_buckets(buckets, alloc, 182333347Speter "S:list-report", 183333347Speter "xmlns:S", SVN_XML_NAMESPACE, 184333347Speter SVN_VA_NULL); 185333347Speter 186333347Speter svn_ra_serf__add_tag_buckets(buckets, 187333347Speter "S:path", list_ctx->path, 188333347Speter alloc); 189333347Speter svn_ra_serf__add_tag_buckets(buckets, 190333347Speter "S:revision", 191333347Speter apr_ltoa(pool, list_ctx->revision), 192333347Speter alloc); 193333347Speter svn_ra_serf__add_tag_buckets(buckets, 194333347Speter "S:depth", svn_depth_to_word(list_ctx->depth), 195333347Speter alloc); 196333347Speter 197333347Speter if (list_ctx->patterns) 198333347Speter { 199333347Speter for (i = 0; i < list_ctx->patterns->nelts; i++) 200333347Speter { 201333347Speter char *name = APR_ARRAY_IDX(list_ctx->patterns, i, char *); 202333347Speter svn_ra_serf__add_tag_buckets(buckets, 203333347Speter "S:pattern", name, 204333347Speter alloc); 205333347Speter } 206333347Speter if (list_ctx->patterns->nelts == 0) 207333347Speter { 208333347Speter svn_ra_serf__add_empty_tag_buckets(buckets, alloc, 209333347Speter "S:no-patterns", SVN_VA_NULL); 210333347Speter } 211333347Speter } 212333347Speter 213333347Speter for (i = 0; i < list_ctx->props->nelts; i++) 214333347Speter { 215333347Speter const svn_ra_serf__dav_props_t *prop 216333347Speter = &APR_ARRAY_IDX(list_ctx->props, i, const svn_ra_serf__dav_props_t); 217333347Speter const char *name 218333347Speter = apr_pstrcat(pool, prop->xmlns, prop->name, SVN_VA_NULL); 219333347Speter 220333347Speter svn_ra_serf__add_tag_buckets(buckets, "S:prop", name, alloc); 221333347Speter } 222333347Speter 223333347Speter svn_ra_serf__add_close_tag_buckets(buckets, alloc, 224333347Speter "S:list-report"); 225333347Speter 226333347Speter *body_bkt = buckets; 227333347Speter return SVN_NO_ERROR; 228333347Speter} 229333347Speter 230333347Speter 231333347Spetersvn_error_t * 232333347Spetersvn_ra_serf__list(svn_ra_session_t *ra_session, 233333347Speter const char *path, 234333347Speter svn_revnum_t revision, 235333347Speter const apr_array_header_t *patterns, 236333347Speter svn_depth_t depth, 237333347Speter apr_uint32_t dirent_fields, 238333347Speter svn_ra_dirent_receiver_t receiver, 239333347Speter void *receiver_baton, 240333347Speter apr_pool_t *scratch_pool) 241333347Speter{ 242333347Speter list_context_t *list_ctx; 243333347Speter svn_ra_serf__session_t *session = ra_session->priv; 244333347Speter svn_ra_serf__handler_t *handler; 245333347Speter svn_ra_serf__xml_context_t *xmlctx; 246333347Speter const char *req_url; 247333347Speter 248333347Speter list_ctx = apr_pcalloc(scratch_pool, sizeof(*list_ctx)); 249333347Speter list_ctx->pool = scratch_pool; 250333347Speter list_ctx->receiver = receiver; 251333347Speter list_ctx->receiver_baton = receiver_baton; 252333347Speter list_ctx->path = path; 253333347Speter list_ctx->revision = revision; 254333347Speter list_ctx->patterns = patterns; 255333347Speter list_ctx->depth = depth; 256333347Speter list_ctx->dirent_fields = dirent_fields; 257333347Speter list_ctx->props = svn_ra_serf__get_dirent_props(dirent_fields, session, 258333347Speter scratch_pool); 259333347Speter list_ctx->author_buf = svn_stringbuf_create_empty(scratch_pool); 260333347Speter 261333347Speter /* At this point, we may have a deleted file. So, we'll match ra_neon's 262333347Speter * behavior and use the larger of start or end as our 'peg' rev. 263333347Speter */ 264333347Speter SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */, 265333347Speter session, 266333347Speter NULL /* url */, revision, 267333347Speter scratch_pool, scratch_pool)); 268333347Speter 269333347Speter xmlctx = svn_ra_serf__xml_context_create(log_ttable, 270333347Speter NULL, item_closed, NULL, 271333347Speter list_ctx, 272333347Speter scratch_pool); 273333347Speter handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, 274333347Speter scratch_pool); 275333347Speter 276333347Speter handler->method = "REPORT"; 277333347Speter handler->path = req_url; 278333347Speter handler->body_delegate = create_list_body; 279333347Speter handler->body_delegate_baton = list_ctx; 280333347Speter handler->body_type = "text/xml"; 281333347Speter 282333347Speter SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool)); 283333347Speter 284333347Speter if (handler->sline.code != 200) 285333347Speter SVN_ERR(svn_ra_serf__unexpected_status(handler)); 286333347Speter 287333347Speter return SVN_NO_ERROR; 288333347Speter} 289