/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*           Copyright (c) 1987-2006 AT&T Knowledge Ventures            *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                      by AT&T Knowledge Ventures                      *
*                                                                      *
*                A copy of the License is available at                 *
*            http://www.opensource.org/licenses/cpl1.0.txt             *
*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                                                                      *
***********************************************************************/
#pragma prototyped

/*
 * pax rpm format
 */

#include "format.h"

#define RPM_MAGIC	0xedabeedb
#define RPM_CIGAM	0xdbeeabed

#define RPM_HEAD_MAGIC	0x8eade801

typedef struct
{
	unsigned int_4	magic;
	unsigned char	major;
	unsigned char	minor;
	int_2		type;
} Rpm_magic_t;

typedef struct
{
	int_2		archnum;
	char		name[66];
	int_2		osnum;
	int_2		sigtype;
	char		pad[16];
} Rpm_lead_t;

typedef struct
{
	int_2		archnum;
	char		name[66];
	unsigned int_4	specoff;
	unsigned int_4	speclen;
	unsigned int_4	archoff;
} Rpm_lead_old_t;

typedef struct
{
	unsigned int_4	entries;
	unsigned int_4	datalen;
} Rpm_head_t;

typedef struct
{
	unsigned int_4	tag;
	unsigned int_4	type;
	unsigned int_4	offset;
	unsigned int_4	size;
} Rpm_entry_t;

