{-# LANGUAGE RecordWildCards #-}

-- |
-- Module: Codec.RPM.Types
-- Copyright: (c) 2016-2017 Red Hat, Inc.
-- License: LGPL
--
-- Maintainer: https://github.com/weldr
-- Stability: stable
-- Portability: portable

module Codec.RPM.Types(RPM(..),
                       Lead(..),
                       Header(..),
                       SectionHeader(..))

 where

import qualified Data.ByteString as BS
import           Data.Word(Word8, Word16, Word32)
import           Text.PrettyPrint.HughesPJClass(Pretty(..))
import           Text.PrettyPrint((<>), ($$), nest, text, vcat)

import Codec.RPM.Tags

-- | The top level RPM record.  This contains everything in an RPM file, except for the
-- magic value.
data RPM = RPM {
    -- | The 'Lead', an obsolete record used for identifying RPMs.
    rpmLead :: Lead,
    -- | Special 'Header' entries that can be used to verify the integrity of an RPM.  This
    -- is represented as a list because it is technically possible for there to be multiple
    -- signature headers, but in practice there is only ever one.  This is the case even if
    -- multiple signatures are present.  This situation will be represented by multiple 'Tag's
    -- that can be examined to get each signature.  When checking signatures, note that they
    -- only apply to the 'rpmHeaders' and the 'rpmArchive'.
    rpmSignatures :: [Header],
    -- | 'Header' entries that contain all the metadata about an RPM.  There could technically
    -- be several entries here too, but in practice there is only ever one.  It is likely
    -- that each 'Header' will contain many 'Tag's, as RPMs tend to have a large amount of
    -- metadata.
    rpmHeaders :: [Header],
    -- | The contents of the RPM, stored as a compressed CPIO archive.
    rpmArchive :: BS.ByteString }
 deriving(Eq, Show)

instance Pretty RPM where
    pPrint RPM{..} =
        vcat [ text "RPM:",
               nest 2 (text "rpmLead = "    $$ nest 2 (pPrint rpmLead)),
               nest 2 (text "rpmSignatures = " $$ nest 2 (vcat $ map pPrint rpmSignatures)),
               nest 2 (text "rpmHeaders = " $$ nest 2 (vcat $ map pPrint rpmHeaders)),
               nest 2 (text "rpmArchive = ...") ]

-- | Following the magic value that identifies a data stream as an RPM, the Lead is the very
-- first part of the file.  Due to its small size and inflexibility, it is largely obsolete
-- and its use is discouraged even inside of the RPM library.  It is generally only used as
-- additional help beyond the magic value in verifying something is an RPM.  The lead is
-- only exposed here for completeness.
data Lead = Lead {
    -- | The major version number of this RPM, for instance 0x03 for version 3.x.
    rpmMajor    :: Word8,
    -- | The minor version number of this RPM, for instance 0x00 for version 3.0.
    rpmMinor    :: Word8,
    -- | Is this a binary package (0x0000) or a source package (0x0001)?  Other types
    -- may be defined in the future.
    rpmType     :: Word16,
    -- | What platform was this package built for?  x86 is 0x0001.  Many other values
    -- are defined.  See /usr/lib/rpm/rpmrc for the possibilities.
    rpmArchNum  :: Word16,
    -- | The package name, as a NEVRA.  This name is constrained to 66 bytes.  Shorter
    -- names are padded with nulls.
    rpmName     :: String,
    -- | What operating system was this package built for?  Linux is 0x0001.  Many other
    -- values are defined.  See /usr/lib/rpm/rpmrc for the possibilities.
    rpmOSNum    :: Word16,
    -- | What type of signature is used in this RPM?  For now, this appears to always
    -- be set to 0x0005.
    rpmSigType  :: Word16 }
 deriving(Eq, Show)

instance Pretty Lead where
    pPrint Lead{..} =
        vcat [ text "Lead:",
               nest 2 $ text "rpmMajor:   " <> text (show rpmMajor),
               nest 2 $ text "rpmMinor:   " <> text (show rpmMinor),
               nest 2 $ text "rpmType:    " <> text (show rpmType),
               nest 2 $ text "rpmArchNum: " <> text (show rpmArchNum),
               nest 2 $ text "rpmName:    " <> text rpmName,
               nest 2 $ text "rpmOSNum:   " <> text (show rpmOSNum),
               nest 2 $ text "rpmSigType: " <> text (show rpmSigType) ]

-- | A Header represents a block of metadata.  It is used twice in the RPM - as the
-- representation for signatures and as the representation for regular metadata.  Internally,
-- the header is a list of tag descriptors followed by a data store.  These descriptors
-- index into the store and explain what type of thing should be found and how many things
-- should be read.
--
-- Here, the hard work of figuring that out is already done and the results provided as a
-- list of 'Tag's.  The raw store itself is provided for completeness, in case further
-- processing needs to be done on the RPM.  For most users, this will never be needed.
data Header = Header {
    -- | Each header begins with its own 'SectionHeader', describing what type of header
    -- follows and how many entries that header contains.
    headerSectionHeader :: SectionHeader,
    -- | A list of 'Tag' entries and their values.  There are many, many types of tags.
    headerTags :: [Tag],
    -- | The raw header store.
    headerStore :: BS.ByteString }
 deriving(Eq, Show)

instance Pretty Header where
    pPrint Header{..} =
        vcat [ text "Header:",
               nest 2 $ text "headerSectionHeader = " $$ nest 2 (pPrint headerSectionHeader),
               nest 2 $ text "headerTags = "          $$ nest 2 (vcat $ map pPrint headerTags),
               nest 2 $ text "headerStore = ..." ]

-- | The SectionHeader is useful in parsing an RPM.  It allows for figuring out where
-- each section occurs, how large it is, and so forth.  It is likely not useful for
-- consumers of this libary.  Just like with the top-level 'RPM' record, section headers
-- are preceeded with a magic value that is not exposed here.
data SectionHeader = SectionHeader {
    -- | The version of this header structure, currently only 0x01.
    sectionVersion  :: Word8,
    -- | How many 'Tag' entries are stored in this header?
    sectionCount    :: Word32,
    -- | What is the size of the data store in this header?
    sectionSize     :: Word32 }
 deriving(Eq, Show)

instance Pretty SectionHeader where
    pPrint SectionHeader{..} =
        vcat [ text "SectionHeader:",
               nest 2 $ text "sectionHeader: " <> text (show sectionVersion),
               nest 2 $ text "sectionCount:  " <> text (show sectionCount),
               nest 2 $ text "sectionSize:   " <> text (show sectionSize) ]