static dav_error *
dav_rainx_deliver_SPECIAL(const dav_resource *resource, ap_filter_t *output)
{
	const char *result;
	int result_len;
	apr_status_t status;
	apr_pool_t *pool;
	apr_bucket_brigade *bb;
	apr_bucket *bkt;

	DAV_XDEBUG_RES(resource, 0, "%s()", __FUNCTION__);
	pool = resource->info->request->pool;

	/* Check resource type */
	if (resource->type != DAV_RESOURCE_TYPE_PRIVATE)
		return server_create_and_stat_error(resource_get_server_config(resource), pool,
			HTTP_CONFLICT, 0, apr_pstrdup(pool, "Cannot GET this type of resource."));
	if (resource->collection)
		return server_create_and_stat_error(resource_get_server_config(resource), pool,
			HTTP_CONFLICT, 0, apr_pstrdup(pool,"No GET on collections"));

	/* Generate the output */
	result = resource->info->generator(resource, pool);
	result_len = strlen(result);

	/* We must reply a buffer */
	bkt = apr_bucket_heap_create(result, result_len, NULL, output->c->bucket_alloc);
	bb = apr_brigade_create(pool, output->c->bucket_alloc);
	APR_BRIGADE_INSERT_TAIL(bb, bkt);

	/* Nothing more to reply */
	bkt = apr_bucket_eos_create(output->c->bucket_alloc);
	APR_BRIGADE_INSERT_TAIL(bb, bkt);

	DAV_XDEBUG_RES(resource, 0, "%s : ready to deliver", __FUNCTION__);

	if ((status = ap_pass_brigade(output, bb)) != APR_SUCCESS)
		return server_create_and_stat_error(resource_get_server_config(resource), pool,
			HTTP_FORBIDDEN, 0, apr_pstrdup(pool,"Could not write contents to filter."));

	server_inc_stat(resource_get_server_config(resource), RAWX_STATNAME_REP_2XX, 0);

	/* HERE ADD request counter */
	switch(resource->info->type) {
		case STAT:
			server_inc_request_stat(resource_get_server_config(resource), RAWX_STATNAME_REQ_STAT,
				request_get_duration(resource->info->request));
			break;
		case INFO:
			server_inc_request_stat(resource_get_server_config(resource), RAWX_STATNAME_REQ_INFO,
				request_get_duration(resource->info->request));
			break;
		default:
			break;
	}

	return NULL;
}
static dav_error *
dav_rawx_move_resource(dav_resource *src_res, dav_resource *dst_res,
		dav_response **response)
{
	char buff[128];
	apr_pool_t *pool;
	pool = dst_res->pool;
	apr_status_t status;
	dav_error *e = NULL;
	dav_rawx_server_conf *srv_conf = resource_get_server_config(src_res);

	*response = NULL;

	if (DAV_RESOURCE_TYPE_REGULAR != src_res->type)  {
		e = server_create_and_stat_error(srv_conf, pool,
			HTTP_CONFLICT, 0, "Cannot MOVE this type of resource.");
		goto end_move;
	}
	if (src_res->collection) {
		e = server_create_and_stat_error(srv_conf, pool,
			HTTP_CONFLICT, 0, "No MOVE on collections");
		goto end_move;
	}
	if (apr_strnatcasecmp(src_res->info->hex_chunkid,
			dst_res->info->hex_chunkid)) {
		e = server_create_and_stat_error(srv_conf, pool,
				HTTP_FORBIDDEN, 0,
				"Source and destination chunk ids are not the same");
		goto end_move;
	}

	DAV_DEBUG_RES(src_res, 0, "Moving %s to %s",
			resource_get_pathname(src_res), resource_get_pathname(dst_res));
	status = apr_file_rename(resource_get_pathname(src_res),
			resource_get_pathname(dst_res), pool);

	if (status != APR_SUCCESS) {
		e = server_create_and_stat_error(srv_conf,
				pool, HTTP_INTERNAL_SERVER_ERROR, status,
				apr_pstrcat(pool, "Failed to MOVE this chunk: ",
					apr_strerror(status, buff, sizeof(buff)), NULL));
		goto end_move;
	}

	server_inc_stat(srv_conf, RAWX_STATNAME_REP_2XX, 0);

end_move:
	server_inc_request_stat(srv_conf, RAWX_STATNAME_REQ_OTHER,
			request_get_duration(src_res->info->request));

	return e;
}
static dav_error *
dav_rawx_remove_resource(dav_resource *resource, dav_response **response)
{
	char buff[128];
	apr_pool_t *pool;
	apr_status_t status;
	dav_error *e = NULL;

	DAV_XDEBUG_RES(resource, 0, "%s(%s)", __FUNCTION__, resource_get_pathname(resource));
	pool = resource->pool;
	*response = NULL;

	if (DAV_RESOURCE_TYPE_REGULAR != resource->type)  {
		e = server_create_and_stat_error(resource_get_server_config(resource), pool,
			HTTP_CONFLICT, 0, "Cannot DELETE this type of resource.");
		goto end_remove;
	}
	if (resource->collection) {
		e = server_create_and_stat_error(resource_get_server_config(resource), pool,
			HTTP_CONFLICT, 0, "No DELETE on collections");
		goto end_remove;
	}

	status = apr_file_remove(resource_get_pathname(resource), pool);
	if (APR_SUCCESS != status) {
		e = server_create_and_stat_error(resource_get_server_config(resource), pool,
			HTTP_FORBIDDEN, 0, apr_pstrcat(pool,
					"Failed to DELETE this chunk : ",
					apr_strerror(status, buff, sizeof(buff)),
					NULL));
		goto end_remove;
	}

	send_chunk_event("storage.chunk.deleted", resource);

	resource->exists = 0;
	resource->collection = 0;

	server_inc_stat(resource_get_server_config(resource), RAWX_STATNAME_REP_2XX, 0);

end_remove:

	/* Now we pass here even if an error occured, for process request duration */
	server_inc_request_stat(resource_get_server_config(resource), RAWX_STATNAME_REQ_CHUNKDEL,
				request_get_duration(resource->info->request));

	return e;
}
static dav_error *
dav_rawx_close_stream(dav_stream *stream, int commit)
{
	/* LAST STEP OF PUT REQUEST */

	dav_error *e = NULL;

	DAV_DEBUG_REQ(stream->r->info->request, 0,
			"Closing (%s) the stream to [%s]",
			(commit ? "commit" : "rollback"), stream->pathname);

	if (!commit) {
		e = rawx_repo_rollback_upload(stream);
	} else {
		e = rawx_repo_write_last_data_crumble(stream);
		if (e) {
			DAV_DEBUG_REQ(stream->r->info->request, 0,
					"Cannot commit, an error occured while writing end of data");
			dav_error *e_tmp = NULL;
			e_tmp = rawx_repo_rollback_upload(stream);
			if (e_tmp) {
				DAV_ERROR_REQ(stream->r->info->request, 0,
						"Error while rolling back upload: %s", e_tmp->desc);
			}
		} else {
			e = rawx_repo_commit_upload(stream);
		}
	}

	/* stats update */
	if (stream->total_size > 0) {
		server_add_stat(resource_get_server_config(stream->r),
				RAWX_STATNAME_REP_BWRITTEN,
				stream->total_size, 0);
	}
	server_inc_request_stat(resource_get_server_config(stream->r),
			RAWX_STATNAME_REQ_CHUNKPUT,
			request_get_duration(stream->r->info->request));

	if (stream->md5) {
		g_checksum_free(stream->md5);
		stream->md5 = NULL;
	}
	return e;
}
static dav_error *
dav_rawx_deliver(const dav_resource *resource, ap_filter_t *output)
{
	dav_rawx_server_conf *conf;
	apr_pool_t *pool;
	apr_bucket_brigade *bb = NULL;
	apr_status_t status;
	apr_bucket *bkt = NULL;
	dav_resource_private *ctx;
	dav_error *e = NULL;

	apr_finfo_t info;

	DAV_XDEBUG_RES(resource, 0, "%s(%s)", __FUNCTION__, resource_get_pathname(resource));

	pool = resource->pool;
	conf = resource_get_server_config(resource);

	/* Check resource type */
	if (DAV_RESOURCE_TYPE_REGULAR != resource->type) {
		e = server_create_and_stat_error(conf, pool, HTTP_CONFLICT, 0, "Cannot GET this type of resource.");
		goto end_deliver;
	}

	if (resource->collection) {
		e = server_create_and_stat_error(conf, pool, HTTP_CONFLICT, 0, "No GET on collections");
		goto end_deliver;
	}

	ctx = resource->info;

	if (ctx->update_only) {
		/* Check if it is not a busy file. We accept reads during
		 * compression but not attr updates. */
		char *pending_file = apr_pstrcat(pool,
				resource_get_pathname(resource), ".pending", NULL);
		status = apr_stat(&info, pending_file, APR_FINFO_ATIME, pool);
		if (status == APR_SUCCESS) {
			e = server_create_and_stat_error(conf, pool, HTTP_FORBIDDEN,
					0, "File in pending mode.");
			goto end_deliver;
		}

		GError *error_local = NULL;
		/* UPDATE chunk attributes and go on */
		const char *path = resource_get_pathname(resource);
		FILE *f = NULL;
		f = fopen(path, "r");
		/* Try to open the file but forbids a creation */
		if (!set_rawx_info_to_fd(fileno(f), &error_local, &(ctx->chunk))) {
			fclose(f);
			e = server_create_and_stat_error(conf, pool,
					HTTP_FORBIDDEN, 0, apr_pstrdup(pool, gerror_get_message(error_local)));
			g_clear_error(&error_local);
			goto end_deliver;
		}
		fclose(f);
	} else {
		bb = apr_brigade_create(pool, output->c->bucket_alloc);

		if (!ctx->compression){
			apr_file_t *fd = NULL;

			/* Try to open the file but forbids a creation */
			status = apr_file_open(&fd, resource_get_pathname(resource),
					APR_READ|APR_BINARY|APR_BUFFERED, 0, pool);
			if (APR_SUCCESS != status) {
				e = server_create_and_stat_error(conf, pool, HTTP_FORBIDDEN,
						0, "File permissions deny server access.");
				goto end_deliver;
			}

			/* FIXME this does not handle large files. but this is test code anyway */
			bkt = apr_bucket_file_create(fd, 0,
					(apr_size_t)resource->info->finfo.size,
					pool, output->c->bucket_alloc);
		}
		else {
			DAV_DEBUG_RES(resource, 0, "Building a compressed resource bucket");
			gint i64;

			i64 = g_ascii_strtoll(ctx->cp_chunk.uncompressed_size, NULL, 10);

			/* creation of compression specific bucket */
			bkt = apr_pcalloc(pool, sizeof(struct apr_bucket));
			bkt->type = &chunk_bucket_type;
			bkt->length = i64;
			bkt->start = 0;
			bkt->data = ctx;
			bkt->free = chunk_bucket_free_noop;
			bkt->list = output->c->bucket_alloc;
		}

		APR_BRIGADE_INSERT_TAIL(bb, bkt);

		/* as soon as the chunk has been sent, end of stream!*/
		bkt = apr_bucket_eos_create(output->c->bucket_alloc);
		APR_BRIGADE_INSERT_TAIL(bb, bkt);

		if ((status = ap_pass_brigade(output, bb)) != APR_SUCCESS){
			e = server_create_and_stat_error(conf, pool, HTTP_FORBIDDEN, 0, "Could not write contents to filter.");
			/* close file */
			if (ctx->cp_chunk.fd) {
				fclose(ctx->cp_chunk.fd);
			}
			goto end_deliver;
		}
		if (ctx->cp_chunk.buf){
			g_free(ctx->cp_chunk.buf);
			ctx->cp_chunk.buf = NULL;
		}
		if (ctx->cp_chunk.uncompressed_size){
			g_free(ctx->cp_chunk.uncompressed_size);
			ctx->cp_chunk.uncompressed_size = NULL;
		}

		/* close file */
		if (ctx->cp_chunk.fd) {
			fclose(ctx->cp_chunk.fd);
		}

		server_inc_stat(conf, RAWX_STATNAME_REP_2XX, 0);
		server_add_stat(conf, RAWX_STATNAME_REP_BWRITTEN, resource->info->finfo.size, 0);
	}

end_deliver:

	if (bb) {
		apr_brigade_destroy(bb);
		bb = NULL;
	}

	/* Now we pass here even if an error occured, for process request duration */
	server_inc_request_stat(resource_get_server_config(resource), RAWX_STATNAME_REQ_CHUNKGET,
			request_get_duration(resource->info->request));

	return e;
}