license: MIT

deploy: brunch build --production

watch: brunch watch --stdin

repo_name: Loves/qtfile

license: mit


code:
# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
#
# To ban all spiders from the entire site uncomment the next two lines:
# User-agent: *
# Disallow: /


code:
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
#
# This configuration file is loaded before any dependency and
# is restricted to this project.
use Mix.Config

# General application configuration
config :qtfile,
  ecto_repos: [Qtfile.Repo],
  token_secret_key_base: "REPLACE THIS",
  secret_key_ip: "REPLACE THIS",
  environment: Mix.env()

# Configures the endpoint
config :qtfile, QtfileWeb.Endpoint,
  url: [host: "localhost"],
  secret_key_base: "REPLACE THIS",
  render_errors: [view: QtfileWeb.ErrorView, accepts: ~w(html json)],
  pubsub: [name: Qtfile.PubSub,
           adapter: Phoenix.PubSub.PG2],
  max_file_length: 4 * 1024 * 1024 * 1024

# Configures Elixir's Logger
config :logger, :console,
  format: "$time $metadata[$level] $message\n",
  metadata: [:request_id]

config :arc,
  storage: Arc.Storage.Local,
  storage_dir: "uploads/rooms"

# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
import_config "#{Mix.env}.exs"


code:
use Mix.Config

# For development, we disable any cache and enable
# debugging and code reloading.
#
# The watchers configuration can be used to run external
# watchers to your application. For example, we use it
# with brunch.io to recompile .js and .css sources.
config :qtfile, QtfileWeb.Endpoint,
  http: [port: 4000],
  debug_errors: true,
  code_reloader: true,
  check_origin: false,
  watchers: [node: ["node_modules/brunch/bin/brunch", "watch", "--stdin",
                    cd: Path.expand("../assets", __DIR__)]]

# ## SSL Support
#
# In order to use HTTPS in development, a self-signed
# certificate can be generated by running the following
# command from your terminal:
#
#     openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com" -keyout priv/server.key -out priv/server.pem
#
# The `http:` config above can be replaced with:
#
#     https: [port: 4000, keyfile: "priv/server.key", certfile: "priv/server.pem"],
#
# If desired, both `http:` and `https:` keys can be
# configured to run both http and https servers on
# different ports.

# Watch static and templates for browser reloading.
config :qtfile, QtfileWeb.Endpoint,
  live_reload: [
    patterns: [
      ~r{priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$},
      ~r{priv/gettext/.*(po)$},
      ~r{lib/qtfile_web/views/.*(ex)$},
      ~r{lib/qtfile_web/templates/.*(eex)$}
    ]
  ]

# Do not include metadata nor timestamps in development logs
config :logger, :console, format: "[$level] $message\n"

# Set a higher stacktrace during development. Avoid configuring such
# in production as building large stacktraces may be expensive.
config :phoenix, :stacktrace_depth, 20

# Configure your database
config :qtfile, Qtfile.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: "postgres",
  password: "postgres",
  database: "qtfile_dev",
  hostname: "localhost",
  pool_size: 10


code:
use Mix.Config

# For production, we often load configuration from external
# sources, such as your system environment. For this reason,
# you won't find the :http configuration below, but set inside
# QtfileWeb.Endpoint.init/2 when load_from_system_env is
# true. Any dynamic configuration should be done there.
#
# Don't forget to configure the url host to something meaningful,
# Phoenix uses this information when generating URLs.
#
# Finally, we also include the path to a cache manifest
# containing the digested version of static files. This
# manifest is generated by the mix phx.digest task
# which you typically run after static files are built.
config :qtfile, QtfileWeb.Endpoint,
  load_from_system_env: true,
  url: [host: "example.com", port: 80],
  cache_static_manifest: "priv/static/cache_manifest.json"

# Do not print debug messages in production
config :logger, level: :info

# ## SSL Support
#
# To get SSL working, you will need to add the `https` key
# to the previous section and set your `:url` port to 443:
#
#     config :qtfile, QtfileWeb.Endpoint,
#       ...
#       url: [host: "example.com", port: 443],
#       https: [:inet6,
#               port: 443,
#               keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
#               certfile: System.get_env("SOME_APP_SSL_CERT_PATH")]
#
# Where those two env variables return an absolute path to
# the key and cert in disk or a relative path inside priv,
# for example "priv/ssl/server.key".
#
# We also recommend setting `force_ssl`, ensuring no data is
# ever sent via http, always redirecting to https:
#
#     config :qtfile, QtfileWeb.Endpoint,
#       force_ssl: [hsts: true]
#
# Check `Plug.SSL` for all available options in `force_ssl`.

# ## Using releases
#
# If you are doing OTP releases, you need to instruct Phoenix
# to start the server for all endpoints:
#
#     config :phoenix, :serve_endpoints, true
#
# Alternatively, you can configure exactly which server to
# start per endpoint:
#
#     config :qtfile, QtfileWeb.Endpoint, server: true
#

# Finally import the config/prod.secret.exs
# which should be versioned separately.
import_config "prod.secret.exs"


code:
use Mix.Config

# In this file, we keep production configuration that
# you'll likely want to automate and keep away from
# your version control system.
#
# You should document the content of this
# file or create a script for recreating it, since it's
# kept out of version control and might be hard to recover
# or recreate for your teammates (or yourself later on).
config :qtfile, QtfileWeb.Endpoint,
  secret_key_base: ""

# Configure your database
config :qtfile, Qtfile.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: "postgres",
  password: "postgres",
  database: "qtfile_prod",
  pool_size: 15


code:
use Mix.Config

# We don't run a server during test. If one is required,
# you can enable the server option below.
config :qtfile, QtfileWeb.Endpoint,
  http: [port: 4001],
  server: false

# Print only warnings and errors during test
config :logger, level: :warn

# Configure your database
config :qtfile, Qtfile.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: "postgres",
  password: "postgres",
  database: "qtfile_test",
  hostname: "localhost",
  pool: Ecto.Adapters.SQL.Sandbox


code:
defmodule Qtfile do
  @moduledoc """
  Qtfile keeps the contexts that define your domain
  and business logic.

  Contexts are also responsible for managing your data, regardless
  if it comes from the database, an external API or others.
  """
end


code:
defmodule Qtfile.Accounts do
  @moduledoc """
  The Accounts context.
  """

  import Ecto.Query, warn: false
  alias Qtfile.Repo

  alias Qtfile.Accounts.User
  alias Qtfile.SingleToken

  @doc """
  Returns the list of users.

  ## Examples

      iex> list_users()
      [%User{}, ...]

  """
  def list_users do
    Repo.all(User)
  end

  @doc """
  Gets a single user.

  Raises `Ecto.NoResultsError` if the User does not exist.

  ## Examples

      iex> get_user!(123)
      %User{}

      iex> get_user!(456)
      ** (Ecto.NoResultsError)

  """
  def get_user!(id), do: Repo.get!(User, id)

  def get_user_by_username(username) do
    query = from u in User,
      select: u,
      where: u.username == ^username

    Repo.one(query)
  end

  @doc """
  Creates a user.

  ## Examples

      iex> create_user(%{field: value})
      {:ok, %User{}}

      iex> create_user(%{field: bad_value})
      {:error, %Ecto.Changeset{}}

  """
  def create_user(attrs \\ %{}) do
    IO.inspect attrs
    case SingleToken.verify_token(Map.get(attrs, "token", "")) do
      true ->
        # attrs = Map.put(attrs, "password", Bcrypt.hash_pwd_salt(Map.get(attrs, "password")))
        attrs = if Mix.env == :prod do
          Map.put(attrs, "password", Bcrypt.hash_pwd_salt(Map.get(attrs, "password")))
        else
          Map.put(attrs, "password", Map.get(attrs, "password"))
        end
        |> Map.put("role", "user")
        |> Map.put("secret", :crypto.strong_rand_bytes(16))

        %User{}
        |> User.changeset(attrs)
        # |> Ecto.Changeset.cast_assoc(:credential, with: &Credential.changeset/2)
        # |> Ecto.Changeset.cast_assoc(:token, with: &TokenHash.changeset/2)
        |> Repo.insert()
      false ->
        {:error, :invalid_token}
    end

  end

  @doc """
  Updates a user.

  ## Examples

      iex> update_user(user, %{field: new_value})
      {:ok, %User{}}

      iex> update_user(user, %{field: bad_value})
      {:error, %Ecto.Changeset{}}

  """
  def update_user(%User{} = user, attrs) do
    user
    |> User.changeset(attrs)
    |> Repo.update()
  end

  @doc """
  Deletes a User.

  ## Examples

      iex> delete_user(user)
      {:ok, %User{}}

      iex> delete_user(user)
      {:error, %Ecto.Changeset{}}

  """
  def delete_user(%User{} = user) do
    Repo.delete(user)
  end

  @doc """
  Returns an `%Ecto.Changeset{}` for tracking user changes.

  ## Examples

      iex> change_user(user)
      %Ecto.Changeset{source: %User{}}

  """
  def change_user(%User{} = user) do
    User.changeset(user, %{})
  end

  def authenticate_by_username_password(username, password) do
    username = String.downcase(username)

    query =
      from u in User,
      select: %{hashed_password: u.password, user: u},
      where: fragment("lower(?)", u.username) == type(^username, :string)

    case Repo.one(query) do
      %{hashed_password: hashed_password, user: user} ->
        authenticate_by_username_password_helper(%{hashed_password: hashed_password, user: user}, password)
      nil ->
        {:error, :unauthorized}
    end
  end

  def has_mod_authority(user, room) do
    user.role == "mod" or user.role == "admin" or (room != nil and user.id == room.owner)
  end

  defp authenticate_by_username_password_helper(%{hashed_password: hashed_password, user: user}, password) do
    if Mix.env == :prod do
      cond do
        Bcrypt.verify_pass(password, hashed_password) ->
          {:ok, user}
        true ->
          {:error, :unauthorized}
      end
    else
      cond do
        password == hashed_password ->
          {:ok, user}
        true ->
          {:error, :unauthorized}
      end
    end
  end
end


code:
defmodule Qtfile.Accounts.User do
  use Ecto.Schema
  import Ecto.Changeset
  alias Qtfile.Accounts.User

  schema "users" do
    field :name, :string
    field :password, :string
    field :username, :string
    # Available roles:
    # admin - runs the site
    # mod - moderates the site
    # user - regular user, no special privs
    field :role, :string, default: "user"
    field :secret, :binary
    has_many :files, Qtfile.Files.File, foreign_key: :uploader_id
    has_many :rooms, Qtfile.Rooms.Room, foreign_key: :owner_id

    timestamps()
  end

  @doc false
  def changeset(%User{} = user, attrs) do
    user
    |> cast(attrs, [:name, :username, :password, :role, :secret])
    |> validate_required([:name, :username, :password, :role, :secret])
    |> validate_inclusion(:role, ["admin", "mod", "user"])
    |> unique_constraint(:username)
    |> unique_constraint(:secret)
  end
end


code:
defmodule Qtfile.Application do
  use Application

  # See https://hexdocs.pm/elixir/Application.html
  # for more information on OTP Applications
  def start(_type, _args) do
    import Supervisor.Spec

    if Application.get_env(:qtfile, :environment) == :dev do
      PhoenixCowboyLogging.enable_for(:qtfile, QtfileWeb.Endpoint)
    end

    # Define workers and child supervisors to be supervised
    children = [
      # Start the Ecto repository
      supervisor(Qtfile.Repo, []),
      # Start the endpoint when the application starts
      supervisor(QtfileWeb.Endpoint, []),
      # Start your own worker by calling: Qtfile.Worker.start_link(arg1, arg2, arg3)
      # worker(Qtfile.Worker, [arg1, arg2, arg3]),
      supervisor(QtfileWeb.Presence, []),
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: Qtfile.Supervisor]
    Supervisor.start_link(children, opts)
  end

  # Tell Phoenix to update the endpoint configuration
  # whenever the application is updated.
  def config_change(changed, _new, removed) do
    QtfileWeb.Endpoint.config_change(changed, removed)
    :ok
  end
end


code:
defmodule Qtfile.Bans.Ban do
  use Ecto.Schema
  import Ecto.Changeset
  alias Qtfile.Bans.Ban

  schema "bans" do
    belongs_to :banner, Qtfile.Accounts.User, foreign_key: :banner_id
    belongs_to :room, Qtfile.Rooms.Room, foreign_key: :room_id
    field :end, :utc_datetime
    field :reason, :string
    has_many :user_bans, Qtfile.Bans.UserBan, foreign_key: :ban_id
    has_many :file_bans, Qtfile.Bans.FileBan, foreign_key: :ban_id

    timestamps()
  end

  @doc false
  def changeset(%Ban{} = ban, attrs) do
    ban
    |> cast(attrs, [:end, :reason])
    |> put_assoc(:banner, attrs.banner)
    |> put_assoc(:room, attrs.room)
    |> cast_assoc(:user_bans)
    |> cast_assoc(:file_bans)
    |> validate_required([:end, :reason])
  end
