lib/sftpd_file_handler.ex

defmodule Exsftpd.SftpFileHandler do
  defp user_path(path, state) do
    Path.join(state[:root_path], path)
  end

  defp on_event({event_name, meta}, state) do
    case state[:event_handler] do
      nil -> nil
      {module, fun} -> apply(module, fun, [{event_name, state[:user], meta}])
      handler -> handler.({event_name, state[:user], meta})
    end
  end

  defp after_event(param, state, result) do
    on_event(param, state)
    result
  end

  defp get_file_info(io_device) do
    case :file.pid2name(io_device) do
      {:ok, filename} -> {io_device, filename}
      _ -> {io_device}
    end
  end

  def close(io_device, state) do
    after_event({:close, get_file_info(io_device)}, state, {:file.close(io_device), state})
  end

  def delete(path, state) do
    after_event({:delete, path}, state, {:file.delete(user_path(path, state)), state})
  end

  def del_dir(path, state) do
    after_event({:del_dir, path}, state, {:file.del_dir(user_path(path, state)), state})
  end

  def get_cwd(state) do
    {:file.get_cwd(), state}
  end

  def is_dir(abs_path, state) do
    {:filelib.is_dir(user_path(abs_path, state)), state}
  end

  def list_dir(abs_path, state) do
    {:file.list_dir(user_path(abs_path, state)), state}
  end

  def make_dir(dir, state) do
    after_event({:make_dir, dir}, state, {:file.make_dir(user_path(dir, state)), state})
  end

  def make_symlink(path2, path, state) do
    after_event(
      {:make_symlink, {path2, path}},
      state,
      {:file.make_symlink(user_path(path2, state), user_path(path, state)), state}
    )
  end

  def open(path, flags, state) do
    {case :file.open(user_path(path, state), flags) do
       {:ok, pid} ->
         on_event({:open, {get_file_info(pid), path, flags}}, state)
         {:ok, pid}

       other ->
         other
     end, state}
  end

  def position(io_device, offs, state) do
    {:file.position(io_device, offs), state}
  end

  def read(io_device, len, state) do
    after_event({:read, get_file_info(io_device)}, state, {:file.read(io_device, len), state})
  end

  def read_link(path, state) do
    {:file.read_link(user_path(path, state)), state}
  end

  def read_link_info(path, state) do
    {:file.read_link_info(user_path(path, state)), state}
  end

  def read_file_info(path, state) do
    {:file.read_file_info(user_path(path, state)), state}
  end

  def rename(path, path2, state) do
    after_event(
      {:rename, {path, path2}},
      state,
      {:file.rename(user_path(path, state), user_path(path2, state)), state}
    )
  end

  def write(io_device, data, state) do
    after_event({:write, get_file_info(io_device)}, state, {:file.write(io_device, data), state})
  end

  def write_file_info(path, info, state) do
    after_event(
      {:write_file_info, {path, info}},
      state,
      {:file.write_file_info(user_path(path, state), info), state}
    )
  end
end