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
)
var values_Xar_ChecksumAlgorithmsApple = map[Xar_ChecksumAlgorithmsApple]struct{}{0: {}, 1: {}, 2: {}, 3: {}, 4: {}}
func (v Xar_ChecksumAlgorithmsApple) isDefined() bool {
	_, ok := values_Xar_ChecksumAlgorithmsApple[v]
	return ok
}
type Xar struct {
	HeaderPrefix *Xar_FileHeaderPrefix
	Header *Xar_FileHeader
	Toc *Xar_TocType
	_io *kaitai.Stream
	_root *Xar
	_parent kaitai.Struct
	_raw_Header []byte
	_raw_Toc []byte
	_raw__raw_Toc []byte
	_f_checksumAlgorithmOther bool
	checksumAlgorithmOther int8
}
func NewXar() *Xar {
	return &Xar{
	}
}

func (this Xar) IO_() *kaitai.Stream {
	return this._io
}

func (this *Xar) Read(io *kaitai.Stream, parent kaitai.Struct, 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._f_checksumAlgorithmOther = true
	this.checksumAlgorithmOther = int8(3)
	return this.checksumAlgorithmOther, nil
}

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

/**
 * zlib compressed XML further describing the content of the archive
 */
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) IO_() *kaitai.Stream {
	return this._io
}

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

	tmp7, err := this._io.ReadU2be()
	if err != nil {
		return err
	}
	this.Version = uint16(tmp7)
	if !(this.Version == 1) {
		return kaitai.NewValidationNotEqualError(1, this.Version, this._io, "/types/file_header/seq/0")
	}
	tmp8, err := this._io.ReadU8be()
	if err != nil {
		return err
	}
	this.LenTocCompressed = uint64(tmp8)
	tmp9, err := this._io.ReadU8be()
	if err != nil {
		return err
	}
	this.TocLengthUncompressed = uint64(tmp9)
	tmp10, err := this._io.ReadU4be()
	if err != nil {
		return err
	}
	this.ChecksumAlgorithmInt = uint32(tmp10)
	tmp11, err := this.HasChecksumAlgName()
	if err != nil {
		return err
	}
	if (tmp11) {
		tmp12, err := this._io.ReadBytesFull()
		if err != nil {
			return err
		}
		tmp12 = kaitai.BytesTerminate(tmp12, 0, false)
		this.ChecksumAlgName = string(tmp12)
		{
			_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
	}
	this._f_checksumAlgorithmName = true
	var tmp13 string;
	tmp14, err := this.HasChecksumAlgName()
	if err != nil {
		return "", err
	}
	if (tmp14) {
		tmp13 = this.ChecksumAlgName
	} else {
		var tmp15 string;
		if (this.ChecksumAlgorithmInt == Xar_ChecksumAlgorithmsApple__None) {
			tmp15 = "none"
		} else {
			var tmp16 string;
			if (this.ChecksumAlgorithmInt == Xar_ChecksumAlgorithmsApple__Sha1) {
				tmp16 = "sha1"
			} else {
				var tmp17 string;
				if (this.ChecksumAlgorithmInt == Xar_ChecksumAlgorithmsApple__Md5) {
					tmp17 = "md5"
				} else {
					var tmp18 string;
					if (this.ChecksumAlgorithmInt == Xar_ChecksumAlgorithmsApple__Sha256) {
						tmp18 = "sha256"
					} else {
						var tmp19 string;
						if (this.ChecksumAlgorithmInt == Xar_ChecksumAlgorithmsApple__Sha512) {
							tmp19 = "sha512"
						} else {
							tmp19 = ""
						}
						tmp18 = tmp19
					}
					tmp17 = tmp18
				}
				tmp16 = tmp17
			}
			tmp15 = tmp16
		}
		tmp13 = tmp15
	}
	this.checksumAlgorithmName = string(tmp13)
	return this.checksumAlgorithmName, nil
}
func (this *Xar_FileHeader) HasChecksumAlgName() (v bool, err error) {
	if (this._f_hasChecksumAlgName) {
		return this.hasChecksumAlgName, nil
	}
	this._f_hasChecksumAlgName = true
	tmp20, err := this._root.ChecksumAlgorithmOther()
	if err != nil {
		return false, err
	}
	tmp21, err := this.LenHeader()
	if err != nil {
		return false, err
	}
	tmp23, err := this.LenHeader()
	if err != nil {
		return false, err
	}
	tmp22 := tmp23 % 4
	if tmp22 < 0 {
		tmp22 += 4
	}
	this.hasChecksumAlgName = bool( ((this.ChecksumAlgorithmInt == tmp20) && (tmp21 >= 32) && (tmp22 == 0)) )
	return this.hasChecksumAlgName, nil
}
func (this *Xar_FileHeader) LenHeader() (v uint16, err error) {
	if (this._f_lenHeader) {
		return this.lenHeader, nil
	}
	this._f_lenHeader = true
	this.lenHeader = uint16(this._root.HeaderPrefix.LenHeader)
	return this.lenHeader, nil
}

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

/**
 * internal; access `checksum_algorithm_name` instead
 */
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) IO_() *kaitai.Stream {
	return this._io
}

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

	tmp24, err := this._io.ReadBytes(int(4))
	if err != nil {
		return err
	}
	tmp24 = tmp24
	this.Magic = tmp24
	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")
	}
	tmp25, err := this._io.ReadU2be()
	if err != nil {
		return err
	}
	this.LenHeader = uint16(tmp25)
	return err
}

/**
 * internal; access `_root.header.len_header` 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) IO_() *kaitai.Stream {
	return this._io
}

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
}