end


code:
defmodule Qtfile.Bans do
  @moduledoc """
  The Bans context.
  """

  import Ecto.Query, warn: false
  alias Qtfile.Repo

  alias Qtfile.Bans.Ban
  alias Qtfile.Bans.UserBan
  alias Qtfile.Bans.IPBan
  alias Qtfile.Bans.FileBan
  alias Qtfile.Accounts.User
  alias Qtfile.Rooms.Room
  alias Qtfile.Files.File

  @doc """
  Returns the list of bans.

  ## Examples

      iex> list_bans()
      [%Ban{}, ...]

  """
  def list_bans do
    Repo.all(Ban)
  end

  @doc """
  Gets a single ban.

  Raises `Ecto.NoResultsError` if the Ban does not exist.

  ## Examples

      iex> get_ban!(123)
      %Ban{}

      iex> get_ban!(456)
      ** (Ecto.NoResultsError)

  """
  def get_ban!(id), do: Repo.get!(Ban, id)

  @doc """
  Creates a ban.

  ## Examples

      iex> create_ban(%{field: value})
      {:ok, %Ban{}}

      iex> create_ban(%{field: bad_value})
      {:error, %Ecto.Changeset{}}

  """
  def create_ban(attrs \\ %{}) do
    %Ban{}
    |> Ban.changeset(attrs)
    |> Repo.insert()
  end

  @doc """
  Updates a ban.

  ## Examples

      iex> update_ban(ban, %{field: new_value})
      {:ok, %Ban{}}

      iex> update_ban(ban, %{field: bad_value})
      {:error, %Ecto.Changeset{}}

  """
  def update_ban(%Ban{} = ban, attrs) do
    ban
    |> Ban.changeset(attrs)
    |> Repo.update()
  end

  @doc """
  Deletes a Ban.

  ## Examples

      iex> delete_ban(ban)
      {:ok, %Ban{}}

      iex> delete_ban(ban)
      {:error, %Ecto.Changeset{}}

  """
  def delete_ban(%Ban{} = ban) do
    Repo.delete(ban)
  end

  @doc """
  Returns an `%Ecto.Changeset{}` for tracking ban changes.

  ## Examples

      iex> change_ban(ban)
      %Ecto.Changeset{source: %Ban{}}

  """
  def change_ban(%Ban{} = ban) do
    Ban.changeset(ban, %{})
  end

  def get_bans_for(%User{} = user, %Room{} = room) do
    query = from user_ban in UserBan,
      where: user_ban.bannee_id == ^user.id,
      join: ban in assoc(user_ban, :ban),
      preload: [ban: ban],
      where: is_nil(ban.room_id) or ban.room_id == ^room.id,
      select: user_ban
    Repo.all(query)
  end

  def get_bans_for(%File{} = file, %Room{} = room) do
    query = from file_ban in FileBan,
      where: file_ban.hash == ^file.hash,
      join: ban in assoc(file_ban, :ban),
      preload: [ban: ban],
      where: is_nil(ban.room_id) or ban.room_id == ^room.id,
      select: file_ban
    Repo.all(query)
  end

  def get_bans_for(ip_address, %Room{} = room)
  when is_binary(ip_address) do
    query = from ip_ban in IPBan,
      where: ip_ban.ip_address == ^ip_address,
      join: user_ban in assoc(ip_ban, :user_ban),
      join: ban in assoc(user_ban, :ban),
      preload: [user_ban: {user_ban, ban: ban}],
      where: is_nil(ban.room_id) or ban.room_id == ^room.id,
      select: ip_ban
    Repo.all(query)
  end

  def get_bans_for(stuff, %Room{} = room)
  when is_list(stuff) do
    Enum.flat_map(stuff, &get_bans_for(&1, room))
  end

  def defaultBan() do
    %{
      "reason" => "No reason given",
      "user_bans" => [],
      "file_bans" => [],
      "global" => false,
      "end" => 0,
    }
  end

  def preprocess_input_for_database(user, room, ban) do
    ban = Map.merge(ban, defaultBan(), fn(_, v1, _) -> v1 end)
    ban_room = if ban["global"] do nil else room end
    can_ban = Qtfile.Accounts.has_mod_authority(user, ban_room)
    if can_ban do
      processed_user_bans_e =
        Qtfile.Util.mapSequence(ban["user_bans"], fn(user_ban) ->
          processed_ip_bans_e =
            Qtfile.Util.mapSequence(user_ban["ip_bans"], fn(ip_ban) ->
              processed_ip_address_e =
                Qtfile.IPAddressObfuscation.ban_filter(room, user, ip_ban["ip_address"])
              case processed_ip_address_e do
                {:ok, processed_ip_address} ->
                  {:ok, %{ip_address: processed_ip_address}}
                {:error, _} = e -> e
              end
            end)
          case processed_ip_bans_e do
            {:ok, processed_ip_bans} ->
              map =
                %{
                  ip_bans: processed_ip_bans,
                  hell: user_ban["hell"],
                  bannee: Qtfile.Accounts.get_user!(user_ban["bannee_id"]),
                }
              {:ok, map}
            {:error, _} = e -> e
          end
        end)
      processed_file_bans =
        Enum.map(ban["file_bans"], fn(file_ban) ->
          %{hash: file_ban["hash"]}
        end)
      processed_ban_e =
        case processed_user_bans_e do
          {:ok, processed_user_bans} ->
            ban =
              ban
              |> Qtfile.Util.multiPut([
                user_bans: processed_user_bans,
                file_bans: processed_file_bans,
                reason: ban["reason"],
                end: DateTime.from_unix!(ban["end"]),
                banner: user,
                room: ban_room,
              ])
              |> Qtfile.Util.multiDelete([
                "user_bans",
                "file_bans",
                "reason",
                "end",
                "global",
              ])
            {:ok, ban}
          {:error, _} = e -> e
        end
      processed_ban_e
    else
      {:error, :insufficient_ban_permission}
    end
  end
end


code:
defmodule Qtfile.Bans.FileBan do
  use Ecto.Schema
  import Ecto.Changeset
  alias Qtfile.Bans.FileBan

  schema "file_bans" do
    belongs_to :ban, Qtfile.Bans.Ban, foreign_key: :ban_id
    field :hash, :string

    timestamps()
  end

  @doc false
  def changeset(%FileBan{} = file_ban, attrs) do
    file_ban
    |> cast(attrs, [:hash])
    |> fn(file_ban) ->
      if Map.has_key?(attrs, :ban) do
        put_assoc(file_ban, :ban, attrs.ban)
      else
        file_ban
      end
    end.()
    |> validate_required([:hash])
    |> unique_constraint(:ban_hash)
  end
end


code:
defmodule Qtfile.Bans.IPBan do
  use Ecto.Schema
  import Ecto.Changeset
  alias Qtfile.Bans.IPBan

  schema "ip_bans" do
    belongs_to :user_ban, Qtfile.Bans.UserBan, foreign_key: :user_ban_id
    field :ip_address, :binary

    timestamps()
  end

  @doc false
  def changeset(%IPBan{} = ip_ban, attrs) do
    ip_ban
    |> cast(attrs, [:ip_address])
    |> fn(ip_ban) ->
      if Map.has_key?(attrs, :user_ban) do
        put_assoc(ip_ban, :user_ban, attrs.user_ban)
      else
        ip_ban
      end
    end.()
    |> validate_required([:ip_address])
    |> unique_constraint(:user_ban_id_ip_address)
  end
end


code:
defmodule Qtfile.Bans.UserBan do
  use Ecto.Schema
  import Ecto.Changeset
  alias Qtfile.Bans.UserBan

  schema "user_bans" do
    belongs_to :bannee, Qtfile.Accounts.User, foreign_key: :bannee_id
    belongs_to :ban, Qtfile.Bans.Ban, foreign_key: :ban_id
    field :hell, :boolean
    has_many :ip_bans, Qtfile.Bans.IPBan, foreign_key: :user_ban_id

    timestamps()
  end

  @doc false
  def changeset(%UserBan{} = user_ban, attrs) do
    user_ban
    |> cast(attrs, [:hell])
    |> fn(user_ban) ->
      if Map.has_key?(attrs, :ban) do
        put_assoc(user_ban, :ban, attrs.ban)
      else
        user_ban
      end
    end.()
    |> put_assoc(:bannee, attrs.bannee)
    |> cast_assoc(:ip_bans)
    |> unique_constraint(:ban_id_bannee_id)
    |> validate_required([:hell])
  end
end


code:
defmodule Qtfile.Files.File do
  use Ecto.Schema
  import Ecto.Changeset
  alias Qtfile.Files.File

  schema "files" do
    field :filename, :string
    field :mime_type, :string
    field :uuid, :string
    belongs_to :location, Qtfile.Rooms.Room, foreign_key: :location_id
    field :hash, :string
    field :size, :integer
    belongs_to :uploader, Qtfile.Accounts.User, foreign_key: :uploader_id
    field :ip_address, :binary
    field :expiration_date, :utc_datetime

    timestamps()
  end

  @doc false
  def changeset(%File{} = file, attrs) do
    file
    |> cast(
      attrs, [:uuid, :filename, :mime_type, :hash, :size, :ip_address, :expiration_date]
    )
    |> validate_required([
      :uuid,
      :filename,
      :hash,
      :size,
      :ip_address,
      :expiration_date
    ])
    |> put_assoc(:uploader, attrs.uploader)
    |> put_assoc(:location, attrs.location)
    |> unique_constraint(:uuid)
  end
end


code:
defmodule Qtfile.Files do
  @moduledoc """
  The Files context.
  """

  import Ecto.Query, warn: false
  require Logger
  alias Qtfile.Repo

  alias Qtfile.Files.File

  @doc """
  Returns the list of files.

  ## Examples

      iex> list_files()
      [%File{}, ...]

  """
  def list_files do
    Repo.all(File)
  end

  @doc """
  Gets a single file.

  Raises `Ecto.NoResultsError` if the File does not exist.

  ## Examples

      iex> get_file!(123)
      %File{}

      iex> get_file!(456)
      ** (Ecto.NoResultsError)

  """
  def get_file!(id), do: Repo.get!(File, id)

  def get_files_by_room_id(room_id) do
    query = from f in File,
      join: r in assoc(f, :location),
      where: r.room_id == ^room_id,
      order_by: [asc: :expiration_date],
      join: o in assoc(r, :owner),
      join: u in assoc(f, :uploader),
      preload: [location: {r, owner: o}, uploader: u],
      select: f
    Repo.all(query)
  end

  def process_for_browser(%Qtfile.Files.File{} = file) do
    process_for_browser(Map.from_struct(file))
  end

  def process_for_browser(%{location: location, uploader: uploader} = file) do
    file
    |> Qtfile.Util.multiDelete(
      [:location_id, :uploader_id, :location, :uploader, :__meta__, :secret]
    )
    |> Map.put(:room_id, location.room_id)
    |> Map.put(:uploader, uploader.name)
    |> Map.put(:uploader_id, uploader.id)
  end

  def get_file_by_uuid(uuid) do
    query = from f in File,
      where: f.uuid == ^uuid,
      preload: [:uploader, location: [:owner]],
      select: f

    query
    |> Repo.one
  end

  @doc """
  Creates a file.

  ## Examples

      iex> create_file(%{field: value})
      {:ok, %File{}}

      iex> create_file(%{field: bad_value})
      {:error, %Ecto.Changeset{}}

  """
  def create_file(attrs \\ %{}) do
    %File{}
    |> File.changeset(attrs)
    |> Repo.insert()
  end

  def add_file(uuid, filename, room, hash, size, uploader, ip_address, expiration_date, mime_type) do
    data = %{
      uuid: uuid,
      filename: filename,
      mime_type: mime_type,
      location: room,
      hash: hash,
      size: size,
      uploader: uploader,
      ip_address: ip_address,
      expiration_date: expiration_date
    }
    result = create_file(data)
    case result do
      {:ok, _} ->
        QtfileWeb.RoomChannel.broadcast_new_files([data], room.room_id)
        :ok
      {:error, changeset} ->
        Logger.error("failed to add file to db")
        Logger.error(inspect(changeset))
        :error
    end
  end

  @doc """
  Updates a file.

  ## Examples

      iex> update_file(file, %{field: new_value})
      {:ok, %File{}}

      iex> update_file(file, %{field: bad_value})
      {:error, %Ecto.Changeset{}}

  """
  def update_file(%File{} = file, attrs) do
    file
    |> File.changeset(attrs)
    |> Repo.update()
  end

  @doc """
  Deletes a File.

  ## Examples

      iex> delete_file(file)
      {:ok, %File{}}

      iex> delete_file(file)
      {:error, %Ecto.Changeset{}}

  """
  def delete_file(%File{} = file) do
    absolute_path = get_absolute_path(file)
    Repo.delete(file)
    Elixir.File.rm(absolute_path)
  end

  @doc """
  Returns an `%Ecto.Changeset{}` for tracking file changes.

  ## Examples

      iex> change_file(file)
      %Ecto.Changeset{source: %File{}}

  """
  def change_file(%File{} = file) do
    File.changeset(file, %{})
  end

  def get_absolute_path(file) do
    path = Application.get_env(:arc, :storage_dir, "uploads/rooms")
    path <> "/" <> file.location.room_id <> "/" <> file.uuid <> "-original" <> Path.extname(file.filename)
  end
