XAR (eXtensible ARchive): Go parsing library

From Wikipedia:

"XAR (short for eXtensible ARchive format) is an open source file archiver and the archiver's file format. It was created within the OpenDarwin project and is used in macOS X 10.5 and up for software installation routines, as well as browser extensions in Safari 5.0 and up."

File extension

["xar", "pkg", "xip"]

KS implementation details

License: CC0-1.0
Minimal Kaitai Struct required: 0.9

References

This page hosts a formal specification of XAR (eXtensible ARchive) using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Go source code to parse XAR (eXtensible ARchive)

xar.go

// Code generated by kaitai-struct-compiler from a .ksy source file. DO NOT EDIT.

import (
	"github.com/kaitai-io/kaitai_struct_go_runtime/kaitai"
	"bytes"
)


/**
 * From [Wikipedia](https://en.wikipedia.org/wiki/Xar_(archiver)):
 * 
 * "XAR (short for eXtensible ARchive format) is an open source file archiver
 * and the archiver's file format. It was created within the OpenDarwin project
 * and is used in macOS X 10.5 and up for software installation routines, as
 * well as browser extensions in Safari 5.0 and up."
 * @see <a href="https://github.com/mackyle/xar/wiki/xarformat">Source</a>
 */

type Xar_ChecksumAlgorithmsApple int
const (
	Xar_ChecksumAlgorithmsApple__None Xar_ChecksumAlgorithmsApple = 0
	Xar_ChecksumAlgorithmsApple__Sha1 Xar_ChecksumAlgorithmsApple = 1
	Xar_ChecksumAlgorithmsApple__Md5 Xar_ChecksumAlgorithmsApple = 2
	Xar_ChecksumAlgorithmsApple__Sha256 Xar_ChecksumAlgorithmsApple = 3
	Xar_ChecksumAlgorithmsApple__Sha512 Xar_ChecksumAlgorithmsApple = 4
)
type Xar struct {
	HeaderPrefix *Xar_FileHeaderPrefix
	Header *Xar_FileHeader
	Toc *Xar_TocType
	_io *kaitai.Stream
	_root *Xar
	_parent interface{}
	_raw_Header []byte
	_raw_Toc []byte
	_raw__raw_Toc []byte
	_f_checksumAlgorithmOther bool
	checksumAlgorithmOther int8
}
func NewXar() *Xar {
	return &Xar{
	}
}

func (this *Xar) Read(io *kaitai.Stream, parent interface{}, root *Xar) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp1 := NewXar_FileHeaderPrefix()
	err = tmp1.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.HeaderPrefix = tmp1
	tmp2, err := this._io.ReadBytes(int((this.HeaderPrefix.LenHeader - 6)))
	if err != nil {
		return err
	}
	tmp2 = tmp2
	this._raw_Header = tmp2
	_io__raw_Header := kaitai.NewStream(bytes.NewReader(this._raw_Header))
	tmp3 := NewXar_FileHeader()
	err = tmp3.Read(_io__raw_Header, this, this._root)
	if err != nil {
		return err
	}
	this.Header = tmp3
	tmp4, err := this._io.ReadBytes(int(this.Header.LenTocCompressed))
	if err != nil {
		return err
	}
	tmp4 = tmp4
	this._raw__raw_Toc = tmp4
	tmp5, err := kaitai.ProcessZlib(this._raw__raw_Toc)
	if err != nil {
		return err
	}
	this._raw_Toc = tmp5
	_io__raw_Toc := kaitai.NewStream(bytes.NewReader(this._raw_Toc))
	tmp6 := NewXar_TocType()
	err = tmp6.Read(_io__raw_Toc, this, this._root)
	if err != nil {
		return err
	}
	this.Toc = tmp6
	return err
}

/**
 * @see <a href="https://github.com/mackyle/xar/blob/66d451d/xar/include/xar.h.in#L85">Source</a>
 */
