#include "system.h"

#ifdef	__LCLINT__
#define	ntohl(_x)	(_x)
#define	ntohs(_x)	(_x)
#define	htonl(_x)	(_x)
#define	htons(_x)	(_x)
#else
#include <netinet/in.h>
#endif	/* __LCLINT__ */

#include "rpmlib.h"

#include "misc.h"
#include "rpmlead.h"
#include "signature.h"

/* 0 = success */
/* 1 = bad magic */
/* 2 = error */
static int readPackageHeaders(FD_t fd, /*@out@*/struct rpmlead * leadPtr, 
			      /*@out@*/Header * sigs, /*@out@*/Header * hdrPtr)
{
    Header hdrBlock;
    struct rpmlead leadBlock;
    Header * hdr = NULL;
    struct rpmlead * lead;
    char * defaultPrefix;
    struct stat sb;
    int_32 true = 1;

    hdr = hdrPtr ? hdrPtr : &hdrBlock;
    lead = leadPtr ? leadPtr : &leadBlock;

    fstat(Fileno(fd), &sb);
    /* if fd points to a socket, pipe, etc, sb.st_size is *always* zero */
    if (S_ISREG(sb.st_mode) && sb.st_size < sizeof(*lead)) return 1;

    if (readLead(fd, lead)) {
	return 2;
    }

    if (lead->magic[0] != RPMLEAD_MAGIC0 || lead->magic[1] != RPMLEAD_MAGIC1 ||
	lead->magic[2] != RPMLEAD_MAGIC2 || lead->magic[3] != RPMLEAD_MAGIC3) {
	return 1;
    }

    switch(lead->major) {
    case 1:
	rpmError(RPMERR_NEWPACKAGE, _("packaging version 1 is not"
		" supported by this version of RPM"));
	return 2;
	/*@notreached@*/ break;
    case 2:
    case 3:
    case 4:
	if (rpmReadSignature(fd, sigs, lead->signature_type)) {
	   return 2;
	}
	*hdr = headerRead(fd, (lead->major >= 3) ?
			  HEADER_MAGIC_YES : HEADER_MAGIC_NO);
	if (*hdr == NULL) {
	    if (sigs != NULL) {
		headerFree(*sigs);
	    }
	    return 2;
	}

	/* We don't use these entries (and rpm >= 2 never have) and they are
	   pretty misleading. Let's just get rid of them so they don't confuse
	   anyone. */
	if (headerIsEntry(*hdr, RPMTAG_FILEUSERNAME))
	    headerRemoveEntry(*hdr, RPMTAG_FILEUIDS);
	if (headerIsEntry(*hdr, RPMTAG_FILEGROUPNAME))
	    headerRemoveEntry(*hdr, RPMTAG_FILEGIDS);

	/* We switched the way we do relocateable packages. We fix some of
	   it up here, though the install code still has to be a bit 
	   careful. This fixup makes queries give the new values though,
	   which is quite handy. */
	if (headerGetEntry(*hdr, RPMTAG_DEFAULTPREFIX, NULL,
			   (void **) &defaultPrefix, NULL)) {
	    defaultPrefix = strcpy(alloca(strlen(defaultPrefix) + 1), 
				   defaultPrefix);
	    stripTrailingSlashes(defaultPrefix);
	    headerAddEntry(*hdr, RPMTAG_PREFIXES, RPM_STRING_ARRAY_TYPE,
			   &defaultPrefix, 1); 
	}

	/* The file list was moved to a more compressed format which not
	   only saves memory (nice), but gives fingerprinting a nice, fat
	   speed boost (very nice). Go ahead and convert old headers to
	   the new style (this is a noop for new headers) */
	compressFilelist(*hdr);

    /* XXX binary rpms always have RPMTAG_SOURCERPM, source rpms do not */
        if (lead->type == RPMLEAD_SOURCE) {
	    if (!headerIsEntry(*hdr, RPMTAG_SOURCEPACKAGE))
	    	headerAddEntry(*hdr, RPMTAG_SOURCEPACKAGE, RPM_INT32_TYPE,
				&true, 1);
	}
	break;

    default:
	rpmError(RPMERR_NEWPACKAGE, _("only packages with major numbers <= 4 "
		"are supported by this version of RPM"));
	return 2;
	/*@notreached@*/ break;
    } 

    if (hdrPtr == NULL) {
	headerFree(*hdr);
    }
    
    return 0;
}

/* 0 = success */
/* 1 = bad magic */
/* 2 = error */
int rpmReadPackageInfo(FD_t fd, Header * signatures, Header * hdr)
{
    return readPackageHeaders(fd, NULL, signatures, hdr);
}

/* 0 = success */
/* 1 = bad magic */
/* 2 = error */
int rpmReadPackageHeader(FD_t fd, Header * hdr, int * isSource, int * major,
		  int * minor)
{
    int rc;
    struct rpmlead lead;

    rc = readPackageHeaders(fd, &lead, NULL, hdr);
    if (rc) return rc;
   
    if (isSource) *isSource = lead.type == RPMLEAD_SOURCE;
    if (major) *major = lead.major;
    if (minor) *minor = lead.minor;
   
    return 0;
}