end


code:
defmodule Qtfile.IPAddressObfuscation do
  import Bitwise

  def ip_filter(room, user, %{ip_address: ip_address} = data) do
    case user.role do
      "admin" -> %{data | ip_address: human_readable(
                    denormalise_ip_address(ip_address))}
      "mod" -> %{data | ip_address: encrypt_ip_address(ip_address, user.secret)}
      "user" -> if room.owner == user.id do
        %{data | ip_address:
          encrypt_ip_address(ip_address, :crypto.exor(user.secret, room.secret))
        }
      else
          Map.delete(data, :ip_address)
      end
    end
  end

  def ban_filter(room, user, ip_address) do
    case user.role do
      "admin" -> {:ok, ip_address}
      "mod" -> decrypt_ip_address(ip_address, user.secret)
      "user" -> if room.owner == user.id do
        decrypt_ip_address(ip_address, :crypto.exor(user.secret, room.secret))
      else
        {:error, :insufficient_ip_decryption_permission}
      end
    end
  end

  def human_readable(ip_address)

  def human_readable({a, b, c, d}) do
    "#{a}.#{b}.#{c}.#{d}"
  end

  def human_readable({a, b, c, d, e, f, g, h}) do
    [a, b, c, d, e, f, g, h] = Enum.map([a, b, c, d, e, f, g, h], fn(chunk) ->
      Base.encode16(<<chunk::16>>)
    end)
    "#{a}:#{b}:#{c}:#{d}:#{e}:#{f}:#{g}:#{h}"
  end

  defp encrypt_ip_address(ip_address, iv) do
    pt = ip_address
    key = get_secret_key_base()
    {ct, mac} = :crypto.block_encrypt(:aes_gcm, key, iv, {iv, pt, 16})
    result = ct <> mac
    Base.url_encode64(result)
  end

  defp decrypt_ip_address(encrypted_ip_address, iv) do
    key = get_secret_key_base()
    <<ct::128, mac::128>> = Base.url_decode64!(encrypted_ip_address)
    case :crypto.block_decrypt(:aes_gcm, key, iv, {iv, <<ct::128>>, <<mac::128>>}) do
      :error -> {:error, :ip_decryption_failed}
      pt -> {:ok, pt}
    end
  end

  def normalise_ip_address(ip_address)

  def normalise_ip_address({a, b, c, d}) do
    <<d::8, c::8, b::8, a::8, 0::96>>
  end

  def normalise_ip_address({a, b, c, d, e, f, g, h}) do
    <<h::16, g::16, f::16, e::16, d::16, c::16, b::16, a::16>>
  end

  def denormalise_ip_address(ip_address)

  def denormalise_ip_address(<<d::8, c::8, b::8, a::8, 0::96>>) do
    {a, b, c, d}
  end

  def denormalise_ip_address(<<h::16, g::16, f::16, e::16, d::16, c::16, b::16, a::16>>) do
    {a, b, c, d, e, f, g, h}
  end

  defp get_secret_key_base() do
    <<x::256, _::128>> = Base.decode64!(Application.get_env(:qtfile, :secret_key_ip))
    <<x::256>>
  end
end


code:
defmodule Mix.Tasks.Qf.Ban do
  @shortdoc "Bans a user"
  use Mix.Task

  def run([user]) do
    Mix.Task.run("qf.set.status", [user, "banned"])
  end

  def run(_) do
    Mix.shell.error(Qtfile.Tasks.Util.arguments_error())
  end
end


code:
defmodule Mix.Tasks.Qf.Set.Role do
  @shortdoc "Sets the role of a user"
  @moduledoc """
  USAGE: mix qf.set.role USERNAME ROLE
  Check the documentation or the code to see the available roles.
  """
  use Mix.Task
  import Mix.Ecto

  def run([user, role]) do
    Qtfile.Tasks.Util.start_ecto()
    role = String.downcase(role)

    result =
      Qtfile.Accounts.get_user_by_username(user)
      |> Qtfile.Accounts.update_user(%{role: role})

    case result do
      {:ok, _} ->
        Mix.shell.info("Changed #{user}'s role to \"#{role}\"")
      {:error, changeset} ->
        Qtfile.Tasks.Util.print_errors(changeset.errors)
    end
  end

  def run(_) do
    Mix.shell.error(Qtfile.Tasks.Util.arguments_error())
  end
end


code:
defmodule Mix.Tasks.Qf.Set.Status do
  @shortdoc "Sets the status of a user."
  use Mix.Task
  import Mix.Ecto

  def run([user, status]) do
    Qtfile.Tasks.Util.start_ecto()

    result =
      Qtfile.Accounts.get_user_by_username(user)
      |> Qtfile.Accounts.update_user(%{status: status})

    case result do
      {:ok, _} ->
        Mix.shell.info("Changed #{user}'s status to \"#{status}\"")
      {:error, changeset} ->
        Qtfile.Tasks.Util.print_errors(changeset.errors)
    end
  end

  def run(_) do
    Mix.shell.error(Qtfile.Tasks.Util.arguments_error())
  end
end


code:
defmodule Mix.Tasks.Qf.Token do
  @shortdoc "Creates a registration token"
  use Mix.Task
  import Mix.Ecto

  def run([]) do
    Qtfile.Tasks.Util.start_ecto()

    token = Qtfile.SingleToken.create_token()

    response = "=== Generated Token ===
Token: " <> token

    Mix.shell.info(response)
  end

  def run(_) do
    Mix.shell.info("=== This command does not take any arguments ===")
  end
end


code:
defmodule Mix.Tasks.Qf.Unban do
  @shortdoc "Unbans a user"
  use Mix.Task

  def run([user]) do
    Mix.Task.run("qf.set.status", [user, "active"])
  end

  def run(_) do
    Mix.shell.error(Qtfile.Tasks.Util.arguments_error())
  end
end


code:
defmodule Qtfile.Tasks.Util do
  import Mix.Ecto

  def start_ecto() do
    Application.put_env(:logger, :level, :info)
    [repo] = parse_repo([])

    ensure_repo(repo, [])
    ensure_started(repo, [])
  end

  def print_errors([]) do
  end

  def print_errors([{field, {reason, _}}|t]) do
    Mix.shell.info("=== An ecto validation error occurred! ===")
    IO.puts "#{field} #{reason}"
    print_errors(t)
  end

  def arguments_error(), do: "=== Wrong amount of arguments supplied ==="
end


code:
defmodule Qtfile.Repo do
  use Ecto.Repo, otp_app: :qtfile

  @doc """
  Dynamically loads the repository url from the
  DATABASE_URL environment variable.
  """
  def init(_, opts) do
    {:ok, Keyword.put(opts, :url, System.get_env("DATABASE_URL"))}
  end
end


code:
defmodule Qtfile.Rooms.Room do
  use Ecto.Schema
  import Ecto.Changeset
  alias Qtfile.Rooms.Room

  schema "rooms" do
    field :disabled, :boolean, default: false
    # Seconds
    # 259200 is 72 hours
    field :file_ttl, :integer, default: 259200
    has_many :files, Qtfile.Files.File, foreign_key: :location_id
    field :motd, :string
    belongs_to :owner, Qtfile.Accounts.User, foreign_key: :owner_id
    field :room_id, :string
    field :room_name, :string, default: "New Room"
    field :secret, :binary

    timestamps()
  end

  def default_room_name, do: "New Room"
  def default_files, do: []
  def default_motd, do: ""
  def default_file_ttl, do: 259200
  def default_disabled, do: false

  @doc false
  def changeset(%Room{} = room, attrs) do
    room
    |> cast(attrs, [:room_id, :room_name, :motd, :disabled, :file_ttl, :secret])
    |> validate_required([:room_id, :room_name, :disabled, :file_ttl, :secret])
    |> put_assoc(:owner, attrs.owner)
    |> unique_constraint(:room_id)
    |> unique_constraint(:secret)
  end
end


code:
defmodule Qtfile.Rooms do
  @moduledoc """
  The Rooms context.
  """

  import Ecto.Query, warn: false
  alias Qtfile.Repo

  alias Qtfile.Rooms.Room

  def generate_room_id() do
    generate_room_id(6)
  end

  def generate_room_id(length) do
    :crypto.strong_rand_bytes(length) |> Base.url_encode64(padding: false) |> String.slice(0, length)
  end

  @doc """
  Returns the list of rooms.

  ## Examples

      iex> list_rooms()
      [%Room{}, ...]

  """
  def list_rooms do
    query = from r in Room,
      join: o in assoc(r, :owner),
      preload: [owner: o],
      select: r
    Repo.all(query)
  end

  def room_exists?(room_id) do
    not (get_room_by_room_id!(room_id) == nil)
  end

  def uploadable_room(room_id) do
    case get_room_by_room_id!(room_id) do
      %{disabled: false} = room -> {:ok, room}
      _ -> :error
    end
  end

  @doc """
  Gets a single room.

  Raises `Ecto.NoResultsError` if the Room does not exist.

  ## Examples

      iex> get_room!(123)
      %Room{}

      iex> get_room!(456)
      ** (Ecto.NoResultsError)

  """
  def get_room!(id), do: Repo.get!(Room, id)

  def get_room_by_room_id!(room_id) do
    query = from r in Room,
      select: r,
      where: r.room_id == ^room_id

    Repo.one(query)
  end

  @doc """
  Creates a room.

  ## Examples

      iex> create_room(%{field: value})
      {:ok, %Room{}}

      iex> create_room(%{field: bad_value})
      {:error, %Ecto.Changeset{}}

  """

  def create_room(room_id, owner) do
    alias Qtfile.Rooms.Room

    create_room(
      %{
        room_id: room_id,
        owner: owner,
        room_name: Room.default_room_name(),
        disabled: Room.default_disabled(),
        file_ttl: Room.default_file_ttl(),
        files: Room.default_files(),
        motd: Room.default_motd(),
        secret: :crypto.strong_rand_bytes(16),
      }
    )
  end

  def create_room(attrs \\ %{}) do
    %Room{}
    |> Room.changeset(attrs)
    |> Repo.insert()
  end

  @doc """
  Updates a room.

  ## Examples

      iex> update_room(room, %{field: new_value})
      {:ok, %Room{}}

      iex> update_room(room, %{field: bad_value})
      {:error, %Ecto.Changeset{}}

  """
  def update_room(%Room{} = room, attrs) do
    room
    |> Room.changeset(attrs)
    |> Repo.update()
  end

  @doc """
  Deletes a Room.

  ## Examples

      iex> delete_room(room)
      {:ok, %Room{}}

      iex> delete_room(room)
      {:error, %Ecto.Changeset{}}

  """
  def delete_room(%Room{} = room) do
    Repo.delete(room)
  end

  @doc """
  Returns an `%Ecto.Changeset{}` for tracking room changes.

  ## Examples

      iex> change_room(room)
      %Ecto.Changeset{source: %Room{}}

  """
  def change_room(%Room{} = room) do
    Room.changeset(room, %{})
  end
end


