defmodule Glific.URI do
@moduledoc """
This file has been copied (and modified a wee bit)from
https://github.com/jerel/ecto_fields/blob/master/lib/fields/url.ex
The ownership, author and license (MIT) remains with the original owner of that repository
"""
@doc false
@spec type :: :string
def type, do: :string
@doc """
Validate that the given value is a valid fully qualified url
## Examples
iex> EctoFields.URL.cast("http://1.1.1.1")
:ok
iex> EctoFields.URL.cast("http://example.com")
:ok
iex> EctoFields.URL.cast("https://example.com")
:ok
iex> EctoFields.URL.cast("http://example.com/test/foo.html?search=1&page=two#header")
:ok
iex> EctoFields.URL.cast("http://example.com:8080/")
:ok
iex> EctoFields.URL.cast("myblog.html")
:error
iex> EctoFields.URL.cast("http://example.com\blog\first")
:error
"""
@spec cast(String.t()) :: :ok | :error
def cast(url) when is_binary(url) and byte_size(url) > 0 do
url
|> validate_protocol
|> validate_host
|> validate_uri
end
def cast(nil), do: :ok
def cast(_), do: :error
defp validate_protocol("http://" <> rest = url) do
{url, rest}
end
defp validate_protocol("https://" <> rest = url) do
{url, rest}
end
defp validate_protocol(_), do: :error
defp validate_host(:error), do: :error
defp validate_host({url, rest}) do
[domain | uri] = String.split(rest, "/")
domain =
case String.split(domain, ":") do
# ipv6
[_, _, _, _, _, _, _, _] -> domain
[domain, _port] -> domain
_ -> domain
end
erl_host = String.to_charlist(domain)
if :inet_parse.domain(erl_host) or
match?({:ok, _}, :inet_parse.ipv4strict_address(erl_host)) or
match?({:ok, _}, :inet_parse.ipv6strict_address(erl_host)) do
{url, Enum.join(uri, "/")}
else
:error
end
end
defp validate_uri(:error), do: :error
defp validate_uri({_url, uri}) do
if uri == URI.encode(uri) |> URI.decode(),
do: :ok,
else: :error
end
end