# `MishkaCmsCore.Runtime.FileSignature`
[🔗](https://github.com/mishka-group/mishka_cms_core/blob/v0.0.1/lib/mishka_cms_core/runtime/file_signature.ex#L5)

What a file is, read from its first bytes instead of from the name or the type its sender gave it.

A file name and a MIME type are claims made by whoever uploaded the file. The first bytes are what
the file is, so every upload that a site serves back from its own origin is checked here before it
is stored: `MishkaCmsCore.Runtime.Media` for the media library and
`MishkaCmsCore.Runtime.PageUpload` for a visitor's files.

## Kinds

A kind is a family of formats that share a signature:

| Kind | Extensions | First bytes |
|---|---|---|
| `:png` | `png` | the PNG signature |
| `:jpeg` | `jpg`, `jpeg` | `FF D8 FF` |
| `:gif` | `gif` | `GIF87a` or `GIF89a` |
| `:webp` | `webp` | `RIFF`, four bytes, `WEBP` |
| `:pdf` | `pdf` | `%PDF-` |
| `:iso_media` | `mp4`, `mov` | a box size under 16 MB, then an ISO base media box name (`ftyp`, `moov`, `mdat`, ...) |
| `:matroska` | `webm` | the EBML header `1A 45 DF A3` |
| `:ole2` | `doc`, `xls`, `ppt` | the OLE2 compound file header |
| `:zip` | `docx`, `xlsx`, `pptx`, `odt`, `ods`, `zip` | `PK` and a local file header |
| `:text` | `txt`, `csv` | valid UTF-8 with no NUL byte and no control character but tab and line breaks |

An extension that is not in the table has no kind, so nothing matches it: `html`, `htm`, `svg`,
`xml` and `js` are never a kind, and the bytes of a script do not become a picture by being
named `.png`.

# `kind`

```elixir
@type kind() ::
  :png
  | :jpeg
  | :gif
  | :webp
  | :pdf
  | :iso_media
  | :matroska
  | :ole2
  | :zip
  | :text
```

# `kind`

```elixir
@spec kind(String.t()) :: kind() | nil
```

The kind an extension belongs to, with or without its dot and in any case, or `nil` for one that
is not in the table.

# `matches?`

```elixir
@spec matches?(Path.t(), String.t()) :: boolean()
```

Whether the file at `path` is what `extension` says: the extension has a kind and the first bytes
of the file are that kind.

# `sniff`

```elixir
@spec sniff(Path.t()) :: {:ok, kind()} | :error
```

The kind of the file at `path`, read from its first bytes. `:error` when the file cannot be read
or is none of the kinds in the table.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