code:
defmodule Qtfile.SingleToken do
  @moduledoc """
  The SingleToken context.
  """

  import Ecto.Query, warn: false
  alias Qtfile.Repo

  alias Qtfile.SingleToken.Token

  def generate_token() do
    generate_token(48)
  end

  def generate_token(length) do
    :crypto.strong_rand_bytes(length) |> Base.url_encode64(padding: false)
  end

  def verify_token(token) do
    query = from t in Token,
      select: t.token,
      where: t.token == ^token

    result = Repo.all(query) |> Enum.count

    result > 0
  end

  @doc """
  Returns the list of tokens.

  ## Examples

      iex> list_tokens()
      [%Token{}, ...]

  """
  def list_tokens do
    Repo.all(Token)
  end

  @doc """
  Gets a single token.

  Raises `Ecto.NoResultsError` if the Token does not exist.

  ## Examples

      iex> get_token!(123)
      %Token{}

      iex> get_token!(456)
      ** (Ecto.NoResultsError)

  """
  def get_token!(id), do: Repo.get!(Token, id)

  def get_token() do
    get_token(1)
  end

  def get_token(limit) do
    # query = from token in Token,
    #   select: token.token

    # stream = Repo.stream(query, [{:max_rows, 1}])

    # Repo.transaction(fn() ->
    #   Enum.to_list(stream)
    # end)

    query = from t in Token,
      select: %{token: t.token, id: t.id},
      limit: ^limit

    Repo.all(query)
  end

  @doc """
  Creates a token.

  ## Examples

      iex> create_token(%{field: value})
      {:ok, %Token{}}

      iex> create_token(%{field: bad_value})
      {:error, %Ecto.Changeset{}}

  """
  def create_token(attrs \\ %{}) do
    changeset = %{token: generate_token()}

    %Token{}
    |> Token.changeset(changeset)
    |> Repo.insert()

    Map.get(changeset, :token)
  end

  @doc """
  Updates a token.

  ## Examples

      iex> update_token(token, %{field: new_value})
      {:ok, %Token{}}

      iex> update_token(token, %{field: bad_value})
      {:error, %Ecto.Changeset{}}

  """
  def update_token(%Token{} = token, attrs) do
    token
    |> Token.changeset(attrs)
    |> Repo.update()
  end

  @doc """
  Deletes a Token.

  ## Examples

      iex> delete_token(token)
      {:ok, %Token{}}

      iex> delete_token(token)
      {:error, %Ecto.Changeset{}}

  """
  def delete_token(%Token{} = token) do
    Repo.delete(token)
  end

  def delete_token_by_hash(token) do
    query = from t in Token,
      select: %{token: t.token, id: t.id},
      where: t.token == ^token

    case Repo.all(query) do
      [%{token: token, id: id}] ->
        delete_token(%Token{token: token, id: id})
      _ ->
        {:error, "lmao"}
    end
  end

  @doc """
  Returns an `%Ecto.Changeset{}` for tracking token changes.

  ## Examples

      iex> change_token(token)
      %Ecto.Changeset{source: %Token{}}

  """
  def change_token(%Token{} = token) do
    Token.changeset(token, %{})
  end
end


code:
defmodule Qtfile.SingleToken.Token do
  use Ecto.Schema
  import Ecto.Changeset
  alias Qtfile.SingleToken.Token

  schema "tokens" do
    field :token, :string

    timestamps()
  end

  @doc false
  def changeset(%Token{} = token, attrs) do
    token
    |> cast(attrs, [:token])
    |> validate_required([:token])
    |> unique_constraint(:token)
  end
end


code:
defmodule Qtfile.Util do
  def get_ip_address(%{remote_ip: ip}) do
    Qtfile.IPAddressObfuscation.normalise_ip_address(ip)
  end

  def mapSequence(list, mapping) do
    List.foldr(list, {:ok, []}, fn(x, exs) ->
      case {mapping.(x), exs} do
        {{:ok, x}, {:ok, xs}} -> {:ok, [x | xs]}
        _ -> :error
      end
    end)
  end

  def multiDelete(map, keys) do
    Enum.reduce(keys, map, fn (n, c) ->
      Map.delete(c, n)
    end)
  end

  def multiPut(map, kvpairs) do
    Enum.reduce(kvpairs, map, fn ({k, v}, c) ->
      Map.put(c, k, v)
    end)
  end
end


code:
defmodule QtfileWeb do
  @moduledoc """
  The entrypoint for defining your web interface, such
  as controllers, views, channels and so on.

  This can be used in your application as:

      use QtfileWeb, :controller
      use QtfileWeb, :view

  The definitions below will be executed for every view,
  controller, etc, so keep them short and clean, focused
  on imports, uses and aliases.

  Do NOT define functions inside the quoted expressions
  below. Instead, define any helper function in modules
  and import those modules here.
  """

  def controller do
    quote do
      use Phoenix.Controller, namespace: QtfileWeb
      import Plug.Conn
      import QtfileWeb.Router.Helpers
      import QtfileWeb.Gettext
    end
  end

  def view do
    quote do
      use Phoenix.View, root: "lib/qtfile_web/templates",
                        namespace: QtfileWeb

      # Import convenience functions from controllers
      import Phoenix.Controller, only: [get_flash: 2, view_module: 1]

      # Use all HTML functionality (forms, tags, etc)
      use Phoenix.HTML

      import QtfileWeb.Router.Helpers
      import QtfileWeb.ErrorHelpers
      import QtfileWeb.Gettext
    end
  end

  def router do
    quote do
      use Phoenix.Router
      import Plug.Conn
      import Phoenix.Controller
    end
  end

  def channel do
    quote do
      use Phoenix.Channel
      import QtfileWeb.Gettext
    end
  end

  @doc """
  When used, dispatch to the appropriate controller/view/etc.
  """
  defmacro __using__(which) when is_atom(which) do
    apply(__MODULE__, which, [])
  end
end


code:
defmodule QtfileWeb.Presence do
  @moduledoc """
  Provides presence tracking to channels and processes.

  See the [`Phoenix.Presence`](http://hexdocs.pm/phoenix/Phoenix.Presence.html)
  docs for more details.

  ## Usage

  Presences can be tracked in your channel after joining:

      defmodule Qtfile.MyChannel do
        use QtfileWeb, :channel
        alias Qtfile.Presence

        def join("some:topic", _params, socket) do
          send(self, :after_join)
          {:ok, assign(socket, :user_id, ...)}
        end

        def handle_info(:after_join, socket) do
          push socket, "presence_state", Presence.list(socket)
          {:ok, _} = Presence.track(socket, socket.assigns.user_id, %{
            online_at: inspect(System.system_time(:seconds))
          })
          {:noreply, socket}
        end
      end

  In the example above, `Presence.track` is used to register this
  channel's process as a presence for the socket's user ID, with
  a map of metadata. Next, the current presence list for
  the socket's topic is pushed to the client as a `"presence_state"` event.

  Finally, a diff of presence join and leave events will be sent to the
  client as they happen in real-time with the "presence_diff" event.
  See `Phoenix.Presence.list/2` for details on the presence datastructure.

  ## Fetching Presence Information

  The `fetch/2` callback is triggered when using `list/1`
  and serves as a mechanism to fetch presence information a single time,
  before broadcasting the information to all channel subscribers.
  This prevents N query problems and gives you a single place to group
  isolated data fetching to extend presence metadata.

  The function receives a topic and map of presences and must return a
  map of data matching the Presence datastructure:

      %{"123" => %{metas: [%{status: "away", phx_ref: ...}],
        "456" => %{metas: [%{status: "online", phx_ref: ...}]}

  The `:metas` key must be kept, but you can extend the map of information
  to include any additional information. For example:

      def fetch(_topic, entries) do
        users = entries |> Map.keys() |> Accounts.get_users_map(entries)
        # => %{"123" => %{name: "User 123"}, "456" => %{name: nil}}

        for {key, %{metas: metas}} <- entries, into: %{} do
          {key, %{metas: metas, user: users[key]}}
        end
      end

  The function above fetches all users from the database who
  have registered presences for the given topic. The fetched
  information is then extended with a `:user` key of the user's
  information, while maintaining the required `:metas` field from the
  original presence data.
  """
  use Phoenix.Presence, otp_app: :qtfile,
                        pubsub_server: Qtfile.PubSub
  alias Qtfile.Accounts.User
  import Ecto.Query
  alias Qtfile.Repo

  def fetch(_topic, entries) do
    query =
      from u in User,
      where: u.id in ^Map.keys(entries),
      select: {u.id, %{role: u.role, name: u.name}}

    users = query |> Repo.all |> Enum.map(fn({id, data}) ->
      {Integer.to_string(id), data}
    end) |> Enum.into(%{})

    for {key, %{metas: metas}} <- entries, into: %{} do
      {key, %{metas: metas, user: users[key]}}
    end
  end
end


code:
defmodule QtfileWeb.RoomChannel do
  require Logger
  use Phoenix.Channel
  alias QtfileWeb.Presence
  intercept ["files", "presence_diff"]

  def join("room:" <> room_id, _message, socket) do
    if Qtfile.Rooms.room_exists?(room_id) do
      send(self(), :after_join)
      {:ok, socket}
    else
      {:error, %{reason: "Room does not exist"}}
    end
  end

  def join(_topic, _message, _socket) do
    {:error, %{reason: "Unavailable"}}
  end

  # def handle_in("broadcast_notification", %{"title" => title, "body" => body}, socket) do
  #   IO.inspect title
  #   IO.inspect body
  #   broadcast!(socket, "notification", %{title: title, body: body})
  #   {:noreply, socket}
  # end

  defp get_user_and_room(socket) do
    user_id = socket.assigns[:user_id]
    "room:" <> room_id = socket.topic
    room = Qtfile.Rooms.get_room_by_room_id!(room_id)
    user = Qtfile.Accounts.get_user!(user_id)
    {user, room}
  end

  def handle_out("files", %{body: files}, socket) do
    {user, room} = get_user_and_room(socket)
    new_files = Enum.map(files, fn(file) ->
      Qtfile.IPAddressObfuscation.ip_filter(room, user, file)
    end)
    push(socket, "files", %{body: new_files})
    {:noreply, socket}
  end

  def handle_out("presence_diff", diff, socket) do
    push(socket, "presence_diff", presence_filter_ips_diff(diff, socket))
    {:noreply, socket}
  end

  defp presence_filter_ips(presence_diff, socket) do
    {user, room} = get_user_and_room(socket)
    presence_diff
    |> Enum.map(fn({user_id, %{metas: metas} = data}) ->
      new_metas = Enum.map(metas,
      &(Qtfile.IPAddressObfuscation.ip_filter(room, user, &1))
    )
      {user_id, %{data | metas: new_metas}}
    end)
    |> Enum.into(%{})
  end

  defp presence_filter_ips_diff(presence_info, socket) do
    presence_info
    |> Enum.map(fn({operation_id, values}) ->
      values
      |> presence_filter_ips(socket)
      |> (&{operation_id, &1}).()
    end)
    |> Enum.into(%{})
  end

  def handle_info(:after_join, socket) do
    {user, room} = get_user_and_room(socket)
    #:timer.apply_interval(300, __MODULE__, :increment, [socket])
    files = Enum.map(Qtfile.Files.get_files_by_room_id(room.room_id),
      &(Qtfile.Files.process_for_browser/1))
    push(socket, "presence_state", presence_filter_ips(Presence.list(socket), socket))
    {:ok, _} = Presence.track(socket, user.id,
      %{
        online_at: DateTime.utc_now(),
        ip_address: socket.assigns.ip_address,
      }
    )
    push(socket, "role", %{body: user.role})
    handle_out("files", %{body: files}, socket)
  end

  def handle_info({:deleted, room_id, file_uuid}, socket) do
    QtfileWeb.Endpoint.broadcast!("room:" <> room_id, "deleted", %{body: file_uuid})
    {:noreply, socket}
  end

  def handle_in("delete", %{"files" => files}, socket) do
    {user, room} = get_user_and_room(socket)
    if Qtfile.Accounts.has_mod_authority(user, room) do
      results = Enum.map(files, fn(uuid) ->
        file = Qtfile.Files.get_file_by_uuid(uuid)
        if file != nil and file.location_id == room.id do
          Qtfile.Files.delete_file(file)
          broadcast_deleted_file(file)
          :ok
        else
          :error
        end
      end)
      {:reply, {:ok, %{results: results}}, socket}
    else
      {:reply, :error, socket}
    end
  end

  def handle_in("ban", ban, socket) do
    {user, room} = get_user_and_room(socket)
    ban_e = Qtfile.Bans.preprocess_input_for_database(user, room, ban)
    result =
      case ban_e do
        {:ok, ban} -> Qtfile.Bans.create_ban(ban)
        {:error, _} = e -> e
      end
    case result do
      {:ok, _} -> {:reply, {:ok, %{success: true}}, socket}
      {:error, e} ->
        error =
          case e do
            :insufficient_ban_permission ->
              "you do not have permission to ban with these parameters"
            :insufficient_ip_decryption_permission ->
              "you do not have permission to decrypt one of the ip addresses specified"
            :ip_decryption_failed ->
              "failed to decrypt one of the specified ip addresses"
            _ ->
              Logger.info("error creating ban: ")
              Logger.info(e)
              "unknown error, please report to an admin"
          end
        {:reply, {:ok, %{success: false, error: e}}, socket}
    end
  end

  def broadcast_new_files(files, room_id) do
    files = Enum.map(files, &(Qtfile.Files.process_for_browser/1))
    QtfileWeb.Endpoint.broadcast!("room:" <> room_id, "files", %{body: files})
  end

  def broadcast_deleted_file(file) do
    send(self(), {:deleted, file.location.room_id, file.uuid})
  end

  def increment(socket) do
    # number = Channelstest.Incrementer.increment()
    # IO.inspect number
    number = 1

    push(socket, "update", %{body: number})
  end
