Blame libarchive/archive_write_set_format_warc.c

Packit Service 1d0348
/*-
Packit Service 1d0348
 * Copyright (c) 2014 Sebastian Freundt
Packit Service 1d0348
 * Author: Sebastian Freundt  <devel@fresse.org>
Packit Service 1d0348
 *
Packit Service 1d0348
 * All rights reserved.
Packit Service 1d0348
 *
Packit Service 1d0348
 * Redistribution and use in source and binary forms, with or without
Packit Service 1d0348
 * modification, are permitted provided that the following conditions
Packit Service 1d0348
 * are met:
Packit Service 1d0348
 * 1. Redistributions of source code must retain the above copyright
Packit Service 1d0348
 *    notice, this list of conditions and the following disclaimer.
Packit Service 1d0348
 * 2. Redistributions in binary form must reproduce the above copyright
Packit Service 1d0348
 *    notice, this list of conditions and the following disclaimer in the
Packit Service 1d0348
 *    documentation and/or other materials provided with the distribution.
Packit Service 1d0348
 *
Packit Service 1d0348
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
Packit Service 1d0348
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
Packit Service 1d0348
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
Packit Service 1d0348
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
Packit Service 1d0348
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
Packit Service 1d0348
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
Packit Service 1d0348
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
Packit Service 1d0348
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
Packit Service 1d0348
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
Packit Service 1d0348
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Packit Service 1d0348
 */
Packit Service 1d0348
Packit Service 1d0348
#include "archive_platform.h"
Packit Service 1d0348
__FBSDID("$FreeBSD$");
Packit Service 1d0348
Packit Service 1d0348
#ifdef HAVE_ERRNO_H
Packit Service 1d0348
#include <errno.h>
Packit Service 1d0348
#endif
Packit Service 1d0348
#include <stdio.h>
Packit Service 1d0348
#ifdef HAVE_STDLIB_H
Packit Service 1d0348
#include <stdlib.h>
Packit Service 1d0348
#endif
Packit Service 1d0348
#ifdef HAVE_STRING_H
Packit Service 1d0348
#include <string.h>
Packit Service 1d0348
#endif
Packit Service 1d0348
#ifdef HAVE_TIME_H
Packit Service 1d0348
#include <time.h>
Packit Service 1d0348
#endif
Packit Service 1d0348
Packit Service 1d0348
#include "archive.h"
Packit Service 1d0348
#include "archive_entry.h"
Packit Service 1d0348
#include "archive_entry_locale.h"
Packit Service 1d0348
#include "archive_private.h"
Packit Service 1d0348
#include "archive_random_private.h"
Packit Service 1d0348
#include "archive_write_private.h"
Packit Service 1d0348
Packit Service 1d0348
struct warc_s {
Packit Service 1d0348
	unsigned int omit_warcinfo:1;
Packit Service 1d0348
Packit Service 1d0348
	time_t now;
Packit Service 1d0348
	mode_t typ;
Packit Service 1d0348
	unsigned int rng;
Packit Service 1d0348
	/* populated size */
Packit Service 1d0348
	uint64_t populz;
Packit Service 1d0348
};
Packit Service 1d0348
Packit Service 1d0348
static const char warcinfo[] =
Packit Service 1d0348
    "software: libarchive/" ARCHIVE_VERSION_ONLY_STRING "\r\n"
Packit Service 1d0348
    "format: WARC file version 1.0\r\n";