func (this *Xar) ChecksumAlgorithmOther() (v int8, err error) {
	if (this._f_checksumAlgorithmOther) {
		return this.checksumAlgorithmOther, nil
	}
	this.checksumAlgorithmOther = int8(3)
	this._f_checksumAlgorithmOther = true
	return this.checksumAlgorithmOther, nil
}

/**
 * internal; access `_root.header` instead
 */

/**
 * zlib compressed XML further describing the content of the archive
 */
type Xar_FileHeaderPrefix struct {
	Magic []byte
	LenHeader uint16
	_io *kaitai.Stream
	_root *Xar
	_parent *Xar
}
func NewXar_FileHeaderPrefix() *Xar_FileHeaderPrefix {
	return &Xar_FileHeaderPrefix{
	}
}

func (this *Xar_FileHeaderPrefix) Read(io *kaitai.Stream, parent *Xar, root *Xar) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp7, err := this._io.ReadBytes(int(4))
	if err != nil {
		return err
	}
	tmp7 = tmp7
	this.Magic = tmp7
	if !(bytes.Equal(this.Magic, []uint8{120, 97, 114, 33})) {
		return kaitai.NewValidationNotEqualError([]uint8{120, 97, 114, 33}, this.Magic, this._io, "/types/file_header_prefix/seq/0")
	}
	tmp8, err := this._io.ReadU2be()
	if err != nil {
		return err
	}
	this.LenHeader = uint16(tmp8)
	return err
}

/**
 * internal; access `_root.header.len_header` instead
 */
type Xar_FileHeader struct {
	Version uint16
	LenTocCompressed uint64
	TocLengthUncompressed uint64
	ChecksumAlgorithmInt uint32
	ChecksumAlgName string
	_io *kaitai.Stream
	_root *Xar
	_parent *Xar
	_f_checksumAlgorithmName bool
	checksumAlgorithmName string
	_f_hasChecksumAlgName bool
	hasChecksumAlgName bool
	_f_lenHeader bool
	lenHeader uint16
}
func NewXar_FileHeader() *Xar_FileHeader {
	return &Xar_FileHeader{
	}
}

func (this *Xar_FileHeader) Read(io *kaitai.Stream, parent *Xar, root *Xar) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp9, err := this._io.ReadU2be()
	if err != nil {
		return err
	}
	this.Version = uint16(tmp9)
	if !(this.Version == 1) {
		return kaitai.NewValidationNotEqualError(1, this.Version, this._io, "/types/file_header/seq/0")
	}
	tmp10, err := this._io.ReadU8be()
	if err != nil {
		return err
	}
	this.LenTocCompressed = uint64(tmp10)
	tmp11, err := this._io.ReadU8be()
	if err != nil {
		return err
	}
	this.TocLengthUncompressed = uint64(tmp11)
	tmp12, err := this._io.ReadU4be()
	if err != nil {
		return err
	}
	this.ChecksumAlgorithmInt = uint32(tmp12)
	tmp13, err := this.HasChecksumAlgName()
	if err != nil {
		return err
	}
	if (tmp13) {
		tmp14, err := this._io.ReadBytesFull()
		if err != nil {
			return err
		}
		tmp14 = kaitai.BytesTerminate(tmp14, 0, false)
		this.ChecksumAlgName = string(tmp14)
		{
			_it := this.ChecksumAlgName
			if !( ((_it != "") && (_it != "none")) ) {
				return kaitai.NewValidationExprError(this.ChecksumAlgName, this._io, "/types/file_header/seq/4")
			}
		}
	}
	return err
}