end


code:
defmodule QtfileWeb.UserSocket do
  use Phoenix.Socket

  ## Channels
  channel "room:*", QtfileWeb.RoomChannel

  ## Transports
  transport :websocket, Phoenix.Transports.WebSocket
  # transport :longpoll, Phoenix.Transports.LongPoll

  # Socket params are passed from the client and can
  # be used to verify and authenticate a user. After
  # verification, you can put default assigns into
  # the socket that will be set for all channels, ie
  #
  #     {:ok, assign(socket, :user_id, verified_user_id)}
  #
  # To deny connection, return `:error`.
  #
  # See `Phoenix.Token` documentation for examples in
  # performing token verification on connect.
  def connect(%{"token" => token}, socket) do
    case Phoenix.Token.verify(socket, Application.get_env(:qtfile, :token_secret_key_base), token, max_age: 31557600) do
      {:ok, {user_id, ip_address}} ->
        socket
        |> assign(:user_id, user_id)
        |> assign(:ip_address, ip_address)
        |> (&{:ok, &1}).()
      {:error, _} ->
        :error
    end
  end

  def connect(_params, _socket) do
    :error
  end

  # Socket id's are topics that allow you to identify all sockets for a given user:
  #
  #     def id(socket), do: "user_socket:#{socket.assigns.user_id}"
  #
  # Would allow you to broadcast a "disconnect" event and terminate
  # all active sockets and channels for a given user:
  #
  #     QtfileWeb.Endpoint.broadcast("user_socket:#{user.id}", "disconnect", %{})
  #
  # Returning `nil` makes this socket anonymous.
  def id(_socket), do: nil
end


code:
defmodule QtfileWeb.FileController do
  use QtfileWeb, :controller
  @image_extensions ~w(.jpg .jpeg .png)
  @nice_mime_types ~w(text audio video image)

  def upload(conn, %{"room_id" => room_id} = params) do
    uploader_id = get_session(conn, :user_id)
    ip_address = Qtfile.Util.get_ip_address(conn)

    if validate_upload(room_id, uploader_id, ip_address) do
      upload_validated(conn, params)
    else
      json conn, %{success: false, error: "room does not exist", preventRetry: true}
    end
  end

  def upload(conn, _) do
    json conn, %{success: false, error: "failed to provide room id in request", preventRetry: true}
  end

  defp validate_upload(room_id, user_id, ip_address) do
    case Qtfile.Rooms.uploadable_room(room_id) do
      {:ok, room} ->
        case Qtfile.Bans.get_bans_for([Qtfile.Accounts.get_user!(user_id), ip_address], room) do
          [] -> true
          _ -> false
        end
      :error -> false
    end
  end

  defp save_file(conn, size) do
    hash = :crypto.hash_init(:sha)
    path = "uploads-temp/" <> Ecto.UUID.generate()
    {:ok, file} = :file.open(path, [:write, :binary])
    {:ok, _} = :file.position(file, size)
    :ok = :file.truncate(file)
    {:ok, _} = :file.position(file, 0)
    {conn, result} = save_file_loop(conn, file, size, hash)
    result =
      case result do
        {:ok, hash} -> {:ok, path, hash}
        x -> x
      end
    {conn, result}
  end

  defp save_file_loop(conn, _file, 0, _hash) do
    {conn, {:error, :file_too_big}}
  end

  defp save_file_loop(conn, file, size, hash) do
    result = read_body(conn,
      length: Application.get_env(:qtfile, QtfileWeb.Endpoint, :max_file_length),
      read_length: 64 * 1024,
      read_timeout: 1024
    )
    case result do
      {:error, e} -> {conn, {:error, e}}
      {status, data, conn} ->
        len = :erlang.byte_size(data)
        :ok = :file.write(file, data)
        hash = :crypto.hash_update(hash, data)
        case status do
          :ok ->
            :file.sync(file)
            :file.close(file)
            {conn, {:ok, Base.encode16(:crypto.hash_final(hash), case: :lower)}}
          :more -> save_file_loop(conn, file, size - len, hash)
        end
    end
  end

  defp upload_validated(conn,
    %{
      "room_id" => room_id,
      "filename" => filename,
      "content_type" => content_type
    }) do
    size = :erlang.binary_to_integer(hd(get_req_header(conn, "content-length")))
    {conn, save_result} = save_file(conn, size)
    response =
      case save_result do
        {:ok, path, hash} ->
          file = %Plug.Upload{path: path, content_type: content_type, filename: filename}
          room = Qtfile.Rooms.get_room_by_room_id!(room_id)
          uuid = Ecto.UUID.generate()
          scope = %{room_id: room_id, uuid: uuid}

          cond do
            check_extension(filename, @image_extensions) == true ->
              Qtfile.ImageFile.store({file, scope})
              |> store_in_db(conn, uuid, room, content_type, hash,
                Qtfile.ImageFile.storage_dir(:original, {file, room}))

            true ->
              Qtfile.GenericFile.store({file, scope})
              |> store_in_db(conn, uuid, room, content_type, hash,
                Qtfile.GenericFile.storage_dir(:original, {file, room}))
          end
        {:error, e} -> %{success: false, error: e}
      end

    # conn
    # |> redirect(to: room_path(conn, :index, room_id))
    # json conn, %{success: true}
    json conn, response
  end

  defp upload_validated(conn,
    %{
      "room_id" => room_id,
      "filename" => filename,
    }) do
    upload_validated(conn, %{
      "room_id" => room_id,
      "filename" => filename,
      "content_type" => nil
    })
  end

  def download(conn, %{"uuid" => uuid, "realfilename" => _realfilename}) do
    file = Qtfile.Files.get_file_by_uuid(uuid)
    path = Application.get_env(:arc, :storage_dir, "uploads/rooms")

    if file != nil do
      absolute_path = Qtfile.Files.get_absolute_path(file)

      mime_type = file.mime_type

      nice_file =
        case mime_type do
          nil -> false
          _ ->
            case String.split(mime_type, "/") do
              [type, _] -> Enum.member?(@nice_mime_types, type)
              _ -> false
            end
        end

      if nice_file do
        conn
        |> put_resp_content_type(mime_type)
        |> send_file(200, absolute_path)
      else
        download_params = [filename: file.filename] ++ if file.mime_type == nil do
          []
        else
          [content_type: file.mime_type]
        end
        conn
        |> send_download({:file, absolute_path}, download_params)
      end

    else
      conn
      |> put_status(404)
      |> text("file not found!")
    end
  end

  def download_no_filename(conn, %{"uuid" => uuid}) do
    download(conn, %{"uuid" => uuid, "realfilename" => ""})
  end

  def check_extension(filename, extensions) do
    file_extension = filename |> Path.extname |> String.downcase
    Enum.member?(extensions, file_extension)
  end

  defp store_in_db({:ok, filename}, conn, uuid, room, mime_type, hash, path) do
    file_path = Path.absname(path <> "/" <> uuid <> "-original" <> Path.extname(filename))
    file_size =
      case File.stat(file_path) do
        {:ok, %{size: size}} ->
          size
        _ ->
          0
      end
    uploader_id = get_session(conn, :user_id)
    uploader = Qtfile.Accounts.get_user!(uploader_id)
    ip_address = Qtfile.Util.get_ip_address(conn)
    %{file_ttl: file_ttl} = room
    expiration_date = DateTime.from_unix!(DateTime.to_unix(DateTime.utc_now()) + file_ttl)

    Qtfile.Files.add_file(uuid, filename, room, hash, file_size, uploader, ip_address, expiration_date, mime_type)

    %{success: true}
  end

  defp store_in_db(_ok_filename_tuple, _conn, _uuid, _room, _upload_date, _mime_type, _path) do
    %{success: false, error: "failed to upload file", preventRetry: true}
  end
end


code:
defmodule QtfileWeb.LoginController do
  use QtfileWeb, :controller

  def index(conn, _params) do
    render(conn, "index.html")
  end
end


code:
defmodule QtfileWeb.PageController do
  use QtfileWeb, :controller

  def index(conn, _params) do
    render conn, "index.html"
  end
end


code:
defmodule QtfileWeb.RoomController do
  use QtfileWeb, :controller
  require Logger

  def index(conn, %{"room_id" => room_id}) do
    if Qtfile.Rooms.room_exists?(room_id) do
      conn
      |> assign(:room_id, room_id)
      |> render "room.html"
    else
      text conn, "FAILED TO FIND ROM FUCK OF"
    end
  end

  def create_room(conn, _params) do
    user = Qtfile.Accounts.get_user!(get_session(conn, :user_id))
    room_id = Qtfile.Rooms.generate_room_id()

    case Qtfile.Rooms.create_room(room_id, user) do
      {:ok, _} -> redirect(conn, to: room_path(conn, :index, room_id))
      {:error, e} ->
        Logger.error("failed to create room:")
        Logger.error(inspect(e))
        redirect(conn, to: "/")

    end

  end

  def not_found(conn, params) do
    IO.inspect params
    text conn, "fuck off"
  end
end


code:
defmodule QtfileWeb.RoomsController do
  use QtfileWeb, :controller

  def index(conn, _params) do
    render(conn, "index.html")
  end
end


code:
defmodule QtfileWeb.UserController do
  use QtfileWeb, :controller
  alias Qtfile.Accounts
  alias Qtfile.Accounts.User

  def login(conn, %{"user" => %{"username" => username, "password" => password}}) do
    case Accounts.authenticate_by_username_password(username, password) do
      {:ok, user} ->
        conn
        |> put_session(:user_id, user.id)
        |> configure_session(renew: true)
        |> redirect(to: "/")
      {:error, :unauthorized} ->
        conn
        |> put_flash(:error, "Wrong username or password")
        |> redirect(to: user_path(conn, :login_page))
        |> halt()
    end
  end

  def register(conn, %{"user" => user_params}) do
    case Accounts.create_user(user_params) do
      {:error, :invalid_token} ->
        conn
        |> put_flash(:error, "Invalid registration token")
        |> redirect(to: user_path(conn, :register_page))
        |> halt()
      {:error, %Ecto.Changeset{} = changeset} ->
        conn
        |> put_flash(:error, "failed to register an account")
        |> redirect(to: "/")
        |> halt()
      {:ok, user} ->
        Qtfile.SingleToken.delete_token_by_hash(Map.get(user_params, "token", ""))

        conn
        |> put_session(:user_id, user.id)
        |> configure_session(renew: true)
        |> redirect(to: "/")
    end
  end

  def logout(conn, _params) do
    conn
    |> configure_session(drop: true)
    |> redirect(to: "/")
    |> halt()
  end

  def login_page(conn, _params) do
    render(conn, "login.html")
  end

  def register_page(conn, _params) do
    render(conn, "register.html")
  end
end


code:
defmodule QtfileWeb.Endpoint do
  use Phoenix.Endpoint, otp_app: :qtfile

  socket "/socket", QtfileWeb.UserSocket

  # Serve at "/" the static files from "priv/static" directory.
  #
  # You should set gzip to true if you are running phoenix.digest
  # when deploying your static files in production.
  plug Plug.Static,
    at: "/", from: :qtfile, gzip: false,
    only: ~w(css fonts images js favicon.ico robots.txt)

  # Code reloading can be explicitly enabled under the
  # :code_reloader configuration of your endpoint.
  if code_reloading? do
    socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
    plug Phoenix.LiveReloader
    plug Phoenix.CodeReloader
  end

  plug Plug.RequestId
  plug Plug.Logger
  plug Plug.MethodOverride
  plug Plug.Head

  # The session will be stored in the cookie and signed,
  # this means its contents can be read but not tampered with.
  # Set :encryption_salt if you would also like to encrypt it.
  plug Plug.Session,
    store: :cookie,
    key: "_qtfile_key",
    signing_salt: "lCKOtTOh",
    max_age: 31557600,
    encryption_salt: (Application.get_env(:qtfile, QtfileWeb.Endpoint) |> Keyword.get(:secret_key_base))

  plug QtfileWeb.Router

  @doc """
  Callback invoked for dynamically configuring the endpoint.

  It receives the endpoint configuration and checks if
  configuration should be loaded from the system environment.
  """
  def init(_key, config) do
    if config[:load_from_system_env] do
      port = System.get_env("PORT") || raise "expected the PORT environment variable to be set"
      {:ok, Keyword.put(config, :http, [:inet6, port: port])}
    else
      {:ok, config}
    end
  end
end


