1.8 KiB · text History 4b6fe3b
defmodule OhMyExOhIrcd.IRC.Vhost do
  @moduledoc """
  Operator-assigned virtual hosts (vhosts).

  An oper can override a user's displayed host — via the `CHGHOST` /
  `SETHOST` IRC commands or the web admin console — replacing the
  hashed-IP cloak in their `nick!user@host` prefix.

  Two layers:

    * **Live** — `set_live/3` messages the target's `Connection` process
      so the change takes effect immediately. If the target is logged in,
      the Connection also persists the vhost to their account.
    * **Persistent** — stored on the account (`users.vhost`) and re-applied
      on every subsequent connection at registration. Set directly for
      offline users via `OhMyExOhIrcd.Accounts.set_vhost_by_email/2`.

  Passing `nil` clears the vhost, reverting the user to their cloak.
  """

  alias OhMyExOhIrcd.Cluster.HordeRegistry

  # Same grammar as `Accounts.User`'s vhost changeset: no spaces and none
  # of `! @ :` (which would corrupt the IRC prefix), 1..64 chars.
  @host_format ~r/^[A-Za-z0-9._\/-]{1,64}$/

  @doc "True iff `host` is a syntactically valid vhost."
  @spec valid_host?(term()) :: boolean()
  def valid_host?(host) when is_binary(host), do: Regex.match?(@host_format, host)
  def valid_host?(_), do: false

  @doc """
  Push a vhost (or `nil` to clear) onto a connected nick's live session.
  Returns `:ok`, or `{:error, :no_such_nick}` if nobody is using `nick`.
  Persistence (for logged-in targets) happens inside the Connection.
  """
  @spec set_live(String.t(), String.t() | nil, String.t()) :: :ok | {:error, :no_such_nick}
  def set_live(nick, host, by) when is_binary(nick) and (is_binary(host) or is_nil(host)) do
    case HordeRegistry.whereis({:user, String.downcase(nick)}) do
      pid when is_pid(pid) ->
        send(pid, {:irc_sethost, host, by})
        :ok

      _ ->
        {:error, :no_such_nick}
    end
  end
end