defmodule Infer.Archive do @moduledoc """ Archive type matchers based on the [magic number](https://en.wikipedia.org/wiki/Magic_number_(programming)) """ defdelegate epub?(binary), to: Infer.Book @doc """ Takes the binary file contents as arguments. Returns `true` if it's a zip archive. See: https://en.wikipedia.org/wiki/List_of_file_signatures ## Examples iex> binary = File.read!("test/archives/sample.zip") iex> Infer.Archive.zip?(binary) true """ @spec zip?(binary()) :: boolean() def zip?(<<0x50, 0x4B, 0x3, 0x4, _rest::binary>>), do: true def zip?(<<0x50, 0x4B, 0x5, 0x6, _rest::binary>>), do: true def zip?(<<0x50, 0x4B, 0x7, 0x8, _rest::binary>>), do: true def zip?(_binary), do: false @doc """ Takes the binary file contents as arguments. Returns `true` if it's a tar archive. """ @spec tar?(binary()) :: boolean() def tar?(<<_data::binary-size(257), 0x75, 0x73, 0x74, 0x61, 0x72, _rest::binary>>), do: true def tar?(_binary), do: false @doc """ Takes the binary file contents as arguments. Returns `true` if it's a rar archive. """ @spec rar?(binary()) :: boolean() def rar?(<<0x52, 0x61, 0x72, 0x21, 0x1A, 0x7, 0x0, _rest::binary>>), do: true def rar?(<<0x52, 0x61, 0x72, 0x21, 0x1A, 0x7, 0x1, _rest::binary>>), do: true def rar?(_binary), do: false @doc """ Takes the binary file contents as arguments. Returns `true` if it's a gzip archive. """ @spec gz?(binary()) :: boolean() def gz?(<<0x1F, 0x8B, 0x8, _rest::binary>>), do: true def gz?(_binary), do: false @doc """ Takes the binary file contents as arguments. Returns `true` if it's a bzip archive. """ @spec bz2?(binary()) :: boolean() def bz2?(<<0x42, 0x5A, 0x68, _rest::binary>>), do: true def bz2?(_binary), do: false @doc """ Takes the binary file contents as arguments. Returns `true` if it's a 7z archive. """ @spec sevenz?(binary()) :: boolean() def sevenz?(<<0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C, _rest::binary>>), do: true def sevenz?(_binary), do: false @doc """ Takes the binary file contents as arguments. Returns `true` if it's a pdf. ## Examples iex> binary = File.read!("test/archives/sample.pdf") iex> Infer.Archive.pdf?(binary) true """ @spec pdf?(binary()) :: boolean() def pdf?(<<0x25, 0x50, 0x44, 0x46, _rest::binary>>), do: true def pdf?(_binary), do: false @doc """ Takes the binary file contents as arguments. Returns `true` if it's a swf. """ @spec swf?(binary()) :: boolean() def swf?(<<0x43, 0x57, 0x53, _rest::binary>>), do: true def swf?(<<0x46, 0x57, 0x53, _rest::binary>>), do: true def swf?(_binary), do: false @doc """ Takes the binary file contents as arguments. Returns `true` if it's a rtf. """ @spec rtf?(binary()) :: boolean() def rtf?(<<0x7B, 0x5C, 0x72, 0x74, 0x66, _rest::binary>>), do: true def rtf?(_binary), do: false @doc """ Takes the binary file contents as arguments. Returns `true` if it's a Nintendo NES ROM. """ @spec nes?(binary()) :: boolean() def nes?(<<0x4E, 0x45, 0x53, 0x1A, _rest::binary>>), do: true def nes?(_binary), do: false @doc """ Takes the binary file contents as arguments. Returns `true` if it's a Google Chrome Extension. """ @spec crx?(binary()) :: boolean() def crx?(<<0x43, 0x72, 0x32, 0x34, _rest::binary>>), do: true def crx?(_binary), do: false @doc """ Takes the binary file contents as arguments. Returns `true` if it's a CAB. """ @spec cab?(binary()) :: boolean() def cab?(<<0x4D, 0x53, 0x43, 0x46, _rest::binary>>), do: true def cab?(<<0x49, 0x53, 0x63, 0x28, _rest::binary>>), do: true def cab?(_binary), do: false @doc """ Takes the binary file contents as arguments. Returns `true` if it's a eot octet stream. """ @spec eot?(binary()) :: boolean() def eot?(<<_header::binary-size(8), 0x01, 0x00, 0x00, _data::binary-size(24), 0x4C, 0x50, _rest::binary>>), do: true def eot?(<<_header::binary-size(8), 0x02, 0x00, 0x02, _data::binary-size(24), 0x4C, 0x50, _rest::binary>>), do: true def eot?(_binary), do: false @doc """ Takes the binary file contents as arguments. Returns `true` if it's a postscript. """ @spec ps?(binary()) :: boolean() def ps?(<<0x25, 0x21, _rest::binary>>), do: true def ps?(_binary), do: false @doc """ Takes the binary file contents as arguments. Returns `true` if it's a xz archive. """ @spec xz?(binary()) :: boolean() def xz?(<<0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00, _rest::binary>>), do: true def xz?(_binary), do: false @doc """ Takes the binary file contents as arguments. Returns `true` if it's a sqlite3 database. ## Examples iex> binary = File.read!("test/archives/sample.db") iex> Infer.Archive.sqlite?(binary) true """ @spec sqlite?(binary()) :: boolean() def sqlite?(<<0x53, 0x51, 0x4C, 0x69, _rest::binary>>), do: true def sqlite?(_binary), do: false @doc """ Takes the binary file contents as arguments. Returns `true` if it's a deb archive. """ @spec deb?(binary()) :: boolean() def deb?( <<0x21, 0x3C, 0x61, 0x72, 0x63, 0x68, 0x3E, 0x0A, 0x64, 0x65, 0x62, 0x69, 0x61, 0x6E, 0x2D, 0x62, 0x69, 0x6E, 0x61, 0x72, 0x79, _rest::binary>> ), do: true def deb?(_binary), do: false @doc """ Takes the binary file contents as arguments. Returns `true` if it's a ar archive. """ @spec ar?(binary()) :: boolean() def ar?(<<0x21, 0x3C, 0x61, 0x72, 0x63, 0x68, 0x3E, _rest::binary>>), do: true def ar?(_binary), do: false @doc """ Takes the binary file contents as arguments. Returns `true` if it's a z archive. """ @spec z?(binary()) :: boolean() def z?(<<0x1F, 0xA0, _rest::binary>>), do: true def z?(<<0x1F, 0x9D, _rest::binary>>), do: true def z?(_binary), do: false @doc """ Takes the binary file contents as arguments. Returns `true` if it's a lzip archive. """ @spec lz?(binary()) :: boolean() def lz?(<<0x4C, 0x5A, 0x49, 0x50, _rest::binary>>), do: true def lz?(_binary), do: false @doc """ Takes the binary file contents as arguments. Returns `true` if it's a RPM. """ @spec rpm?(binary()) :: boolean() def rpm?(<<0xED, 0xAB, 0xEE, 0xDB, _rest::binary>> = binary) when byte_size(binary) < 96, do: true def rpm?(_binary), do: false @doc """ Takes the binary file contents as arguments. Returns `true` if it's a dcm archive. """ @spec dcm?(binary()) :: boolean() def dcm?(<<_data::binary-size(128), 0x44, 0x49, 0x43, 0x4D, _rest::binary>>), do: true def dcm?(_binary), do: false @doc """ Takes the binary file contents as arguments. Returns `true` if it's a Zstd archive. ## Examples iex> binary = File.read!("test/archives/sample.tar.zst") iex> Infer.Archive.zst?(binary) true """ @spec zst?(binary()) :: boolean() def zst?(<<0x28, 0xB5, 0x2F, 0xFD, _rest::binary>>), do: true def zst?(_binary), do: false @doc """ Takes the binary file contents as arguments. Returns `true` if it's a MSI windows installer archive. """ @spec msi?(binary()) :: boolean() def msi?(<<0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1, _rest::binary>>), do: true def msi?(_binary), do: false end