code:
defmodule QtfileWeb.Gettext do
  @moduledoc """
  A module providing Internationalization with a gettext-based API.

  By using [Gettext](https://hexdocs.pm/gettext),
  your module gains a set of macros for translations, for example:

      import QtfileWeb.Gettext

      # Simple translation
      gettext "Here is the string to translate"

      # Plural translation
      ngettext "Here is the string to translate",
               "Here are the strings to translate",
               3

      # Domain-based translation
      dgettext "errors", "Here is the error message to translate"

  See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage.
  """
  use Gettext, otp_app: :qtfile
end


code:
defmodule QtfileWeb.Router do
  use QtfileWeb, :router

  pipeline :browser do
    plug Plug.Parsers,
      parsers: [:urlencoded, :multipart, :json],
      pass: ["*/*"],
      json_decoder: Poison
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end

  pipeline :api do
    plug Plug.Parsers,
      parsers: [:urlencoded, :multipart, :json],
      pass: ["*/*"],
      json_decoder: Poison
    plug :accepts, ["json"]
    plug :fetch_session
    plug :fetch_flash
  end

  pipeline :upload do
    plug Plug.Parsers,
      parsers: [:urlencoded],
      pass: ["*/*"]
    plug :fetch_session
    plug :fetch_flash
  end

  scope "/", QtfileWeb do
    pipe_through :browser # Use the default browser stack

    get "/", PageController, :index
    get "/login", UserController, :login_page
    get "/register", UserController, :register_page

    scope "/" do
      pipe_through :logged_in?

      get "/new", RoomController, :create_room
      get "/rooms", RoomsController, :index
      get "/get/:uuid/:realfilename", FileController, :download
      get "/get/:uuid/", FileController, :download_no_filename
    end
  end

  scope "/r", QtfileWeb do
    pipe_through [:browser, :logged_in?]

    get "/", RoomController, :not_found
    get "/:room_id", RoomController, :index
  end

  scope "/api", QtfileWeb do
    scope "/" do
      pipe_through :upload
      pipe_through :logged_in?

      post "/upload", FileController, :upload
    end

    pipe_through :api

    post "/login", UserController, :login
    post "/register", UserController, :register
    get "/logout", UserController, :logout

    scope "/mod" do
      pipe_through [:logged_in?, :is_mod?]
    end
  end

  # Other scopes may use custom stacks.
  # scope "/api", QtfileWeb do
  #   pipe_through :api
  # end

  defp logged_in?(conn, _) do
    case get_session(conn, :user_id) do
      nil ->
        conn
        |> send_resp(:forbidden, "Not logged in")
        |> halt()
      user_id ->
        conn
    end
  end

  defp is_mod?(conn, _) do
    user = Qtfile.Accounts.get_user!(get_session(conn, :user_id))
    unless user.role == "mod" or user.role == "admin" do
      conn
      |> send_resp(:forbidden, "Insufficient privileges")
      |> halt()
    else
      conn
    end
  end
end


code:
<% user_id = Plug.Conn.get_session(@conn, :user_id) %>
<% logged_in? = user_id != nil %>
<% ip_address = Qtfile.Util.get_ip_address(@conn) %>

<!DOCTYPE html>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="">
    <meta name="author" content="">
    <%= if logged_in? do %>
        <%= tag :meta, name: "channel_token", content: Phoenix.Token.sign(@conn, Application.get_env(:qtfile, :token_secret_key_base), {user_id, ip_address}, max_age: 31557600), id: "channel_token" %>
    <% end %>

    <title><%= title() %></title>
    <link rel="stylesheet" href="<%= static_path(@conn, "/css/app.css") %>">
    <link rel="shortcut icon" type="image/png" href="/images/favicon.png" />

      <%= render QtfileWeb.SharedView, "header.html", Map.put(assigns, :header_elements,
          [
              %{text: "rooms", url: "/rooms", condition: fn -> logged_in? end},
              %{text: "new room", url: "/new", condition: fn -> logged_in? end},
              %{text: "profile", url: "/profile", condition: fn -> logged_in? end},
              %{text: "logout", url: "/api/logout", condition: fn -> logged_in? end},

              %{text: "login", url: "/login", condition: fn -> not logged_in? end},
              %{text: "register", url: "/register", condition: fn -> not logged_in? end},
          ]) %>

        <%= render @view_module, @view_template, assigns %>

<%= get_flash(@conn, :info) %>

<%= get_flash(@conn, :error) %>


code:
Loading

      <template>

            Upload

          <input multiple type="file" id="upload-button" class="button"/>

      </template>

      <template v-if="mod">

Delete

      </template>

      <template>

          <input v-model="filter" placeholder="Filter">

      </template>

      <template v-if="mod">

      </template>

<template id="file-list-template">

    <template v-for="(upload, index) in uploads">

    </template>
    <template v-if="filter == ''">
      <template v-for="(file, index) in files">

      </template>
    </template>
    <template v-else>
      <template v-for="(file, index) in filteredFiles">

      </template>
    </template>

</template>

<template id="upload-template">

    {{ file.name }} - {{ progress }}%

</template>

<template id="file-template">

      {{ filename }}

        {{ uploader }}
        <template v-if="mod">{{ ip_address }}</template>

      {{ formattedExpirationDate }}
      <template v-if="mod">

          <input class="mark" type="checkbox" v-model="marked">

      </template>

</template>

<template id="async-button-template">

      {{ String.fromCodePoint(defaultIcon) }}

⌛

❌

✓

</template>

<template id="presence-template">

Presences

      <template v-for="(user, id) in presences">

          {{ user.user.name }}/{{ user.user.role }}:

            <template v-for="meta in user.metas">

                {{ meta.ip_address }}
                {{ meta.online_at }}

            </template>

      </template>

</template>


code:
    id
    Room
    Owner

    <%= print_all_rooms() %>


code:
     <a href="<%= room_path(QtfileWeb.Endpoint, :index, @room_id) %>" class="room-id"><%= @room_id %>
     <%= @room_name %>
     <%= @room_owner %>


code:
    qtfile
    <%= for %{text: text, url: url, condition: condition} <- @header_elements do %>
      <%= if condition.() do %>
        | <a class="nav-text" href="<%= url %>"><%= text %>
      <% end %>
    <%= end %>

  <%= if Mix.env == :dev do %>

THIS IS A DEV VERSION OF THE SITE. PLEASE DO NOT USE. PASSWORDS ARE SAVED IN PLAINTEXT IN DEV ENVIRONMENT

  <%= end %>


code:
<%= form_for @conn, user_path(@conn, :login), [method: :post, as: :user], fn(f) -> %>

            <%= text_input f, :username, placeholder: "Username" %>

            <%= password_input f, :password, placeholder: "Password" %>

    <%= submit "Login" %>

<% end %>


code:
<%= form_for @conn, user_path(@conn, :register), [method: :post, as: :user], fn(f) -> %>

register now

        <%= text_input f, :name, id: "user_name", placeholder: "Name" %> this is your display name

        <%= text_input f, :username, id: "user_username", placeholder: "Username" %> this is what you use to login. other people do not see this

        <%= password_input f, :password, id: "user_password", placeholder: "Password" %> use a secure password or get bent

        <%= password_input f, :token, id: "user_token", placeholder: "Token" %> get a registration token from the site administrator

    <%= submit "Register" %>

<% end %>


code:
defmodule Qtfile.GenericFile do
  use Arc.Definition
  use Arc.Ecto.Definition
  import Ecto
  def __storage, do: Arc.Storage.Local

  # Include ecto support (requires package arc_ecto installed):

  @versions [:original]

  # To add a thumbnail version:
  # @versions [:original, :thumb]

  # Whitelist file extensions:
  # def validate({file, _}) do
  #   ~w(.jpg .jpeg .gif .png) |> Enum.member?(Path.extname(file.file_name))
  # end

  # Define a thumbnail transformation:
  # def transform(:thumb, _) do
  #   {:convert, "-strip -thumbnail 250x250^ -gravity center -extent 250x250 -format png", :png}
  # end

  # Override the persisted filenames:
  def filename(version, {file, scope}) do
    # version
    "#{scope.uuid}-#{version}"
  end

  # Override the storage directory:
  # def storage_dir(version, {file, scope}) do
  #   "uploads/user/avatars/#{scope.id}"
  # end

  def storage_dir(version, {file, scope}) do
    "uploads/rooms/#{scope.room_id}"
  end

  # Provide a default URL if there hasn't been a file uploaded
  # def default_url(version, scope) do
  #   "/images/avatars/default_#{version}.png"
  # end

  # Specify custom headers for s3 objects
  # Available options are [:cache_control, :content_disposition,
  #    :content_encoding, :content_length, :content_type,
  #    :expect, :expires, :storage_class, :website_redirect_location]
  #
  # def s3_object_headers(version, {file, scope}) do
  #   [content_type: Plug.MIME.path(file.file_name)]
  # end
end


code:
defmodule Qtfile.ImageFile do
  use Arc.Definition
  use Arc.Ecto.Definition
  import Ecto
  def __storage, do: Arc.Storage.Local

  # Include ecto support (requires package arc_ecto installed):

  # @versions [:original]

  # To add a thumbnail version:
  @versions [:original, :thumb]

  # Whitelist file extensions:
  # def validate({file, _}) do
  #   ~w(.jpg .jpeg .gif .png) |> Enum.member?(Path.extname(file.file_name))
  # end

  # Define a thumbnail transformation:
  def transform(:thumb, _) do
    # windows is garbage jesus christ just purge it please
    {os, _} = :os.type()

    if os === :unix do
      {:convert, "-strip -limit area 10MB -limit disk 100MB -thumbnail 250x250^ -gravity center -extent 250x250 -format png", :png}
    else
      :noaction
    end
  end

  # Override the persisted filenames:
  def filename(version, {file, scope}) do
    # version
    "#{scope.uuid}-#{version}"
  end

  # Override the storage directory:
  def storage_dir(version, {file, scope}) do
    "uploads/rooms/#{scope.room_id}"
  end

  # Provide a default URL if there hasn't been a file uploaded
  # def default_url(version, scope) do
  #   "/images/avatars/default_#{version}.png"
  # end

  # Specify custom headers for s3 objects
  # Available options are [:cache_control, :content_disposition,
  #    :content_encoding, :content_length, :content_type,
  #    :expect, :expires, :storage_class, :website_redirect_location]
  #
  # def s3_object_headers(version, {file, scope}) do
  #   [content_type: Plug.MIME.path(file.file_name)]
  # end
end


code:
defmodule QtfileWeb.ErrorHelpers do
  @moduledoc """
  Conveniences for translating and building error messages.
  """

  use Phoenix.HTML

  @doc """
  Generates tag for inlined form input errors.
  """
  def error_tag(form, field) do
    Enum.map(Keyword.get_values(form.errors, field), fn (error) ->
      content_tag :span, translate_error(error), class: "help-block"
    end)
  end

  @doc """
  Translates an error message using gettext.
  """
  def translate_error({msg, opts}) do
    # Because error messages were defined within Ecto, we must
    # call the Gettext module passing our Gettext backend. We
    # also use the "errors" domain as translations are placed
    # in the errors.po file.
    # Ecto will pass the :count keyword if the error message is
    # meant to be pluralized.
    # On your own code and templates, depending on whether you
    # need the message to be pluralized or not, this could be
    # written simply as:
    #
    #     dngettext "errors", "1 file", "%{count} files", count
    #     dgettext "errors", "is invalid"
    #
    if count = opts[:count] do
      Gettext.dngettext(QtfileWeb.Gettext, "errors", msg, msg, count, opts)
    else
      Gettext.dgettext(QtfileWeb.Gettext, "errors", msg, opts)
    end
  end
end


code:
defmodule QtfileWeb.ErrorView do
  use QtfileWeb, :view

  def render("404.html", _assigns) do
    "Page not found"
  end

  def render("500.html", _assigns) do
    "Internal server error"
  end

  # In case no render clause matches or no
  # template is found, let's render it as 500
  def template_not_found(_template, assigns) do
    render "500.html", assigns
  end
end


code:
defmodule QtfileWeb.FileView do
  use QtfileWeb, :view
end


code:
defmodule QtfileWeb.LayoutView do
  use QtfileWeb, :view

  def title do
    "qtfile - Realtime temporary filesharing!"
  end
end


code:
defmodule QtfileWeb.PageView do
  use QtfileWeb, :view
end


code:
defmodule QtfileWeb.RoomView do
  use QtfileWeb, :view
end


code:
defmodule QtfileWeb.RoomsView do
  use QtfileWeb, :view

  def print_all_rooms do
    rooms = Qtfile.Rooms.list_rooms()

    for %{room_id: room_id, room_name: room_name, owner: owner} <- rooms do
      render(__MODULE__, "room_line.html", room_id: room_id, room_name: room_name, room_owner: owner.name)
    end
  end
