Storehouse.Check (Storehouse v0.1.0)

Copy Markdown View Source

The type of a file, from its bytes.

The browser's content type is a claim. describe/3 keeps only a type that the bytes of the file prove, from a short allow-list of passive types:

  • PNG, JPEG, GIF and WebP images, from their signatures, with their width and height from their headers: a JPEG's turned as its EXIF orientation says, as browsers draw it. The claim does not matter: a PNG sent as text/html is a PNG.
  • PDF, from %PDF-.
  • MP4 video (video/mp4), from its ftyp box with an MP4 brand (isom, mp41, mp42, avc1, M4V, …), and WebM video (video/webm), from its EBML header with the webm document type. A QuickTime (.mov), Matroska (.mkv) or audio-only (M4A) file is not in the list.
  • ZIP files (PK\x03\x04): the Office Open XML and OpenDocument types when the browser claims one of them, else application/zip.
  • The older Office files (the OLE2 signature): application/msword, application/vnd.ms-excel or application/vnd.ms-powerpoint when the browser claims one of them.
  • text/plain, only when the browser claims it and the start of the file is valid UTF-8 with no NUL byte.

Every other file is application/octet-stream: text/html, application/xhtml+xml, image/svg+xml, JavaScript, and any type that is not in the list. Serve such a file as a download; its key ends in .bin (extension/1).

inline?/1 says which types a browser shows in the page (the images, PDF and the videos): serve every other with Content-Disposition: attachment, as Storehouse.Local.Plug does.

Summary

Functions

The type a browser claims for a file, as the checked type it would be if its bytes prove it: a type of the allow-list, else application/octet-stream. A file uploaded straight to a store is stored as this type, and refused when its bytes say otherwise.

Returns the checked content type for a file extension (as extension/1 gives it, and .jpeg), or "application/octet-stream".

Reads the file at path and returns its checked content type, its size and, for an image, its width and height. claim is the browser's type ("image/png; charset=binary", or nil).

As describe/3, of the start of a file (head, its first 256 KB or all of it, as head_size/0 says) and its size in bytes: a file that is in a store and not here, uploaded straight to it.

Returns the file extension of a checked content type. A type that is not in the allow-list gives ".bin".

How much of the start of a file describe_head/4 reads, in bytes.

The content types of the images: PNG, JPEG, GIF and WebP.

Returns true for a checked type that a browser can show in the page: the images, PDF and the videos. Serve every other type with Content-Disposition: attachment.

Every type describe/3 gives, application/octet-stream included.

Types

description()

@type description() :: %{
  content_type: String.t(),
  bytes: non_neg_integer(),
  width: pos_integer() | nil,
  height: pos_integer() | nil
}

What describe/3 finds.

Functions

claimed(claim)

@spec claimed(String.t() | nil) :: String.t()

The type a browser claims for a file, as the checked type it would be if its bytes prove it: a type of the allow-list, else application/octet-stream. A file uploaded straight to a store is stored as this type, and refused when its bytes say otherwise.

Examples

iex> Storehouse.Check.claimed("image/PNG")
"image/png"

iex> Storehouse.Check.claimed("text/html")
"application/octet-stream"

content_type(extension)

@spec content_type(String.t()) :: String.t()

Returns the checked content type for a file extension (as extension/1 gives it, and .jpeg), or "application/octet-stream".

Examples

iex> Storehouse.Check.content_type(".PDF")
"application/pdf"

iex> Storehouse.Check.content_type(".svg")
"application/octet-stream"

describe(path, claim \\ nil, opts \\ [])

@spec describe(Path.t(), String.t() | nil, keyword()) ::
  {:ok, description()} | {:error, term()}

Reads the file at path and returns its checked content type, its size and, for an image, its width and height. claim is the browser's type ("image/png; charset=binary", or nil).

Options

  • :max_bytes - a larger file gives {:error, :too_large}.
  • :max_pixels - an image of more pixels (width × height) gives {:error, :too_large}: one a browser would struggle to draw.

describe_head(head, bytes, claim \\ nil, opts \\ [])

@spec describe_head(binary(), non_neg_integer(), String.t() | nil, keyword()) ::
  {:ok, description()} | {:error, term()}

As describe/3, of the start of a file (head, its first 256 KB or all of it, as head_size/0 says) and its size in bytes: a file that is in a store and not here, uploaded straight to it.

extension(content_type)

@spec extension(String.t()) :: String.t()

Returns the file extension of a checked content type. A type that is not in the allow-list gives ".bin".

Examples

iex> Storehouse.Check.extension("image/jpeg")
".jpg"

iex> Storehouse.Check.extension("text/html")
".bin"

head_size()

@spec head_size() :: pos_integer()

How much of the start of a file describe_head/4 reads, in bytes.

images()

@spec images() :: [String.t()]

The content types of the images: PNG, JPEG, GIF and WebP.

inline?(content_type)

@spec inline?(String.t()) :: boolean()

Returns true for a checked type that a browser can show in the page: the images, PDF and the videos. Serve every other type with Content-Disposition: attachment.

Examples

iex> Storehouse.Check.inline?("video/webm")
true

iex> Storehouse.Check.inline?("text/plain")
false

types()

@spec types() :: [String.t()]

Every type describe/3 gives, application/octet-stream included.