Packit Service 1d0348
Packit Service 1d0348
typedef enum {
Packit Service 1d0348
	WT_NONE,
Packit Service 1d0348
	/* warcinfo */
Packit Service 1d0348
	WT_INFO,
Packit Service 1d0348
	/* metadata */
Packit Service 1d0348
	WT_META,
Packit Service 1d0348
	/* resource */
Packit Service 1d0348
	WT_RSRC,
Packit Service 1d0348
	/* request, unsupported */
Packit Service 1d0348
	WT_REQ,
Packit Service 1d0348
	/* response, unsupported */
Packit Service 1d0348
	WT_RSP,
Packit Service 1d0348
	/* revisit, unsupported */
Packit Service 1d0348
	WT_RVIS,
Packit Service 1d0348
	/* conversion, unsupported */
Packit Service 1d0348
	WT_CONV,
Packit Service 1d0348
	/* continuation, unsupported at the moment */
Packit Service 1d0348
	WT_CONT,
Packit Service 1d0348
	/* invalid type */
Packit Service 1d0348
	LAST_WT
Packit Service 1d0348
} warc_type_t;
Packit Service 1d0348
Packit Service 1d0348
typedef struct {
Packit Service 1d0348
	warc_type_t type;
Packit Service 1d0348
	const char *tgturi;
Packit Service 1d0348
	const char *recid;
Packit Service 1d0348
	time_t rtime;
Packit Service 1d0348
	time_t mtime;
Packit Service 1d0348
	const char *cnttyp;
Packit Service 1d0348
	uint64_t cntlen;
Packit Service 1d0348
} warc_essential_hdr_t;
Packit Service 1d0348
Packit Service 1d0348
typedef struct {
Packit Service 1d0348
	unsigned int u[4U];
Packit Service 1d0348
} warc_uuid_t;
Packit Service 1d0348
Packit Service 1d0348
static int _warc_options(struct archive_write*, const char *key, const char *v);
Packit Service 1d0348
static int _warc_header(struct archive_write *a, struct archive_entry *entry);
Packit Service 1d0348
static ssize_t _warc_data(struct archive_write *a, const void *buf, size_t sz);
Packit Service 1d0348
static int _warc_finish_entry(struct archive_write *a);
Packit Service 1d0348
static int _warc_close(struct archive_write *a);
Packit Service 1d0348
static int _warc_free(struct archive_write *a);
Packit Service 1d0348
Packit Service 1d0348
/* private routines */
Packit Service 1d0348
static ssize_t _popul_ehdr(struct archive_string *t, size_t z, warc_essential_hdr_t);
Packit Service 1d0348
static int _gen_uuid(warc_uuid_t *tgt);
Packit Service 1d0348
Packit Service 1d0348

Packit Service 1d0348
/*
Packit Service 1d0348
 * Set output format to ISO 28500 (aka WARC) format.
Packit Service 1d0348
 */
Packit Service 1d0348
int
Packit Service 1d0348
archive_write_set_format_warc(struct archive *_a)
Packit Service 1d0348
{
Packit Service 1d0348
	struct archive_write *a = (struct archive_write *)_a;
Packit Service 1d0348
	struct warc_s *w;
Packit Service 1d0348
Packit Service 1d0348
	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
Packit Service 1d0348
	    ARCHIVE_STATE_NEW, "archive_write_set_format_warc");
Packit Service 1d0348
Packit Service 1d0348
	/* If another format was already registered, unregister it. */