end


code:
defmodule QtfileWeb.SharedView do
  use QtfileWeb, :view
end


code:
defmodule QtfileWeb.UserView do
  use QtfileWeb, :view
end


code:
defmodule Qtfile.Mixfile do
  use Mix.Project

  def project do
    [
      app: :qtfile,
      version: "0.0.1",
      elixir: "~> 1.4",
      elixirc_paths: elixirc_paths(Mix.env),
      compilers: [:phoenix, :gettext] ++ Mix.compilers,
      start_permanent: Mix.env == :prod,
      aliases: aliases(),
      deps: deps()
    ]
  end

  # Configuration for the OTP application.
  #
  # Type `mix help compile.app` for more information.
  def application do
    [
      mod: {Qtfile.Application, []},
      extra_applications: [:logger, :runtime_tools, :arc_ecto]
    ]
  end

  # Specifies which paths to compile per environment.
  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_),     do: ["lib"]

  # Specifies your project dependencies.
  #
  # Type `mix help deps` for examples and options.
  defp deps do
    [
      {:phoenix, "~> 1.3.0"},
      {:phoenix_pubsub, "~> 1.0"},
      {:phoenix_ecto, "~> 3.2"},
      {:postgrex, ">= 0.0.0"},
      {:phoenix_html, "~> 2.10"},
      {:phoenix_live_reload, "~> 1.0", only: :dev},
      {:gettext, "~> 0.11"},
      {:cowboy, "~> 1.0"},
      {:arc, "~> 0.8.0"},
      {:arc_ecto, "~> 0.7.0"},
      {:uuid, "~> 1.1"},
      {:comeonin, "~> 4.0", only: :prod},
      {:bcrypt_elixir, "~> 1.0", only: :prod},
      {:phoenix_cowboy_logging, "~> 1.0", only: :dev},
    ]
  end

  # Aliases are shortcuts or tasks specific to the current project.
  # For example, to create, migrate and run the seeds file at once:
  #
  #     $ mix ecto.setup
  #
  # See the documentation for `Mix` for more info on aliases.
  defp aliases do
    [
      "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
      "ecto.reset": ["ecto.drop", "ecto.setup"],
      "test": ["ecto.create --quiet", "ecto.migrate", "test"]
    ]
  end
end


code:
%{"arc": {:hex, :arc, "0.8.0", "bb7cf8ea50f30f9c2bb357270c074def42a7ec6a3b4605be731cc5faf8fde6fd", [:mix], [{:ex_aws, "~> 1.1", [hex: :ex_aws, optional: true]}, {:httpoison, "~> 0.11", [hex: :httpoison, optional: false]}, {:poison, "~> 2.2 or ~> 3.1", [hex: :poison, optional: true]}, {:sweet_xml, "~> 0.6", [hex: :sweet_xml, optional: true]}]},
  "arc_ecto": {:hex, :arc_ecto, "0.7.0", "428ed7ea4ed0be8996d8fc6e9f651d3e26fe9c9063f26cbf6731c865369845d3", [:mix], [{:arc, "~> 0.8.0", [hex: :arc, optional: false]}, {:ecto, "~> 2.0", [hex: :ecto, optional: false]}]},
  "bcrypt_elixir": {:hex, :bcrypt_elixir, "1.0.5", "cca70b5c8d9a98a0151c2d2796c728719c9c4b3f8bd2de015758ef577ee5141e", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, optional: false]}]},
  "certifi": {:hex, :certifi, "2.0.0", "a0c0e475107135f76b8c1d5bc7efb33cd3815cb3cf3dea7aefdd174dabead064", [:rebar3], []},
  "comeonin": {:hex, :comeonin, "4.0.3", "4e257dcb748ed1ca2651b7ba24fdbd1bd24efd12482accf8079141e3fda23a10", [:mix], [{:argon2_elixir, "~> 1.2", [hex: :argon2_elixir, optional: true]}, {:bcrypt_elixir, "~> 0.12.1 or ~> 1.0", [hex: :bcrypt_elixir, optional: true]}, {:pbkdf2_elixir, "~> 0.12", [hex: :pbkdf2_elixir, optional: true]}]},
  "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], []},
  "cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, optional: false]}]},
  "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []},
  "db_connection": {:hex, :db_connection, "1.1.2", "2865c2a4bae0714e2213a0ce60a1b12d76a6efba0c51fbda59c9ab8d1accc7a8", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, optional: true]}]},
  "decimal": {:hex, :decimal, "1.4.1", "ad9e501edf7322f122f7fc151cce7c2a0c9ada96f2b0155b8a09a795c2029770", [:mix], []},
  "ecto": {:hex, :ecto, "2.2.6", "3fd1067661d6d64851a0d4db9acd9e884c00d2d1aa41cc09da687226cf894661", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, optional: true]}]},
  "elixir_make": {:hex, :elixir_make, "0.4.0", "992f38fabe705bb45821a728f20914c554b276838433349d4f2341f7a687cddf", [:mix], []},
  "file_system": {:hex, :file_system, "0.2.2", "7f1e9de4746f4eb8a4ca8f2fbab582d84a4e40fa394cce7bfcb068b988625b06", [:mix], []},
  "gettext": {:hex, :gettext, "0.13.1", "5e0daf4e7636d771c4c71ad5f3f53ba09a9ae5c250e1ab9c42ba9edccc476263", [:mix], []},
  "hackney": {:hex, :hackney, "1.10.1", "c38d0ca52ea80254936a32c45bb7eb414e7a96a521b4ce76d00a69753b157f21", [:rebar3], [{:certifi, "2.0.0", [hex: :certifi, optional: false]}, {:idna, "5.1.0", [hex: :idna, optional: false]}, {:metrics, "1.0.1", [hex: :metrics, optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, optional: false]}]},
  "httpoison": {:hex, :httpoison, "0.13.0", "bfaf44d9f133a6599886720f3937a7699466d23bb0cd7a88b6ba011f53c6f562", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, optional: false]}]},
  "idna": {:hex, :idna, "5.1.0", "d72b4effeb324ad5da3cab1767cb16b17939004e789d8c0ad5b70f3cea20c89a", [:rebar3], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, optional: false]}]},
  "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []},
  "mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [:mix], []},
  "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []},
  "phoenix": {:hex, :phoenix, "1.3.0", "1c01124caa1b4a7af46f2050ff11b267baa3edb441b45dbf243e979cd4c5891b", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, optional: false]}]},
  "phoenix_cowboy_logging": {:hex, :phoenix_cowboy_logging, "1.0.0", "c23182e52c326bdc09f7ba4fc4597a8f048317b77e16f9a6eb1ded0c93508ba5", [], [{:cowboy, "~> 1.0", [hex: :cowboy, repo: "hexpm", optional: false]}], "hexpm"},
  "phoenix_ecto": {:hex, :phoenix_ecto, "3.3.0", "702f6e164512853d29f9d20763493f2b3bcfcb44f118af2bc37bb95d0801b480", [:mix], [{:ecto, "~> 2.1", [hex: :ecto, optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, optional: true]}, {:plug, "~> 1.0", [hex: :plug, optional: false]}]},
  "phoenix_html": {:hex, :phoenix_html, "2.10.5", "4f9df6b0fb7422a9440a73182a566cb9cbe0e3ffe8884ef9337ccf284fc1ef0a", [:mix], [{:plug, "~> 1.0", [hex: :plug, optional: false]}]},
  "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.1.3", "1d178429fc8950b12457d09c6afec247bfe1fcb6f36209e18fbb0221bdfe4d41", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, optional: false]}, {:phoenix, "~> 1.0 or ~> 1.2 or ~> 1.3", [hex: :phoenix, optional: false]}]},
  "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.0.2", "bfa7fd52788b5eaa09cb51ff9fcad1d9edfeb68251add458523f839392f034c1", [:mix], []},
  "plug": {:hex, :plug, "1.4.3", "236d77ce7bf3e3a2668dc0d32a9b6f1f9b1f05361019946aae49874904be4aed", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, optional: true]}, {:mime, "~> 1.0", [hex: :mime, optional: false]}]},
  "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], []},
  "poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [:rebar], []},
  "postgrex": {:hex, :postgrex, "0.13.3", "c277cfb2a9c5034d445a722494c13359e361d344ef6f25d604c2353185682bfc", [:mix], [{:connection, "~> 1.0", [hex: :connection, optional: false]}, {:db_connection, "~> 1.1", [hex: :db_connection, optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, optional: false]}]},
  "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], []},
  "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], []},
  "unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [:rebar3], []},
  "uuid": {:hex, :uuid, "1.1.8", "e22fc04499de0de3ed1116b770c7737779f226ceefa0badb3592e64d5cfb4eb9", [:mix], []}}


code:
## `msgid`s in this file come from POT (.pot) files.
##
## Do not add, change, or remove `msgid`s manually here as
## they're tied to the ones in the corresponding POT file
## (with the same domain).
##
## Use `mix gettext.extract --merge` or `mix gettext.merge`
## to merge POT files into PO files.
msgid ""
msgstr ""
"Language: en\n"

## From Ecto.Changeset.cast/4
msgid "can't be blank"
msgstr ""

## From Ecto.Changeset.unique_constraint/3
msgid "has already been taken"
msgstr ""

## From Ecto.Changeset.put_change/3
msgid "is invalid"
msgstr ""

## From Ecto.Changeset.validate_acceptance/3
msgid "must be accepted"
msgstr ""

## From Ecto.Changeset.validate_format/3
msgid "has invalid format"
msgstr ""

## From Ecto.Changeset.validate_subset/3
msgid "has an invalid entry"
msgstr ""

## From Ecto.Changeset.validate_exclusion/3
msgid "is reserved"
msgstr ""

## From Ecto.Changeset.validate_confirmation/3
msgid "does not match confirmation"
msgstr ""

## From Ecto.Changeset.no_assoc_constraint/3
msgid "is still associated with this entry"
msgstr ""

msgid "are still associated with this entry"
msgstr ""

## From Ecto.Changeset.validate_length/3
msgid "should be %{count} character(s)"
msgid_plural "should be %{count} character(s)"
msgstr[0] ""
msgstr[1] ""

msgid "should have %{count} item(s)"
msgid_plural "should have %{count} item(s)"
msgstr[0] ""
msgstr[1] ""

msgid "should be at least %{count} character(s)"
msgid_plural "should be at least %{count} character(s)"
msgstr[0] ""
msgstr[1] ""

msgid "should have at least %{count} item(s)"
msgid_plural "should have at least %{count} item(s)"
msgstr[0] ""
msgstr[1] ""

msgid "should be at most %{count} character(s)"
msgid_plural "should be at most %{count} character(s)"
msgstr[0] ""
msgstr[1] ""

msgid "should have at most %{count} item(s)"
msgid_plural "should have at most %{count} item(s)"
msgstr[0] ""
msgstr[1] ""

## From Ecto.Changeset.validate_number/3
msgid "must be less than %{number}"
msgstr ""

msgid "must be greater than %{number}"
msgstr ""

msgid "must be less than or equal to %{number}"
msgstr ""

msgid "must be greater than or equal to %{number}"
msgstr ""

msgid "must be equal to %{number}"
msgstr ""


code:
## This file is a PO Template file.
##
## `msgid`s here are often extracted from source code.
## Add new translations manually only if they're dynamic
## translations that can't be statically extracted.
##
## Run `mix gettext.extract` to bring this file up to
## date. Leave `msgstr`s empty as changing them here as no
## effect: edit them in PO (`.po`) files instead.

## From Ecto.Changeset.cast/4
msgid "can't be blank"
msgstr ""

## From Ecto.Changeset.unique_constraint/3
msgid "has already been taken"
msgstr ""

## From Ecto.Changeset.put_change/3
msgid "is invalid"
msgstr ""

## From Ecto.Changeset.validate_acceptance/3
msgid "must be accepted"
msgstr ""

## From Ecto.Changeset.validate_format/3
msgid "has invalid format"
msgstr ""

## From Ecto.Changeset.validate_subset/3
msgid "has an invalid entry"
msgstr ""

## From Ecto.Changeset.validate_exclusion/3
msgid "is reserved"
msgstr ""

## From Ecto.Changeset.validate_confirmation/3
msgid "does not match confirmation"
msgstr ""

## From Ecto.Changeset.no_assoc_constraint/3
msgid "is still associated with this entry"
msgstr ""

msgid "are still associated with this entry"
msgstr ""

## From Ecto.Changeset.validate_length/3
msgid "should be %{count} character(s)"
msgid_plural "should be %{count} character(s)"
msgstr[0] ""
msgstr[1] ""

msgid "should have %{count} item(s)"
msgid_plural "should have %{count} item(s)"
msgstr[0] ""
msgstr[1] ""