static int
rpm_getprologue(Pax_t* pax, Format_t* fp, register Archive_t* ap, File_t* f, unsigned char* buf, size_t size)
{
	Rpm_magic_t	magic;
	Rpm_magic_t	verify;
	Rpm_lead_t	lead;
	Rpm_lead_old_t	lead_old;
	Rpm_head_t	head;
	char*		s;
	int		i;
	int		swap;
	long		num;
	unsigned char	zip[2];

	if (size < sizeof(magic))
		return 0;
	memcpy(&magic, buf, sizeof(magic));
	verify.magic = RPM_MAGIC;
	if ((swap = swapop(&verify.magic, &magic.magic, sizeof(magic.magic))) < 0)
		return 0;
	message((-2, "%s: magic swap=%d magic=%08x major=%d minor=%d", ap->name, swap, magic.magic, magic.major, magic.minor));
	if (magic.major == 1)
	{
		if (size < (sizeof(magic) + sizeof(lead_old)))
			return 0;
		paxread(pax, ap, NiL, (off_t)sizeof(magic), (off_t)sizeof(magic), 0);
		if (paxread(pax, ap, &lead_old, (off_t)sizeof(lead_old), (off_t)sizeof(lead_old), 0) <= 0)
			return 0;
		if (swap)
			swapmem(swap, &lead_old, &lead_old, sizeof(lead_old));
		if (paxseek(pax, ap, (off_t)lead_old.archoff, SEEK_SET, 0) != (off_t)lead_old.archoff)
		{
			error(2, "%s: %s embedded archive seek error", ap->name, fp->name);
			return -1;
		}
	}
	else if (magic.major)
	{
		if (size < (sizeof(magic) + sizeof(lead)))
			return 0;
		paxread(pax, ap, NiL, (off_t)sizeof(magic), (off_t)sizeof(magic), 0);
		if (paxread(pax, ap, &lead, (off_t)sizeof(lead), (off_t)sizeof(lead), 0) <= 0)
			return 0;
		memcpy(state.volume, lead.name, sizeof(state.volume) - 1);
		if (swap & 1)
			swapmem(swap & 1, &lead, &lead, sizeof(lead));
		message((-2, "%s: lead name=%s archnum=%d osnum=%d sigtype=%d", ap->name, state.volume, lead.archnum, lead.osnum, lead.sigtype));
		if (s = strrchr(ap->name, '/'))
			s++;
		else
			s = ap->name;
		if (!memcmp(s, state.volume, strlen(state.volume)))
			state.volume[0] = 0;
		switch (lead.sigtype)
		{
		case 0:
			num = 0;
			break;
		case 1:
			num = 256;
			if (paxread(pax, ap, NiL, (off_t)num, (off_t)num, 0) <= 0)
			{
				error(2, "%s: %s format header %ld byte data block expected", ap->name, fp->name, num);
				return -1;
			}
			break;
		case 5:
			for (;;)
			{
				if (paxread(pax, ap, zip, (off_t)sizeof(zip), (off_t)sizeof(zip), 0) <= 0)
				{
					error(2, "%s: %s format header magic expected at offset %ld", ap->name, fp->name, ap->io->offset + ap->io->count);
					return -1;
				}
				if (zip[0] == 0x1f && zip[1] == 0x8b)
				{
					paxunread(pax, ap, zip, (off_t)sizeof(zip));
					break;
				}
				num = (ap->io->count - 2) & 7;
				message((-2, "%s: align pad=%ld", ap->name, num ? (8 - num) : num));
				switch (num)
				{
				case 0:
					paxunread(pax, ap, zip, (off_t)2);
					break;
				case 7:
					paxunread(pax, ap, zip + 1, (off_t)1);
					break;
				case 6:
					break;
				default:
					num = 6 - num;
					if (paxread(pax, ap, NiL, (off_t)num, (off_t)num, 0) <= 0)
					{
						error(2, "%s: %s format header %ld byte pad expected", ap->name, fp->name, num);
						return -1;
					}
					break;
				}
				if (paxread(pax, ap, &verify, (off_t)sizeof(verify), (off_t)sizeof(verify), 0) <= 0)
				{
					error(2, "%s: %s format header magic expected at offset %ld", ap->name, fp->name, ap->io->offset + ap->io->count);
					return -1;
				}
				if (((unsigned char*)&verify)[0] == 0x1f && ((unsigned char*)&verify)[1] == 0x8b)
				{
					paxunread(pax, ap, &verify, (off_t)sizeof(verify));
					break;
				}
				if (swap)
				{
					swapmem(swap, &verify.magic, &verify.magic, sizeof(verify.magic));
					if (swap & 1)
						swapmem(swap & 1, &verify.type, &verify.type, sizeof(verify.type));
				}
				message((-2, "%s: verify magic=%08x major=%d minor=%d type=%d", ap->name, verify.magic, verify.major, verify.minor, verify.type));
				if (verify.magic != RPM_HEAD_MAGIC)
				{
					error(2, "%s: invalid %s format signature header magic", ap->name, fp->name);
					return -1;
				}
				if (paxread(pax, ap, &head, (off_t)sizeof(head), (off_t)sizeof(head), 0) <= 0)
				{
					error(2, "%s: %s format signature header expected", ap->name, fp->name);
					return -1;
				}
				if (swap)
					swapmem(swap, &head, &head, sizeof(head));
				num = head.entries * sizeof(Rpm_entry_t) + head.datalen;
				message((-2, "%s: head entries=%lu datalen=%lu num=%lu", ap->name, head.entries, head.datalen, num));
				if (paxread(pax, ap, NiL, (off_t)num, (off_t)num, 0) <= 0)
				{
					error(2, "%s: %s format header %ld byte data block expected", ap->name, fp->name, num);
					return -1;
				}
			}
			break;
		default:
			error(2, "%s: %s format version %d.%d signature type %d not supported", ap->name, fp->name, magic.major, magic.minor, lead.sigtype);
			return -1;
		}
	}
	else
	{
		error(2, "%s: %s format version %d.%d not supported", ap->name, fp->name, magic.major, magic.minor);
		return -1;
	}
	ap->entry = 0;
	ap->swap = 0;
	ap->swapio = 0;
	ap->volume--;
	i = state.volume[0];
	if (getprologue(ap) <= 0)
	{
		error(2, "%s: %s format embedded archive expected", ap->name, fp->name);
		return -1;
	}
	state.volume[0] = i;
	ap->package = strdup(sfprints("%s %d.%d", fp->name, magic.major, magic.minor));
	return 1;
}

Format_t	pax_rpm_format =
{
	"rpm",
	0,
	"Redhat rpm package encapsulated cpio",
	0,
	ARCHIVE|IN,
	DEFBUFFER,
	DEFBLOCKS,
	0,
	PAXNEXT(pax_rpm_next),
	0,
	0,
	rpm_getprologue,
};

PAXLIB(&pax_rpm_format)