/**
 * If it is not
 * 
 * * `""` (empty string), indicating an unknown integer value (access
 *   `checksum_algorithm_int` for debugging purposes to find out
 *   what that value is), or
 * * `"none"`, indicating that the TOC checksum is not provided (in that
 *   case, the `<checksum>` property or its `style` attribute should be
 *   missing, or the `style` attribute must be set to `"none"`),
 * 
 * it must exactly match the `style` attribute value of the
 * `<checksum>` property in the root node `<toc>`. See
 * <https://github.com/mackyle/xar/blob/66d451d/xar/lib/archive.c#L345-L371>
 * for reference.
 * 
 * The `xar` (eXtensible ARchiver) program [uses OpenSSL's function
 * `EVP_get_digestbyname`](
 *   https://github.com/mackyle/xar/blob/66d451d/xar/lib/archive.c#L328
 * ) to verify this value (if it's not `""` or `"none"`, of course).
 * So it's reasonable to assume that this can only have one of the values
 * that OpenSSL recognizes.
 */
func (this *Xar_FileHeader) ChecksumAlgorithmName() (v string, err error) {
	if (this._f_checksumAlgorithmName) {
		return this.checksumAlgorithmName, nil
	}
	var tmp15 string;
	tmp16, err := this.HasChecksumAlgName()
	if err != nil {
		return "", err
	}
	if (tmp16) {
		tmp15 = this.ChecksumAlgName
	} else {
		var tmp17 string;
		if (this.ChecksumAlgorithmInt == Xar_ChecksumAlgorithmsApple__None) {
			tmp17 = "none"
		} else {
			var tmp18 string;
			if (this.ChecksumAlgorithmInt == Xar_ChecksumAlgorithmsApple__Sha1) {
				tmp18 = "sha1"
			} else {
				var tmp19 string;
				if (this.ChecksumAlgorithmInt == Xar_ChecksumAlgorithmsApple__Md5) {
					tmp19 = "md5"
				} else {
					var tmp20 string;
					if (this.ChecksumAlgorithmInt == Xar_ChecksumAlgorithmsApple__Sha256) {
						tmp20 = "sha256"
					} else {
						var tmp21 string;
						if (this.ChecksumAlgorithmInt == Xar_ChecksumAlgorithmsApple__Sha512) {
							tmp21 = "sha512"
						} else {
							tmp21 = ""
						}
						tmp20 = tmp21
					}
					tmp19 = tmp20
				}
				tmp18 = tmp19
			}
			tmp17 = tmp18
		}
		tmp15 = tmp17
	}
	this.checksumAlgorithmName = string(tmp15)
	this._f_checksumAlgorithmName = true
	return this.checksumAlgorithmName, nil
}
func (this *Xar_FileHeader) HasChecksumAlgName() (v bool, err error) {
	if (this._f_hasChecksumAlgName) {
		return this.hasChecksumAlgName, nil
	}
	tmp22, err := this._root.ChecksumAlgorithmOther()
	if err != nil {
		return false, err
	}
	tmp23, err := this.LenHeader()
	if err != nil {
		return false, err
	}
	tmp25, err := this.LenHeader()
	if err != nil {
		return false, err
	}
	tmp24 := tmp25 % 4
	if tmp24 < 0 {
		tmp24 += 4
	}
	this.hasChecksumAlgName = bool( ((this.ChecksumAlgorithmInt == tmp22) && (tmp23 >= 32) && (tmp24 == 0)) )
	this._f_hasChecksumAlgName = true
	return this.hasChecksumAlgName, nil
}
func (this *Xar_FileHeader) LenHeader() (v uint16, err error) {
	if (this._f_lenHeader) {
		return this.lenHeader, nil
	}
	this.lenHeader = uint16(this._root.HeaderPrefix.LenHeader)
	this._f_lenHeader = true
	return this.lenHeader, nil
}

/**
 * internal; access `checksum_algorithm_name` instead
 */

/**
 * internal; access `checksum_algorithm_name` instead
 */
type Xar_TocType struct {
	XmlString string
	_io *kaitai.Stream
	_root *Xar
	_parent *Xar
}
func NewXar_TocType() *Xar_TocType {
	return &Xar_TocType{
	}
}

func (this *Xar_TocType) Read(io *kaitai.Stream, parent *Xar, root *Xar) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp26, err := this._io.ReadBytesFull()
	if err != nil {
		return err
	}
	tmp26 = tmp26
	this.XmlString = string(tmp26)
	return err
}