msgid "should be at least %{count} character(s)"
msgid_plural "should be at least %{count} character(s)"
msgstr[0] ""
msgstr[1] ""

msgid "should have at least %{count} item(s)"
msgid_plural "should have at least %{count} item(s)"
msgstr[0] ""
msgstr[1] ""

msgid "should be at most %{count} character(s)"
msgid_plural "should be at most %{count} character(s)"
msgstr[0] ""
msgstr[1] ""

msgid "should have at most %{count} item(s)"
msgid_plural "should have at most %{count} item(s)"
msgstr[0] ""
msgstr[1] ""

## From Ecto.Changeset.validate_number/3
msgid "must be less than %{number}"
msgstr ""

msgid "must be greater than %{number}"
msgstr ""

msgid "must be less than or equal to %{number}"
msgstr ""

msgid "must be greater than or equal to %{number}"
msgstr ""

msgid "must be equal to %{number}"
msgstr ""


code:
defmodule Qtfile.Repo.Migrations.CreateUsers do
  use Ecto.Migration

  def change do
    create table(:users) do
      add :name, :string
      add :username, :string
      add :password, :string
      add :status, :string, default: "active"
      add :role, :string, default: "user"

      timestamps()
    end

    create unique_index(:users, [:username])
  end
end


code:
defmodule Qtfile.Repo.Migrations.CreateTokens do
  use Ecto.Migration

  def change do
    create table(:tokens) do
      add :token, :string

      timestamps()
    end

    create unique_index(:tokens, [:token])
  end
end


code:
defmodule Qtfile.Repo.Migrations.CreateRooms do
  use Ecto.Migration

  def change do
    create table(:rooms) do
      add :room_id, :string
      add :room_name, :string
      add :owner, :string
      add :files, {:array, :map}
      add :motd, :string
      add :disabled, :boolean, default: false, null: false
      add :file_ttl, :integer

      timestamps()
    end

    create unique_index(:rooms, [:room_id])
  end
end


code:
defmodule Qtfile.Repo.Migrations.CreateFiles do
  use Ecto.Migration

  def change do
    create table(:files) do
      add :uuid, :string
      add :filename, :string
      add :mime_type, :string
      add :rooms_id, references(:rooms)
      add :hash, :string
      add :size, :integer
      add :users_id, references(:users)
      add :ip_address, :string
      add :expiration_date, :utc_datetime

      timestamps()
    end

    create unique_index(:files, [:uuid])
    create index(:files, [:expiration_date])
    create index(:files, [:users_id])
    create index(:files, [:rooms_id])
  end
end


code:
defmodule Qtfile.Repo.Migrations.AddSecretToUser do
  use Ecto.Migration

  def change do
    alter table(:users) do
      add :secret, :binary
    end

    create unique_index(:users, [:secret])
  end
end


code:
defmodule Qtfile.Repo.Migrations.MigrateUsers do
  use Ecto.Migration
  import Ecto.Query

  def change do
    query =
      from u in Qtfile.Accounts.User
    users = Qtfile.Repo.all(query)
    Enum.map(users, fn(user) ->
      secret = :crypto.strong_rand_bytes(16)
      changeset = Qtfile.Accounts.User.changeset(user, %{secret: secret})
      Qtfile.Repo.update!(changeset)
    end)
  end
end


code:
defmodule Qtfile.Repo.Migrations.AddSecretToRoom do
  use Ecto.Migration

  def change do
    alter table(:rooms) do
      add :secret, :binary
    end

    create unique_index(:rooms, [:secret])
  end
end


code:
defmodule Qtfile.Repo.Migrations.BinaryIp do
  use Ecto.Migration
  import Ecto.Query

  def change do
    Qtfile.Repo.delete_all(from f in Qtfile.Files.File)

    alter table(:files) do
      remove :ip_address
      add :ip_address, :binary, null: false
    end
  end
end


code:
defmodule Qtfile.Repo.Migrations.ChangeRoomAndUserFieldNames do
  use Ecto.Migration

  def change do
    rename table("files"), :rooms_id, to: :location
    rename table("files"), :users_id, to: :uploader
  end
end


code:
defmodule Qtfile.Repo.Migrations.ForeignOwner do
  use Ecto.Migration
  import Ecto.Query

  def change do
    alter table(:rooms) do
      remove :owner
      add :owner, references(:users)
    end
  end
end


code:
defmodule Qtfile.Repo.Migrations.Renames do
  use Ecto.Migration

  def change do
    rename table("files"), :location, to: :location_id
    rename table("files"), :uploader, to: :uploader_id
    rename table("rooms"), :owner, to: :owner_id
  end
end


code:
defmodule Qtfile.Repo.Migrations.CreateBans do
  use Ecto.Migration

  def change do
    create table(:bans) do
      add :banner_id, references(:users)
      add :room_id, references(:rooms)
      add :end, :utc_datetime
      add :reason, :string

      timestamps()
    end
    create index(:bans, [:end, :banner_id, :room_id])
    create index(:bans, [:end, :room_id])
    create index(:bans, [:banner_id, :room_id])
    create index(:bans, [:room_id])

    create table(:file_bans) do
      add :ban_id, references(:bans)
      add :hash, :string

      timestamps()
    end
    create unique_index(:file_bans, [:ban_id, :hash])
    create index(:file_bans, [:hash])

    create table(:user_bans) do
      add :ban_id, references(:bans)
      add :bannee_id, references(:users)
      add :hell, :boolean

      timestamps()
    end
    create unique_index(:user_bans, [:ban_id, :bannee_id])
    create index(:user_bans, [:bannee_id])

    create table(:ip_bans) do
      add :user_ban_id, references(:user_bans)
      add :ip_address, :binary

      timestamps()
    end
    create unique_index(:ip_bans, [:user_ban_id, :ip_address])
    create index(:ip_bans, [:ip_address])
  end
end


code:
defmodule Qtfile.Repo.Migrations.Indexes do
  use Ecto.Migration

  def change do
    create index(:rooms, [:owner_id])
    create index(:rooms, [:disabled])
    create index(:files, [:hash])
    create index(:files, [:ip_address])
    create index(:users, [:role])
  end
end


code:
defmodule Qtfile.Repo.Migrations.DeleteUselessStatusField do
  use Ecto.Migration

  def change do
    alter table(:users) do
      remove :status
    end
  end
end


code:
# Script for populating the database. You can run it as:
#
#     mix run priv/repo/seeds.exs
#
# Inside the script, you can read and write to any of your
# repositories directly:
#
#     Qtfile.Repo.insert!(%Qtfile.SomeSchema{})
#
# We recommend using the bang functions (`insert!`, `update!`
# and so on) as they will fail if something goes wrong.


code:
defmodule Qtfile.AccountsTest do
  use Qtfile.DataCase

  alias Qtfile.Accounts

  describe "users" do
    alias Qtfile.Accounts.User

    @valid_attrs %{name: "some name", password: "some password", username: "some username"}
    @update_attrs %{name: "some updated name", password: "some updated password", username: "some updated username"}
    @invalid_attrs %{name: nil, password: nil, username: nil}

    def user_fixture(attrs \\ %{}) do
      {:ok, user} =
        attrs
        |> Enum.into(@valid_attrs)
        |> Accounts.create_user()

      user
    end

    test "list_users/0 returns all users" do
      user = user_fixture()
      assert Accounts.list_users() == [user]
    end

    test "get_user!/1 returns the user with given id" do
      user = user_fixture()
      assert Accounts.get_user!(user.id) == user
    end

    test "create_user/1 with valid data creates a user" do
      assert {:ok, %User{} = user} = Accounts.create_user(@valid_attrs)
      assert user.name == "some name"
      assert user.password == "some password"
      assert user.username == "some username"
    end

    test "create_user/1 with invalid data returns error changeset" do
      assert {:error, %Ecto.Changeset{}} = Accounts.create_user(@invalid_attrs)
    end

    test "update_user/2 with valid data updates the user" do
      user = user_fixture()
      assert {:ok, user} = Accounts.update_user(user, @update_attrs)
      assert %User{} = user
      assert user.name == "some updated name"
      assert user.password == "some updated password"
      assert user.username == "some updated username"
    end

    test "update_user/2 with invalid data returns error changeset" do
      user = user_fixture()
      assert {:error, %Ecto.Changeset{}} = Accounts.update_user(user, @invalid_attrs)
      assert user == Accounts.get_user!(user.id)
    end

    test "delete_user/1 deletes the user" do
      user = user_fixture()
      assert {:ok, %User{}} = Accounts.delete_user(user)
      assert_raise Ecto.NoResultsError, fn -> Accounts.get_user!(user.id) end
    end

    test "change_user/1 returns a user changeset" do
      user = user_fixture()
      assert %Ecto.Changeset{} = Accounts.change_user(user)
    end
  end
end


code:
defmodule Qtfile.BansTest do
  use Qtfile.DataCase

  alias Qtfile.Bans

  describe "bans" do
    alias Qtfile.Bans.Ban

    @valid_attrs %{}
    @update_attrs %{}
    @invalid_attrs %{}

    def ban_fixture(attrs \\ %{}) do
      {:ok, ban} =
        attrs
        |> Enum.into(@valid_attrs)
        |> Bans.create_ban()

      ban
    end

    test "list_bans/0 returns all bans" do
      ban = ban_fixture()
      assert Bans.list_bans() == [ban]
    end

    test "get_ban!/1 returns the ban with given id" do
      ban = ban_fixture()
      assert Bans.get_ban!(ban.id) == ban
    end

    test "create_ban/1 with valid data creates a ban" do
      assert {:ok, %Ban{} = ban} = Bans.create_ban(@valid_attrs)
    end

    test "create_ban/1 with invalid data returns error changeset" do
      assert {:error, %Ecto.Changeset{}} = Bans.create_ban(@invalid_attrs)
    end

    test "update_ban/2 with valid data updates the ban" do
      ban = ban_fixture()
      assert {:ok, ban} = Bans.update_ban(ban, @update_attrs)
      assert %Ban{} = ban
    end

    test "update_ban/2 with invalid data returns error changeset" do
      ban = ban_fixture()
      assert {:error, %Ecto.Changeset{}} = Bans.update_ban(ban, @invalid_attrs)
      assert ban == Bans.get_ban!(ban.id)
    end

    test "delete_ban/1 deletes the ban" do
      ban = ban_fixture()
      assert {:ok, %Ban{}} = Bans.delete_ban(ban)
      assert_raise Ecto.NoResultsError, fn -> Bans.get_ban!(ban.id) end
    end

    test "change_ban/1 returns a ban changeset" do
      ban = ban_fixture()
      assert %Ecto.Changeset{} = Bans.change_ban(ban)
    end
  end
end


code:
defmodule Qtfile.FilesTest do
  use Qtfile.DataCase

  alias Qtfile.Files

  describe "files" do
    alias Qtfile.Files.File

    @valid_attrs %{filename: "some filename", uuid: "some uuid"}
    @update_attrs %{filename: "some updated filename", uuid: "some updated uuid"}
    @invalid_attrs %{filename: nil, uuid: nil}

    def file_fixture(attrs \\ %{}) do
      {:ok, file} =
        attrs
        |> Enum.into(@valid_attrs)
        |> Files.create_file()

      file
    end

    test "list_files/0 returns all files" do
      file = file_fixture()
      assert Files.list_files() == [file]
    end

    test "get_file!/1 returns the file with given id" do
      file = file_fixture()
      assert Files.get_file!(file.id) == file
    end

    test "create_file/1 with valid data creates a file" do
      assert {:ok, %File{} = file} = Files.create_file(@valid_attrs)
      assert file.filename == "some filename"
      assert file.uuid == "some uuid"
    end

    test "create_file/1 with invalid data returns error changeset" do
      assert {:error, %Ecto.Changeset{}} = Files.create_file(@invalid_attrs)
    end

    test "update_file/2 with valid data updates the file" do
      file = file_fixture()
      assert {:ok, file} = Files.update_file(file, @update_attrs)
      assert %File{} = file
      assert file.filename == "some updated filename"
      assert file.uuid == "some updated uuid"
    end

    test "update_file/2 with invalid data returns error changeset" do
      file = file_fixture()
      assert {:error, %Ecto.Changeset{}} = Files.update_file(file, @invalid_attrs)
      assert file == Files.get_file!(file.id)
    end

    test "delete_file/1 deletes the file" do
      file = file_fixture()
      assert {:ok, %File{}} = Files.delete_file(file)
      assert_raise Ecto.NoResultsError, fn -> Files.get_file!(file.id) end
    end

    test "change_file/1 returns a file changeset" do
      file = file_fixture()
      assert %Ecto.Changeset{} = Files.change_file(file)
    end
  end
end