Packit Service 1d0348
	if (a->format_free != NULL) {
Packit Service 1d0348
		(a->format_free)(a);
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	w = malloc(sizeof(*w));
Packit Service 1d0348
	if (w == NULL) {
Packit Service 1d0348
		archive_set_error(&a->archive, ENOMEM,
Packit Service 1d0348
		    "Can't allocate warc data");
Packit Service 1d0348
		return (ARCHIVE_FATAL);
Packit Service 1d0348
	}
Packit Service 1d0348
	/* by default we're emitting a file wide header */
Packit Service 1d0348
	w->omit_warcinfo = 0U;
Packit Service 1d0348
	/* obtain current time for date fields */
Packit Service 1d0348
	w->now = time(NULL);
Packit Service 1d0348
	/* reset file type info */
Packit Service 1d0348
	w->typ = 0;
Packit Service 1d0348
	/* also initialise our rng */
Packit Service 1d0348
	w->rng = (unsigned int)w->now;
Packit Service 1d0348
Packit Service 1d0348
	a->format_data = w;
Packit Service 1d0348
	a->format_name = "WARC/1.0";
Packit Service 1d0348
	a->format_options = _warc_options;
Packit Service 1d0348
	a->format_write_header = _warc_header;
Packit Service 1d0348
	a->format_write_data = _warc_data;
Packit Service 1d0348
	a->format_close = _warc_close;
Packit Service 1d0348
	a->format_free = _warc_free;
Packit Service 1d0348
	a->format_finish_entry = _warc_finish_entry;
Packit Service 1d0348
	a->archive.archive_format = ARCHIVE_FORMAT_WARC;
Packit Service 1d0348
	a->archive.archive_format_name = "WARC/1.0";
Packit Service 1d0348
	return (ARCHIVE_OK);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348

Packit Service 1d0348
/* archive methods */
Packit Service 1d0348
static int
Packit Service 1d0348
_warc_options(struct archive_write *a, const char *key, const char *val)
Packit Service 1d0348
{
Packit Service 1d0348
	struct warc_s *w = a->format_data;
Packit Service 1d0348
Packit Service 1d0348
	if (strcmp(key, "omit-warcinfo") == 0) {
Packit Service 1d0348
		if (val == NULL || strcmp(val, "true") == 0) {
Packit Service 1d0348
			/* great */
Packit Service 1d0348
			w->omit_warcinfo = 1U;
Packit Service 1d0348
			return (ARCHIVE_OK);
Packit Service 1d0348
		}
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	/* Note: The "warn" return is just to inform the options
Packit Service 1d0348
	 * supervisor that we didn't handle it.  It will generate
Packit Service 1d0348
	 * a suitable error if no one used this option. */
Packit Service 1d0348
	return (ARCHIVE_WARN);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static int
Packit Service 1d0348
_warc_header(struct archive_write *a, struct archive_entry *entry)
Packit Service 1d0348
{
Packit Service 1d0348
	struct warc_s *w = a->format_data;
Packit Service 1d0348
	struct archive_string hdr;
Packit Service 1d0348
#define MAX_HDR_SIZE 512
Packit Service 1d0348
Packit Service 1d0348
	/* check whether warcinfo record needs outputting */
Packit Service 1d0348
	if (!w->omit_warcinfo) {
Packit Service 1d0348
		ssize_t r;
Packit Service 1d0348
		warc_essential_hdr_t wi = {
Packit Service 1d0348
			WT_INFO,
Packit Service 1d0348
			/*uri*/NULL,
Packit Service 1d0348
			/*urn*/NULL,
Packit Service 1d0348
			/*rtm*/0,
Packit Service 1d0348
			/*mtm*/0,
Packit Service 1d0348
			/*cty*/"application/warc-fields",
Packit Service 1d0348
			/*len*/sizeof(warcinfo) - 1U,
Packit Service 1d0348
		};
Packit Service 1d0348
		wi.rtime = w->now;
Packit Service 1d0348
		wi.mtime = w->now;
Packit Service 1d0348
Packit Service 1d0348
		archive_string_init(&hdr);
Packit Service 1d0348
		r = _popul_ehdr(&hdr, MAX_HDR_SIZE, wi);
Packit Service 1d0348
		if (r >= 0) {
Packit Service 1d0348
			/* jackpot! */
Packit Service 1d0348
			/* now also use HDR buffer for the actual warcinfo */
Packit Service 1d0348
			archive_strncat(&hdr, warcinfo, sizeof(warcinfo) -1);
Packit Service 1d0348
Packit Service 1d0348
			/* append end-of-record indicator */
Packit Service 1d0348
			archive_strncat(&hdr, "\r\n\r\n", 4);
Packit Service 1d0348
Packit Service 1d0348
			/* write to output stream */
Packit Service 1d0348
			__archive_write_output(a, hdr.s, archive_strlen(&hdr));
Packit Service 1d0348
		}
Packit Service 1d0348
		/* indicate we're done with file header writing */
Packit Service 1d0348
		w->omit_warcinfo = 1U;
Packit Service 1d0348
		archive_string_free(&hdr);
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	if (archive_entry_pathname(entry) == NULL) {
Packit Service 1d0348
		archive_set_error(&a->archive, EINVAL,
Packit Service 1d0348
		    "Invalid filename");
Packit Service 1d0348
		return (ARCHIVE_WARN);
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	w->typ = archive_entry_filetype(entry);
Packit Service 1d0348
	w->populz = 0U;
Packit Service 1d0348
	if (w->typ == AE_IFREG) {
Packit Service 1d0348
		warc_essential_hdr_t rh = {
Packit Service 1d0348
			WT_RSRC,
Packit Service 1d0348
			/*uri*/NULL,
Packit Service 1d0348
			/*urn*/NULL,
Packit Service 1d0348
			/*rtm*/0,
Packit Service 1d0348
			/*mtm*/0,
Packit Service 1d0348
			/*cty*/NULL,
Packit Service 1d0348
			/*len*/0,
Packit Service 1d0348
		};
Packit Service 1d0348
		ssize_t r;
Packit Service 1d0348
		rh.tgturi = archive_entry_pathname(entry);
Packit Service 1d0348
		rh.rtime = w->now;
Packit Service 1d0348
		rh.mtime = archive_entry_mtime(entry);
Packit Service 1d0348
		rh.cntlen = (size_t)archive_entry_size(entry);
Packit Service 1d0348
Packit Service 1d0348
		archive_string_init(&hdr);
Packit Service 1d0348
		r = _popul_ehdr(&hdr, MAX_HDR_SIZE, rh);
Packit Service 1d0348
		if (r < 0) {
Packit Service 1d0348
			/* don't bother */
Packit Service 1d0348
			archive_set_error(
Packit Service 1d0348
				&a->archive,
Packit Service 1d0348
				ARCHIVE_ERRNO_FILE_FORMAT,
Packit Service 1d0348
				"cannot archive file");
Packit Service 1d0348
			return (ARCHIVE_WARN);
Packit Service 1d0348
		}
Packit Service 1d0348
		/* otherwise append to output stream */
Packit Service 1d0348
		__archive_write_output(a, hdr.s, r);
Packit Service 1d0348
		/* and let subsequent calls to _data() know about the size */
Packit Service 1d0348
		w->populz = rh.cntlen;
Packit Service 1d0348
		archive_string_free(&hdr);
Packit Service 1d0348
		return (ARCHIVE_OK);
Packit Service 1d0348
	}
Packit Service 1d0348
	/* just resort to erroring as per Tim's advice */
Packit Service 1d0348
	archive_set_error(
Packit Service 1d0348
		&a->archive,
Packit Service 1d0348
		ARCHIVE_ERRNO_FILE_FORMAT,
Packit Service 1d0348
		"WARC can only process regular files");
Packit Service 1d0348
	return (ARCHIVE_FAILED);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static ssize_t
Packit Service 1d0348
_warc_data(struct archive_write *a, const void *buf, size_t len)
Packit Service 1d0348
{
Packit Service 1d0348
	struct warc_s *w = a->format_data;
Packit Service 1d0348
Packit Service 1d0348
	if (w->typ == AE_IFREG) {
Packit Service 1d0348
		int rc;
Packit Service 1d0348
Packit Service 1d0348
		/* never write more bytes than announced */
Packit Service 1d0348
		if (len > w->populz) {
Packit Service 1d0348
			len = (size_t)w->populz;
Packit Service 1d0348
		}
Packit Service 1d0348
Packit Service 1d0348
		/* now then, out we put the whole shebang */
Packit Service 1d0348
		rc = __archive_write_output(a, buf, len);
Packit Service 1d0348
		if (rc != ARCHIVE_OK) {
Packit Service 1d0348
			return rc;
Packit Service 1d0348
		}
Packit Service 1d0348
	}
Packit Service 1d0348
	return len;
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static int
Packit Service 1d0348
_warc_finish_entry(struct archive_write *a)
Packit Service 1d0348
{
Packit Service 1d0348
	static const char _eor[] = "\r\n\r\n";
Packit Service 1d0348
	struct warc_s *w = a->format_data;
Packit Service 1d0348
Packit Service 1d0348
	if (w->typ == AE_IFREG) {
Packit Service 1d0348
		int rc = __archive_write_output(a, _eor, sizeof(_eor) - 1U);
Packit Service 1d0348
Packit Service 1d0348
		if (rc != ARCHIVE_OK) {
Packit Service 1d0348
			return rc;
Packit Service 1d0348
		}
Packit Service 1d0348
	}
Packit Service 1d0348
	/* reset type info */
Packit Service 1d0348
	w->typ = 0;
Packit Service 1d0348
	return (ARCHIVE_OK);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static int
Packit Service 1d0348
_warc_close(struct archive_write *a)
Packit Service 1d0348
{
Packit Service 1d0348
	(void)a; /* UNUSED */
Packit Service 1d0348
	return (ARCHIVE_OK);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static int
Packit Service 1d0348
_warc_free(struct archive_write *a)
Packit Service 1d0348
{
Packit Service 1d0348
	struct warc_s *w = a->format_data;
Packit Service 1d0348
Packit Service 1d0348
	free(w);
Packit Service 1d0348
	a->format_data = NULL;
Packit Service 1d0348
	return (ARCHIVE_OK);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348

Packit Service 1d0348
/* private routines */
Packit Service 1d0348
static void
Packit Service 1d0348
xstrftime(struct archive_string *as, const char *fmt, time_t t)
Packit Service 1d0348
{
Packit Service 1d0348
/** like strftime(3) but for time_t objects */
Packit Service 1d0348
	struct tm *rt;
Packit Service 1d0348
#if defined(HAVE_GMTIME_R) || defined(HAVE__GMTIME64_S)
Packit Service 1d0348
	struct tm timeHere;
Packit Service 1d0348
#endif
Packit Service 1d0348
	char strtime[100];
Packit Service 1d0348
	size_t len;
Packit Service 1d0348
Packit Service 1d0348
#ifdef HAVE_GMTIME_R
Packit Service 1d0348
	if ((rt = gmtime_r(&t, &timeHere)) == NULL)
Packit Service 1d0348
		return;
Packit Service 1d0348
#elif defined(HAVE__GMTIME64_S)
Packit Service 1d0348
	_gmtime64_s(&timeHere, &t);
Packit Service 1d0348
#else
Packit Service 1d0348
	if ((rt = gmtime(&t)) == NULL)
Packit Service 1d0348
		return;
Packit Service 1d0348
#endif
Packit Service 1d0348
	/* leave the hard yacker to our role model strftime() */
Packit Service 1d0348
	len = strftime(strtime, sizeof(strtime)-1, fmt, rt);
Packit Service 1d0348
	archive_strncat(as, strtime, len);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static ssize_t
Packit Service 1d0348
_popul_ehdr(struct archive_string *tgt, size_t tsz, warc_essential_hdr_t hdr)
Packit Service 1d0348
{
Packit Service 1d0348
	static const char _ver[] = "WARC/1.0\r\n";
Packit Service 1d0348
	static const char * const _typ[LAST_WT] = {
Packit Service 1d0348
		NULL, "warcinfo", "metadata", "resource", NULL
Packit Service 1d0348
	};
Packit Service 1d0348
	char std_uuid[48U];
Packit Service 1d0348
Packit Service 1d0348
	if (hdr.type == WT_NONE || hdr.type > WT_RSRC) {
Packit Service 1d0348
		/* brilliant, how exactly did we get here? */
Packit Service 1d0348
		return -1;
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	archive_strcpy(tgt, _ver);
Packit Service 1d0348
Packit Service 1d0348
	archive_string_sprintf(tgt, "WARC-Type: %s\r\n", _typ[hdr.type]);
Packit Service 1d0348
Packit Service 1d0348
	if (hdr.tgturi != NULL) {
Packit Service 1d0348
		/* check if there's a xyz:// */
Packit Service 1d0348
		static const char _uri[] = "";
Packit Service 1d0348
		static const char _fil[] = "file://";
Packit Service 1d0348
		const char *u;
Packit Service 1d0348
		char *chk = strchr(hdr.tgturi, ':');
Packit Service 1d0348
Packit Service 1d0348
		if (chk != NULL && chk[1U] == '/' && chk[2U] == '/') {
Packit Service 1d0348
			/* yep, it's definitely a URI */
Packit Service 1d0348
			u = _uri;
Packit Service 1d0348
		} else {
Packit Service 1d0348
			/* hm, best to prepend file:// then */
Packit Service 1d0348
			u = _fil;
Packit Service 1d0348
		}
Packit Service 1d0348
		archive_string_sprintf(tgt,
Packit Service 1d0348
			"WARC-Target-URI: %s%s\r\n", u, hdr.tgturi);
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	/* record time is usually when the http is sent off,
Packit Service 1d0348
	 * just treat the archive writing as such for a moment */
Packit Service 1d0348
	xstrftime(tgt, "WARC-Date: %Y-%m-%dT%H:%M:%SZ\r\n", hdr.rtime);
Packit Service 1d0348
Packit Service 1d0348
	/* while we're at it, record the mtime */
Packit Service 1d0348
	xstrftime(tgt, "Last-Modified: %Y-%m-%dT%H:%M:%SZ\r\n", hdr.mtime);
Packit Service 1d0348
Packit Service 1d0348
	if (hdr.recid == NULL) {
Packit Service 1d0348
		/* generate one, grrrr */
Packit Service 1d0348
		warc_uuid_t u;
Packit Service 1d0348
Packit Service 1d0348
		_gen_uuid(&u);
Packit Service 1d0348
		/* Unfortunately, archive_string_sprintf does not
Packit Service 1d0348
		 * handle the minimum number following '%'.
Packit Service 1d0348
		 * So we have to use snprintf function here instead
Packit Service 1d0348
		 * of archive_string_snprintf function. */
Packit Service 1d0348
#if defined(_WIN32) && !defined(__CYGWIN__) && !( defined(_MSC_VER) && _MSC_VER >= 1900)
Packit Service 1d0348
#define snprintf _snprintf
Packit Service 1d0348
#endif
Packit Service 1d0348
		snprintf(
Packit Service 1d0348
			std_uuid, sizeof(std_uuid),
Packit Service 1d0348
			"<urn:uuid:%08x-%04x-%04x-%04x-%04x%08x>",
Packit Service 1d0348
			u.u[0U],
Packit Service 1d0348
			u.u[1U] >> 16U, u.u[1U] & 0xffffU,
Packit Service 1d0348
			u.u[2U] >> 16U, u.u[2U] & 0xffffU,
Packit Service 1d0348
			u.u[3U]);
Packit Service 1d0348
		hdr.recid = std_uuid;
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	/* record-id is mandatory, fingers crossed we won't fail */
Packit Service 1d0348
	archive_string_sprintf(tgt, "WARC-Record-ID: %s\r\n", hdr.recid);
Packit Service 1d0348
Packit Service 1d0348
	if (hdr.cnttyp != NULL) {
Packit Service 1d0348
		archive_string_sprintf(tgt, "Content-Type: %s\r\n", hdr.cnttyp);
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	/* next one is mandatory */
Packit Service 1d0348
	archive_string_sprintf(tgt, "Content-Length: %ju\r\n", (uintmax_t)hdr.cntlen);
Packit Service 1d0348
	/**/
Packit Service 1d0348
	archive_strncat(tgt, "\r\n", 2);
Packit Service 1d0348
Packit Service 1d0348
	return (archive_strlen(tgt) >= tsz)? -1: (ssize_t)archive_strlen(tgt);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static int
Packit Service 1d0348
_gen_uuid(warc_uuid_t *tgt)
Packit Service 1d0348
{
Packit Service 1d0348
	archive_random(tgt->u, sizeof(tgt->u));
Packit Service 1d0348
	/* obey uuid version 4 rules */
Packit Service 1d0348
	tgt->u[1U] &= 0xffff0fffU;
Packit Service 1d0348
	tgt->u[1U] |= 0x4000U;
Packit Service 1d0348
	tgt->u[2U] &= 0x3fffffffU;
Packit Service 1d0348
	tgt->u[2U] |= 0x80000000U;
Packit Service 1d0348
	return 0;
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
/* archive_write_set_format_warc.c ends here */