aboutsummaryrefslogtreecommitdiff
path: root/lib/ejabberd/config/config.ex
blob: a1b91858aaadddd745c3b71b1ca33fbc3e4d1c5c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
defmodule Ejabberd.Config do
  @moduledoc """
  Base module for configuration file.

  Imports macros for the config DSL and contains functions
  for working/starting the configuration parsed.
  """

  alias Ejabberd.Config.EjabberdModule
  alias Ejabberd.Config.Attr
  alias Ejabberd.Config.EjabberdLogger

  defmacro __using__(_opts) do
    quote do
      import Ejabberd.Config, only: :macros
      import Ejabberd.Logger

      @before_compile Ejabberd.Config
    end
  end

  # Validate the modules parsed and log validation errors at compile time.
  # Could be also possible to interrupt the compilation&execution by throwing
  # an exception if necessary.
  def __before_compile__(_env) do
    get_modules_parsed_in_order()
    |> EjabberdModule.validate
    |> EjabberdLogger.log_errors
  end

  @doc """
  Given the path of the config file, it evaluates it.
  """
  def init(file_path, force \\ false) do
    init_already_executed = Ejabberd.Config.Store.get(:module_name) != []

    case force do
      true ->
        Ejabberd.Config.Store.stop
        Ejabberd.Config.Store.start_link
        do_init(file_path)
      false ->
        if not init_already_executed, do: do_init(file_path)
    end
  end

  @doc """
  Returns a list with all the opts, formatted for ejabberd.
  """
  def get_ejabberd_opts do
    get_general_opts()
    |> Map.put(:modules, get_modules_parsed_in_order())
    |> Map.put(:listeners, get_listeners_parsed_in_order())
    |> Ejabberd.Config.OptsFormatter.format_opts_for_ejabberd
  end

  @doc """
  Register the hooks defined inside the elixir config file.
  """
  def start_hooks do
    get_hooks_parsed_in_order()
    |> Enum.each(&Ejabberd.Config.EjabberdHook.start/1)
  end

  ###
  ### MACROS
  ###

  defmacro listen(module, do: block) do
    attrs = Attr.extract_attrs_from_block_with_defaults(block)

    quote do
      Ejabberd.Config.Store.put(:listeners, %EjabberdModule{
        module: unquote(module),
        attrs: unquote(attrs)
      })
    end
  end

  defmacro module(module, do: block) do
    attrs = Attr.extract_attrs_from_block_with_defaults(block)

    quote do
      Ejabberd.Config.Store.put(:modules, %EjabberdModule{
        module: unquote(module),
        attrs: unquote(attrs)
      })
    end
  end

  defmacro hook(hook_name, opts, fun) do
    quote do
      Ejabberd.Config.Store.put(:hooks, %Ejabberd.Config.EjabberdHook{
        hook: unquote(hook_name),
        opts: unquote(opts),
        fun: unquote(fun)
      })
    end
  end

  # Private API

  defp do_init(file_path) do
    # File evaluation
    Code.eval_file(file_path) |> extract_and_store_module_name()

    # Getting start/0 config
    Ejabberd.Config.Store.get(:module_name)
    |> case do
      nil -> IO.puts "[ ERR ] Configuration module not found."
      [module] -> call_start_func_and_store_data(module)
    end

    # Fetching git modules and install them
    get_modules_parsed_in_order()
    |> EjabberdModule.fetch_git_repos
  end

  # Returns the modules from the store
  defp get_modules_parsed_in_order,
    do: Ejabberd.Config.Store.get(:modules) |> Enum.reverse

  # Returns the listeners from the store
  defp get_listeners_parsed_in_order,
    do: Ejabberd.Config.Store.get(:listeners) |> Enum.reverse

  defp get_hooks_parsed_in_order,
    do: Ejabberd.Config.Store.get(:hooks) |> Enum.reverse

  # Returns the general config options
  defp get_general_opts,
    do: Ejabberd.Config.Store.get(:general) |> List.first

  # Gets the general ejabberd options calling
  # the start/0 function and stores them.
  defp call_start_func_and_store_data(module) do
    opts = apply(module, :start, [])
    Ejabberd.Config.Store.put(:general, opts)
  end

  # Stores the configuration module name
  defp extract_and_store_module_name({{:module, mod, _bytes, _}, _}) do
    Ejabberd.Config.Store.put(:module_name, mod)
  end
end